pth.py 5.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153
  1. #!/usr/bin/env python
  2. # Licensed to Cloudera, Inc. under one
  3. # or more contributor license agreements. See the NOTICE file
  4. # distributed with this work for additional information
  5. # regarding copyright ownership. Cloudera, Inc. licenses this file
  6. # to you under the Apache License, Version 2.0 (the
  7. # "License"); you may not use this file except in compliance
  8. # with the License. You may obtain a copy of the License at
  9. #
  10. # http://www.apache.org/licenses/LICENSE-2.0
  11. #
  12. # Unless required by applicable law or agreed to in writing, software
  13. # distributed under the License is distributed on an "AS IS" BASIS,
  14. # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  15. # See the License for the specific language governing permissions and
  16. # limitations under the License.
  17. """
  18. Tools to manipulate the .pth file in the virtualenv.
  19. """
  20. import glob
  21. import logging
  22. import os
  23. import sys
  24. import common
  25. py2or3 = "2"
  26. if sys.version_info[0] > 2:
  27. from builtins import object
  28. open_file = open
  29. py2or3 = "3"
  30. else:
  31. open_file = file
  32. LOG = logging.getLogger(__name__)
  33. PTH_FILE = 'hue.pth'
  34. def _get_pth_filename():
  35. """
  36. _get_pth_filename -> Path to the .pth file.
  37. Location can be defined via HUE_PTH_DIR environment variable.
  38. May raise SystemError if the virtual env is absent.
  39. """
  40. pth_dir = common.HUE_PTH_DIR
  41. if pth_dir:
  42. return os.path.join(pth_dir, PTH_FILE)
  43. else:
  44. return os.path.join(common._get_python_site_packages_dir(), PTH_FILE)
  45. class PthFile(object):
  46. def __init__(self):
  47. """May raise SystemError if the virtual env is absent"""
  48. self._path = _get_pth_filename()
  49. self._entries = []
  50. self._read()
  51. def _relpath(self, path):
  52. return os.path.relpath(path, os.path.dirname(self._path))
  53. def _read(self):
  54. if os.path.exists(self._path):
  55. self._entries = set(open_file(self._path).read().split('\n'))
  56. def add(self, app):
  57. """
  58. Add the app and its ext eggs into the pth file
  59. PTH files need paths relative to the pth file, not APPS_ROOT
  60. """
  61. if os.path.isabs(app.path):
  62. # Absolute path
  63. module_path = os.path.join(app.abs_path, 'src')
  64. self._entries.add(module_path)
  65. LOG.debug('Add to %s: %s' % (self._path, module_path))
  66. # Add gen-py path if found
  67. gen_py_path = os.path.join(app.abs_path, 'gen-py')
  68. if os.path.exists(gen_py_path):
  69. self._entries.add(gen_py_path)
  70. LOG.debug('Add to %s: %s' % (self._path, gen_py_path))
  71. # Eggs could be in ext-py/<pkg>/dist/*.egg
  72. for py in app.find_ext_pys():
  73. ext_eggs = glob.glob(os.path.join(py, 'dist', '*.egg'))
  74. self._entries.update(ext_eggs)
  75. LOG.debug('Add to %s: %s' % (self._path, ext_eggs))
  76. # And eggs could also be in ext-eggs/*.egg
  77. egg_files = glob.glob(os.path.join(app.path, 'ext-eggs', '*.egg'))
  78. self._entries.update(egg_files)
  79. LOG.debug('Add to %s: %s' % (self._path, egg_files))
  80. else:
  81. # Relative paths require some transformation.
  82. # Paths are relative to directory of pth file
  83. module_path = self._relpath(os.path.join(app.abs_path, 'src'))
  84. self._entries.add(module_path)
  85. LOG.debug('Add to %s: %s' % (self._path, module_path))
  86. # Add gen-py path if found
  87. gen_py_path = self._relpath(os.path.join(app.abs_path, 'gen-py'))
  88. if os.path.exists(os.path.join(app.abs_path, 'gen-py')):
  89. self._entries.add(gen_py_path)
  90. LOG.debug('Add to %s: %s' % (self._path, gen_py_path))
  91. # Eggs could be in ext-py/<pkg>/dist/*.egg
  92. for py in app.find_ext_pys():
  93. ext_eggs = [self._relpath(egg) for egg in glob.glob(os.path.join(py, 'dist', '*.egg'))]
  94. self._entries.update(ext_eggs)
  95. LOG.debug('Add to %s: %s' % (self._path, ext_eggs))
  96. # And eggs could also be in ext-eggs/*.egg
  97. egg_files = [self._relpath(egg_file) for egg_file in glob.glob(os.path.join(app.path, 'ext-eggs', '*.egg'))]
  98. self._entries.update(egg_files)
  99. LOG.debug('Add to %s: %s' % (self._path, egg_files))
  100. def remove(self, app):
  101. """
  102. Remove the app and its ext eggs from the pth file
  103. """
  104. for path in self._entries.copy():
  105. module_path = os.path.join(app.abs_path, 'src')
  106. if path.startswith(module_path):
  107. self._entries.remove(module_path)
  108. rel_module_path = self._relpath(module_path)
  109. if path.startswith(rel_module_path):
  110. self._entries.remove(rel_module_path)
  111. def save(self):
  112. """
  113. Save the pth file
  114. Create a symlink to the path if it does not already exist.
  115. """
  116. with open(self._path, 'w') as _file:
  117. # We want the Hue libraries to come before system libraries in
  118. # case there is a name collision.
  119. if py2or3 == "2":
  120. _file.write("import sys; sys.__plen = len(sys.path)\n")
  121. _file.write('\n'.join(sorted(self._entries)))
  122. if py2or3 == "2":
  123. _file.write("\nimport sys; new=sys.path[sys.__plen:]; del sys.path[sys.__plen:]; sys.path[0:0]=new\n")
  124. LOG.info('=== Saved %s' % self._path)
  125. def sync(self, apps):
  126. """Sync the .pth file with the installed apps"""
  127. self._entries = set()
  128. for app in apps:
  129. self.add(app)