namespaces.py 3.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107
  1. import os
  2. from distutils import log
  3. import itertools
  4. flatten = itertools.chain.from_iterable
  5. class Installer:
  6. nspkg_ext = '-nspkg.pth'
  7. def install_namespaces(self):
  8. nsp = self._get_all_ns_packages()
  9. if not nsp:
  10. return
  11. filename, ext = os.path.splitext(self._get_target())
  12. filename += self.nspkg_ext
  13. self.outputs.append(filename)
  14. log.info("Installing %s", filename)
  15. lines = map(self._gen_nspkg_line, nsp)
  16. if self.dry_run:
  17. # always generate the lines, even in dry run
  18. list(lines)
  19. return
  20. with open(filename, 'wt') as f:
  21. f.writelines(lines)
  22. def uninstall_namespaces(self):
  23. filename, ext = os.path.splitext(self._get_target())
  24. filename += self.nspkg_ext
  25. if not os.path.exists(filename):
  26. return
  27. log.info("Removing %s", filename)
  28. os.remove(filename)
  29. def _get_target(self):
  30. return self.target
  31. _nspkg_tmpl = (
  32. "import sys, types, os",
  33. "has_mfs = sys.version_info > (3, 5)",
  34. "p = os.path.join(%(root)s, *%(pth)r)",
  35. "importlib = has_mfs and __import__('importlib.util')",
  36. "has_mfs and __import__('importlib.machinery')",
  37. (
  38. "m = has_mfs and "
  39. "sys.modules.setdefault(%(pkg)r, "
  40. "importlib.util.module_from_spec("
  41. "importlib.machinery.PathFinder.find_spec(%(pkg)r, "
  42. "[os.path.dirname(p)])))"
  43. ),
  44. (
  45. "m = m or "
  46. "sys.modules.setdefault(%(pkg)r, types.ModuleType(%(pkg)r))"
  47. ),
  48. "mp = (m or []) and m.__dict__.setdefault('__path__',[])",
  49. "(p not in mp) and mp.append(p)",
  50. )
  51. "lines for the namespace installer"
  52. _nspkg_tmpl_multi = (
  53. 'm and setattr(sys.modules[%(parent)r], %(child)r, m)',
  54. )
  55. "additional line(s) when a parent package is indicated"
  56. def _get_root(self):
  57. return "sys._getframe(1).f_locals['sitedir']"
  58. def _gen_nspkg_line(self, pkg):
  59. pth = tuple(pkg.split('.'))
  60. root = self._get_root()
  61. tmpl_lines = self._nspkg_tmpl
  62. parent, sep, child = pkg.rpartition('.')
  63. if parent:
  64. tmpl_lines += self._nspkg_tmpl_multi
  65. return ';'.join(tmpl_lines) % locals() + '\n'
  66. def _get_all_ns_packages(self):
  67. """Return sorted list of all package namespaces"""
  68. pkgs = self.distribution.namespace_packages or []
  69. return sorted(flatten(map(self._pkg_names, pkgs)))
  70. @staticmethod
  71. def _pkg_names(pkg):
  72. """
  73. Given a namespace package, yield the components of that
  74. package.
  75. >>> names = Installer._pkg_names('a.b.c')
  76. >>> set(names) == set(['a', 'a.b', 'a.b.c'])
  77. True
  78. """
  79. parts = pkg.split('.')
  80. while parts:
  81. yield '.'.join(parts)
  82. parts.pop()
  83. class DevelopInstaller(Installer):
  84. def _get_root(self):
  85. return repr(str(self.egg_path))
  86. def _get_target(self):
  87. return self.egg_link