codekingpro/portable-devtools
114k
1import os2import sys3from itertools import product, starmap4import distutils.command.install_lib as orig5 6 7class install_lib(orig.install_lib):8 """Don't add compiled flags to filenames of non-Python files"""9 10 def run(self):11 self.build()12 outfiles = self.install()13 if outfiles is not None:14 # always compile, in case we have any extension stubs to deal with15 self.byte_compile(outfiles)16 17 def get_exclusions(self):18 """19 Return a collections.Sized collections.Container of paths to be20 excluded for single_version_externally_managed installations.21 """22 all_packages = (23 pkg24 for ns_pkg in self._get_SVEM_NSPs()25 for pkg in self._all_packages(ns_pkg)26 )27 28 excl_specs = product(all_packages, self._gen_exclusion_paths())29 return set(starmap(self._exclude_pkg_path, excl_specs))30 31 def _exclude_pkg_path(self, pkg, exclusion_path):32 """33 Given a package name and exclusion path within that package,34 compute the full exclusion path.35 """36 parts = pkg.split('.') + [exclusion_path]37 return os.path.join(self.install_dir, *parts)38 39 @staticmethod40 def _all_packages(pkg_name):41 """42 >>> list(install_lib._all_packages('foo.bar.baz'))43 ['foo.bar.baz', 'foo.bar', 'foo']44 """45 while pkg_name:46 yield pkg_name47 pkg_name, sep, child = pkg_name.rpartition('.')48 49 def _get_SVEM_NSPs(self):50 """51 Get namespace packages (list) but only for52 single_version_externally_managed installations and empty otherwise.53 """54 # TODO: is it necessary to short-circuit here? i.e. what's the cost55 # if get_finalized_command is called even when namespace_packages is56 # False?57 if not self.distribution.namespace_packages:58 return []59 60 install_cmd = self.get_finalized_command('install')61 svem = install_cmd.single_version_externally_managed62 63 return self.distribution.namespace_packages if svem else []64 65 @staticmethod66 def _gen_exclusion_paths():67 """68 Generate file paths to be excluded for namespace packages (bytecode69 cache files).70 """71 # always exclude the package module itself72 yield '__init__.py'73 74 yield '__init__.pyc'75 yield '__init__.pyo'76 77 if not hasattr(sys, 'implementation'):78 return79 80 base = os.path.join('__pycache__', '__init__.' + sys.implementation.cache_tag)81 yield base + '.pyc'82 yield base + '.pyo'83 yield base + '.opt-1.pyc'84 yield base + '.opt-2.pyc'85 86 def copy_tree(87 self,88 infile,89 outfile,90 preserve_mode=1,91 preserve_times=1,92 preserve_symlinks=0,93 level=1,94 ):95 assert preserve_mode and preserve_times and not preserve_symlinks96 exclude = self.get_exclusions()97 98 if not exclude:99 return orig.install_lib.copy_tree(self, infile, outfile)100 101 # Exclude namespace package __init__.py* files from the output102 103 from setuptools.archive_util import unpack_directory104 from distutils import log105 106 outfiles = []107 108 def pf(src, dst):109 if dst in exclude:110 log.warn("Skipping installation of %s (namespace package)", dst)111 return False112 113 log.info("copying %s -> %s", src, os.path.dirname(dst))114 outfiles.append(dst)115 return dst116 117 unpack_directory(infile, outfile, pf)118 return outfiles119 120 def get_outputs(self):121 outputs = orig.install_lib.get_outputs(self)122 exclude = self.get_exclusions()123 if exclude:124 return [f for f in outputs if f not in exclude]125 return outputs126 