pth.py 4.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142
  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 common
  24. LOG = logging.getLogger(__name__)
  25. PTH_FILE = 'hue.pth'
  26. def _get_pth_filename():
  27. """
  28. _get_pth_filename -> Path to the .pth file.
  29. Location can be defined via HUE_PTH_DIR environment variable.
  30. May raise SystemError if the virtual env is absent.
  31. """
  32. pth_dir = common.HUE_PTH_DIR
  33. if pth_dir:
  34. return os.path.join(pth_dir, PTH_FILE)
  35. else:
  36. return os.path.join(common._get_python_site_packages_dir(), PTH_FILE)
  37. class PthFile(object):
  38. def __init__(self):
  39. """May raise SystemError if the virtual env is absent"""
  40. self._path = _get_pth_filename()
  41. self._entries = [ ]
  42. self._read()
  43. def _relpath(self, path):
  44. return os.path.relpath(path, os.path.dirname(self._path))
  45. def _read(self):
  46. if os.path.exists(self._path):
  47. self._entries = set(file(self._path).read().split('\n'))
  48. def add(self, app):
  49. """
  50. Add the app and its ext eggs into the pth file
  51. PTH files need paths relative to the pth file, not APPS_ROOT
  52. """
  53. if os.path.isabs(app.path):
  54. # Absolute path
  55. module_path = os.path.join(app.abs_path, 'src')
  56. self._entries.add(module_path)
  57. LOG.debug('Add to %s: %s' % (self._path, module_path))
  58. # Add gen-py path if found
  59. gen_py_path = os.path.join(app.abs_path, 'gen-py')
  60. if os.path.exists(gen_py_path):
  61. self._entries.add(gen_py_path)
  62. LOG.debug('Add to %s: %s' % (self._path, gen_py_path))
  63. # Eggs could be in ext-py/<pkg>/dist/*.egg
  64. for py in app.find_ext_pys():
  65. ext_eggs = glob.glob(os.path.join(py, 'dist', '*.egg'))
  66. self._entries.update(ext_eggs)
  67. LOG.debug('Add to %s: %s' % (self._path, ext_eggs))
  68. # And eggs could also be in ext-eggs/*.egg
  69. egg_files = glob.glob(os.path.join(app.path, 'ext-eggs', '*.egg'))
  70. self._entries.update(egg_files)
  71. LOG.debug('Add to %s: %s' % (self._path, egg_files))
  72. else:
  73. # Relative paths require some transformation.
  74. # Paths are relative to directory of pth file
  75. module_path = self._relpath(os.path.join(app.abs_path, 'src'))
  76. self._entries.add(module_path)
  77. LOG.debug('Add to %s: %s' % (self._path, module_path))
  78. # Add gen-py path if found
  79. gen_py_path = self._relpath(os.path.join(app.abs_path, 'gen-py'))
  80. if os.path.exists(os.path.join(app.abs_path, 'gen-py')):
  81. self._entries.add(gen_py_path)
  82. LOG.debug('Add to %s: %s' % (self._path, gen_py_path))
  83. # Eggs could be in ext-py/<pkg>/dist/*.egg
  84. for py in app.find_ext_pys():
  85. ext_eggs = [self._relpath(egg) for egg in glob.glob(os.path.join(py, 'dist', '*.egg'))]
  86. self._entries.update(ext_eggs)
  87. LOG.debug('Add to %s: %s' % (self._path, ext_eggs))
  88. # And eggs could also be in ext-eggs/*.egg
  89. egg_files = [self._relpath(egg_file) for egg_file in glob.glob(os.path.join(app.path, 'ext-eggs', '*.egg'))]
  90. self._entries.update(egg_files)
  91. LOG.debug('Add to %s: %s' % (self._path, egg_files))
  92. def remove(self, app):
  93. """
  94. Remove the app and its ext eggs from the pth file
  95. """
  96. for path in self._entries.copy():
  97. module_path = os.path.join(app.abs_path, 'src')
  98. if path.startswith(module_path):
  99. self._entries.remove(module_path)
  100. rel_module_path = self._relpath(module_path)
  101. if path.startswith(rel_module_path):
  102. self._entries.remove(rel_module_path)
  103. def save(self):
  104. """
  105. Save the pth file
  106. Create a symlink to the path if it does not already exist.
  107. """
  108. with open(self._path, 'w') as _file:
  109. # We want the Hue libraries to come before system libraries in
  110. # case there is a name collision.
  111. _file.write("import sys; sys.__plen = len(sys.path)\n")
  112. _file.write('\n'.join(sorted(self._entries)))
  113. _file.write("\nimport sys; new=sys.path[sys.__plen:]; del sys.path[sys.__plen:]; sys.path[0:0]=new\n")
  114. LOG.info('=== Saved %s' % self._path)
  115. def sync(self, apps):
  116. """Sync the .pth file with the installed apps"""
  117. self._entries = set()
  118. for app in apps:
  119. self.add(app)