install_lib.py 4.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147
  1. import os
  2. import sys
  3. from itertools import product, starmap
  4. import distutils.command.install_lib as orig
  5. class install_lib(orig.install_lib):
  6. """Don't add compiled flags to filenames of non-Python files"""
  7. def initialize_options(self):
  8. orig.install_lib.initialize_options(self)
  9. self.multiarch = None
  10. self.install_layout = None
  11. def finalize_options(self):
  12. orig.install_lib.finalize_options(self)
  13. self.set_undefined_options('install',('install_layout','install_layout'))
  14. if self.install_layout == 'deb' and sys.version_info[:2] >= (3, 3):
  15. import sysconfig
  16. self.multiarch = sysconfig.get_config_var('MULTIARCH')
  17. def run(self):
  18. self.build()
  19. outfiles = self.install()
  20. if outfiles is not None:
  21. # always compile, in case we have any extension stubs to deal with
  22. self.byte_compile(outfiles)
  23. def get_exclusions(self):
  24. """
  25. Return a collections.Sized collections.Container of paths to be
  26. excluded for single_version_externally_managed installations.
  27. """
  28. all_packages = (
  29. pkg
  30. for ns_pkg in self._get_SVEM_NSPs()
  31. for pkg in self._all_packages(ns_pkg)
  32. )
  33. excl_specs = product(all_packages, self._gen_exclusion_paths())
  34. return set(starmap(self._exclude_pkg_path, excl_specs))
  35. def _exclude_pkg_path(self, pkg, exclusion_path):
  36. """
  37. Given a package name and exclusion path within that package,
  38. compute the full exclusion path.
  39. """
  40. parts = pkg.split('.') + [exclusion_path]
  41. return os.path.join(self.install_dir, *parts)
  42. @staticmethod
  43. def _all_packages(pkg_name):
  44. """
  45. >>> list(install_lib._all_packages('foo.bar.baz'))
  46. ['foo.bar.baz', 'foo.bar', 'foo']
  47. """
  48. while pkg_name:
  49. yield pkg_name
  50. pkg_name, sep, child = pkg_name.rpartition('.')
  51. def _get_SVEM_NSPs(self):
  52. """
  53. Get namespace packages (list) but only for
  54. single_version_externally_managed installations and empty otherwise.
  55. """
  56. # TODO: is it necessary to short-circuit here? i.e. what's the cost
  57. # if get_finalized_command is called even when namespace_packages is
  58. # False?
  59. if not self.distribution.namespace_packages:
  60. return []
  61. install_cmd = self.get_finalized_command('install')
  62. svem = install_cmd.single_version_externally_managed
  63. return self.distribution.namespace_packages if svem else []
  64. @staticmethod
  65. def _gen_exclusion_paths():
  66. """
  67. Generate file paths to be excluded for namespace packages (bytecode
  68. cache files).
  69. """
  70. # always exclude the package module itself
  71. yield '__init__.py'
  72. yield '__init__.pyc'
  73. yield '__init__.pyo'
  74. if not hasattr(sys, 'implementation'):
  75. return
  76. base = os.path.join('__pycache__', '__init__.' + sys.implementation.cache_tag)
  77. yield base + '.pyc'
  78. yield base + '.pyo'
  79. yield base + '.opt-1.pyc'
  80. yield base + '.opt-2.pyc'
  81. def copy_tree(
  82. self, infile, outfile,
  83. preserve_mode=1, preserve_times=1, preserve_symlinks=0, level=1
  84. ):
  85. assert preserve_mode and preserve_times and not preserve_symlinks
  86. exclude = self.get_exclusions()
  87. if not exclude:
  88. import distutils.dir_util
  89. distutils.dir_util._multiarch = self.multiarch
  90. return orig.install_lib.copy_tree(self, infile, outfile)
  91. # Exclude namespace package __init__.py* files from the output
  92. from setuptools.archive_util import unpack_directory
  93. from distutils import log
  94. outfiles = []
  95. if self.multiarch:
  96. import sysconfig
  97. ext_suffix = sysconfig.get_config_var ('EXT_SUFFIX')
  98. if ext_suffix.endswith(self.multiarch + ext_suffix[-3:]):
  99. new_suffix = None
  100. else:
  101. new_suffix = "%s-%s%s" % (ext_suffix[:-3], self.multiarch, ext_suffix[-3:])
  102. def pf(src, dst):
  103. if dst in exclude:
  104. log.warn("Skipping installation of %s (namespace package)",
  105. dst)
  106. return False
  107. if self.multiarch and new_suffix and dst.endswith(ext_suffix) and not dst.endswith(new_suffix):
  108. dst = dst.replace(ext_suffix, new_suffix)
  109. log.info("renaming extension to %s", os.path.basename(dst))
  110. log.info("copying %s -> %s", src, os.path.dirname(dst))
  111. outfiles.append(dst)
  112. return dst
  113. unpack_directory(infile, outfile, pf)
  114. return outfiles
  115. def get_outputs(self):
  116. outputs = orig.install_lib.get_outputs(self)
  117. exclude = self.get_exclusions()
  118. if exclude:
  119. return [f for f in outputs if f not in exclude]
  120. return outputs