summaryrefslogtreecommitdiff
path: root/setuptools/command/build_py.py
diff options
context:
space:
mode:
Diffstat (limited to 'setuptools/command/build_py.py')
-rw-r--r--setuptools/command/build_py.py93
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):