pth.py 5.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154
  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_SYMLINK = 'hue.link.pth'
  26. PTH_FILE = 'hue.pth'
  27. def _get_pth_symlink():
  28. """
  29. _get_pth_symlink -> Path to the .pth symlink.
  30. May raise SystemError if the virtual env is absent.
  31. """
  32. return os.path.join(common._get_python_site_packages_dir(), PTH_SYMLINK)
  33. def _get_pth_filename():
  34. """
  35. _get_pth_filename -> Path to the .pth file.
  36. Location can be defined via HUE_PTH_DIR environment variable.
  37. May raise SystemError if the virtual env is absent.
  38. """
  39. pth_dir = common.HUE_PTH_DIR
  40. if pth_dir:
  41. return os.path.join(pth_dir, PTH_FILE)
  42. else:
  43. return os.path.join(common._get_python_site_packages_dir(), PTH_FILE)
  44. class PthFile(object):
  45. def __init__(self):
  46. """May raise SystemError if the virtual env is absent"""
  47. self._symlink_path = _get_pth_symlink()
  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(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. # Eggs could be in ext-py/<pkg>/dist/*.egg
  67. for py in app.find_ext_pys():
  68. ext_eggs = glob.glob(os.path.join(py, 'dist', '*.egg'))
  69. self._entries.update(ext_eggs)
  70. LOG.debug('Add to %s: %s' % (self._path, ext_eggs))
  71. # And eggs could also be in ext-eggs/*.egg
  72. egg_files = glob.glob(os.path.join(app.path, 'ext-eggs', '*.egg'))
  73. self._entries.update(egg_files)
  74. LOG.debug('Add to %s: %s' % (self._path, egg_files))
  75. else:
  76. # Relative paths require some transformation.
  77. # Paths are relative to directory of pth file
  78. module_path = self._relpath(os.path.join(app.abs_path, 'src'))
  79. self._entries.add(module_path)
  80. LOG.debug('Add to %s: %s' % (self._path, module_path))
  81. # Eggs could be in ext-py/<pkg>/dist/*.egg
  82. for py in app.find_ext_pys():
  83. ext_eggs = [self._relpath(egg) for egg in glob.glob(os.path.join(py, 'dist', '*.egg'))]
  84. self._entries.update(ext_eggs)
  85. LOG.debug('Add to %s: %s' % (self._path, ext_eggs))
  86. # And eggs could also be in ext-eggs/*.egg
  87. egg_files = [self._relpath(egg_file) for egg_file in glob.glob(os.path.join(app.path, 'ext-eggs', '*.egg'))]
  88. self._entries.update(egg_files)
  89. LOG.debug('Add to %s: %s' % (self._path, egg_files))
  90. def remove(self, app):
  91. """
  92. Remove the app and its ext eggs from the pth file
  93. """
  94. for path in self._entries.copy():
  95. module_path = os.path.join(app.abs_path, 'src')
  96. if path.startswith(module_path):
  97. self._entries.remove(module_path)
  98. rel_module_path = self._relpath(module_path)
  99. if path.startswith(rel_module_path):
  100. self._entries.remove(rel_module_path)
  101. def save(self):
  102. """
  103. Save the pth file
  104. Create a symlink to the path if it does not already exist.
  105. """
  106. tmp_path = self._path + '.new'
  107. file(tmp_path, 'w').write('\n'.join(sorted(self._entries)))
  108. os.rename(tmp_path, self._path)
  109. LOG.info('=== Saved %s' % self._path)
  110. # relpath defined in common.py for python 2.4 and 2.5
  111. rel_symlink_path = os.path.relpath(self._path, os.path.dirname(self._symlink_path))
  112. # overwrite symlink if the path it points to is different from desired PTH.
  113. if os.path.islink(self._symlink_path) and os.readlink(self._symlink_path) != rel_symlink_path:
  114. LOG.info('=== Removing symbolic link at %s' % (self._symlink_path))
  115. os.unlink(self._symlink_path)
  116. LOG.info('=== Removed symbolic link at %s' % (self._symlink_path))
  117. if not os.path.islink(self._symlink_path):
  118. if os.path.exists(self._symlink_path):
  119. raise RuntimeError("Path %s already exists and is not a symbolic link." % self._symlink_path)
  120. LOG.info('=== Creating symbolic link at %s to %s' % (self._symlink_path, rel_symlink_path))
  121. os.symlink(rel_symlink_path, self._symlink_path)
  122. LOG.info('=== Created symbolic link at %s to %s' % (self._symlink_path, rel_symlink_path))
  123. def sync(self, apps):
  124. """Sync the .pth file with the installed apps"""
  125. self._entries = set()
  126. for app in apps:
  127. self.add(app)