1import os 2import imp 3from itertools import product, starmap 4import distutils.command.install_lib as orig 5 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 with 15 self.byte_compile(outfiles) 16 17 def get_exclusions(self): 18 """ 19 Return a collections.Sized collections.Container of paths to be 20 excluded for single_version_externally_managed installations. 21 """ 22 all_packages = ( 23 pkg 24 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 @staticmethod 40 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_name 47 pkg_name, sep, child = pkg_name.rpartition('.') 48 49 def _get_SVEM_NSPs(self): 50 """ 51 Get namespace packages (list) but only for 52 single_version_externally_managed installations and empty otherwise. 53 """ 54 # TODO: is it necessary to short-circuit here? i.e. what's the cost 55 # if get_finalized_command is called even when namespace_packages is 56 # 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_managed 62 63 return self.distribution.namespace_packages if svem else [] 64 65 @staticmethod 66 def _gen_exclusion_paths(): 67 """ 68 Generate file paths to be excluded for namespace packages (bytecode 69 cache files). 70 """ 71 # always exclude the package module itself 72 yield '__init__.py' 73 74 yield '__init__.pyc' 75 yield '__init__.pyo' 76 77 if not hasattr(imp, 'get_tag'): 78 return 79 80 base = os.path.join('__pycache__', '__init__.' + imp.get_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, infile, outfile, 88 preserve_mode=1, preserve_times=1, preserve_symlinks=0, level=1 89 ): 90 assert preserve_mode and preserve_times and not preserve_symlinks 91 exclude = self.get_exclusions() 92 93 if not exclude: 94 return orig.install_lib.copy_tree(self, infile, outfile) 95 96 # Exclude namespace package __init__.py* files from the output 97 98 from setuptools.archive_util import unpack_directory 99 from distutils import log 100 101 outfiles = [] 102 103 def pf(src, dst): 104 if dst in exclude: 105 log.warn("Skipping installation of %s (namespace package)", 106 dst) 107 return False 108 109 log.info("copying %s -> %s", src, os.path.dirname(dst)) 110 outfiles.append(dst) 111 return dst 112 113 unpack_directory(infile, outfile, pf) 114 return outfiles 115 116 def get_outputs(self): 117 outputs = orig.install_lib.get_outputs(self) 118 exclude = self.get_exclusions() 119 if exclude: 120 return [f for f in outputs if f not in exclude] 121 return outputs 122