diff options
Diffstat (limited to 'setuptools/command/build_py.py')
| -rw-r--r-- | setuptools/command/build_py.py | 93 |
1 files changed, 70 insertions, 23 deletions
diff --git a/setuptools/command/build_py.py b/setuptools/command/build_py.py index 8623c777..0bad8295 100644 --- a/setuptools/command/build_py.py +++ b/setuptools/command/build_py.py @@ -6,10 +6,10 @@ import fnmatch import textwrap import io import distutils.errors -import collections import itertools -from setuptools.extern.six.moves import map +from setuptools.extern import six +from setuptools.extern.six.moves import map, filter, filterfalse try: from setuptools.lib2to3_ex import Mixin2to3 @@ -67,6 +67,9 @@ class build_py(orig.build_py, Mixin2to3): return orig.build_py.__getattr__(self, attr) def build_module(self, module, module_file, package): + if six.PY2 and isinstance(package, six.string_types): + # avoid errors on Python 2 when unicode is passed (#190) + package = package.split('.') outfile, copied = orig.build_py.build_module(self, module, module_file, package) if copied: @@ -94,12 +97,19 @@ class build_py(orig.build_py, Mixin2to3): def find_data_files(self, package, src_dir): """Return filenames for package's data files in 'src_dir'""" - globs = (self.package_data.get('', []) - + self.package_data.get(package, [])) - files = self.manifest_files.get(package, [])[:] - for pattern in globs: - # Each pattern has to be converted to a platform-specific path - files.extend(glob(os.path.join(src_dir, convert_path(pattern)))) + patterns = self._get_platform_patterns( + self.package_data, + package, + src_dir, + ) + globs_expanded = map(glob, patterns) + # flatten the expanded globs into an iterable of matches + globs_matches = itertools.chain.from_iterable(globs_expanded) + glob_files = filter(os.path.isfile, globs_matches) + files = itertools.chain( + self.manifest_files.get(package, []), + glob_files, + ) return self.exclude_data_files(package, src_dir, files) def build_package_data(self): @@ -184,26 +194,63 @@ class build_py(orig.build_py, Mixin2to3): def exclude_data_files(self, package, src_dir, files): """Filter filenames for package's data files in 'src_dir'""" - globs = ( - self.exclude_package_data.get('', []) - + self.exclude_package_data.get(package, []) + files = list(files) + patterns = self._get_platform_patterns( + self.exclude_package_data, + package, + src_dir, ) - bad = set( - item - for pattern in globs - for item in fnmatch.filter( - files, - os.path.join(src_dir, convert_path(pattern)), - ) + match_groups = ( + fnmatch.filter(files, pattern) + for pattern in patterns ) - seen = collections.defaultdict(itertools.count) - return [ + # flatten the groups of matches into an iterable of matches + matches = itertools.chain.from_iterable(match_groups) + bad = set(matches) + keepers = ( fn for fn in files if fn not in bad - # ditch dupes - and not next(seen[fn]) - ] + ) + # ditch dupes + return list(_unique_everseen(keepers)) + + @staticmethod + def _get_platform_patterns(spec, package, src_dir): + """ + yield platfrom-specific path patterns (suitable for glob + or fn_match) from a glob-based spec (such as + self.package_data or self.exclude_package_data) + matching package in src_dir. + """ + raw_patterns = itertools.chain( + spec.get('', []), + spec.get(package, []), + ) + return ( + # Each pattern has to be converted to a platform-specific path + os.path.join(src_dir, convert_path(pattern)) + for pattern in raw_patterns + ) + + +# from Python docs +def _unique_everseen(iterable, key=None): + "List unique elements, preserving order. Remember all elements ever seen." + # unique_everseen('AAAABBBCCDAABBB') --> A B C D + # unique_everseen('ABBCcAD', str.lower) --> A B C D + seen = set() + seen_add = seen.add + if key is None: + for element in filterfalse(seen.__contains__, iterable): + seen_add(element) + yield element + else: + for element in iterable: + k = key(element) + if k not in seen: + seen_add(k) + yield element def assert_relative(path): |
