summaryrefslogtreecommitdiff
path: root/setuptools/_distutils/filelist.py
diff options
context:
space:
mode:
Diffstat (limited to 'setuptools/_distutils/filelist.py')
-rw-r--r--setuptools/_distutils/filelist.py62
1 files changed, 45 insertions, 17 deletions
diff --git a/setuptools/_distutils/filelist.py b/setuptools/_distutils/filelist.py
index c92d5fdb..82a77384 100644
--- a/setuptools/_distutils/filelist.py
+++ b/setuptools/_distutils/filelist.py
@@ -4,13 +4,16 @@ Provides the FileList class, used for poking about the filesystem
and building lists of files.
"""
-import os, re
+import os
+import re
import fnmatch
import functools
+
from distutils.util import convert_path
from distutils.errors import DistutilsTemplateError, DistutilsInternalError
from distutils import log
+
class FileList:
"""A list of files built by on exploring the filesystem and filtered by
applying various patterns to what we find there.
@@ -46,7 +49,7 @@ class FileList:
if DEBUG:
print(msg)
- # -- List-like methods ---------------------------------------------
+ # Collection methods
def append(self, item):
self.files.append(item)
@@ -61,8 +64,7 @@ class FileList:
for sort_tuple in sortable_files:
self.files.append(os.path.join(*sort_tuple))
-
- # -- Other miscellaneous utility methods ---------------------------
+ # Other miscellaneous utility methods
def remove_duplicates(self):
# Assumes list has been sorted!
@@ -70,8 +72,7 @@ class FileList:
if self.files[i] == self.files[i - 1]:
del self.files[i]
-
- # -- "File template" methods ---------------------------------------
+ # "File template" methods
def _parse_template_line(self, line):
words = line.split()
@@ -146,9 +147,11 @@ class FileList:
(dir, ' '.join(patterns)))
for pattern in patterns:
if not self.include_pattern(pattern, prefix=dir):
- log.warn(("warning: no files found matching '%s' "
- "under directory '%s'"),
- pattern, dir)
+ msg = (
+ "warning: no files found matching '%s' "
+ "under directory '%s'"
+ )
+ log.warn(msg, pattern, dir)
elif action == 'recursive-exclude':
self.debug_print("recursive-exclude %s %s" %
@@ -174,8 +177,7 @@ class FileList:
raise DistutilsInternalError(
"this cannot happen: invalid action '%s'" % action)
-
- # -- Filtering/selection methods -----------------------------------
+ # Filtering/selection methods
def include_pattern(self, pattern, anchor=1, prefix=None, is_regex=0):
"""Select strings (presumably filenames) from 'self.files' that
@@ -219,9 +221,8 @@ class FileList:
files_found = True
return files_found
-
- def exclude_pattern (self, pattern,
- anchor=1, prefix=None, is_regex=0):
+ def exclude_pattern(
+ self, pattern, anchor=1, prefix=None, is_regex=0):
"""Remove strings (presumably filenames) from 'files' that match
'pattern'. Other parameters are the same as for
'include_pattern()', above.
@@ -240,21 +241,47 @@ class FileList:
return files_found
-# ----------------------------------------------------------------------
# Utility functions
def _find_all_simple(path):
"""
Find all files under 'path'
"""
+ all_unique = _UniqueDirs.filter(os.walk(path, followlinks=True))
results = (
os.path.join(base, file)
- for base, dirs, files in os.walk(path, followlinks=True)
+ for base, dirs, files in all_unique
for file in files
)
return filter(os.path.isfile, results)
+class _UniqueDirs(set):
+ """
+ Exclude previously-seen dirs from walk results,
+ avoiding infinite recursion.
+ Ref https://bugs.python.org/issue44497.
+ """
+ def __call__(self, walk_item):
+ """
+ Given an item from an os.walk result, determine
+ if the item represents a unique dir for this instance
+ and if not, prevent further traversal.
+ """
+ base, dirs, files = walk_item
+ stat = os.stat(base)
+ candidate = stat.st_dev, stat.st_ino
+ found = candidate in self
+ if found:
+ del dirs[:]
+ self.add(candidate)
+ return not found
+
+ @classmethod
+ def filter(cls, items):
+ return filter(cls(), items)
+
+
def findall(dir=os.curdir):
"""
Find all files under 'dir' and return the list of full filenames.
@@ -319,7 +346,8 @@ def translate_pattern(pattern, anchor=1, prefix=None, is_regex=0):
if os.sep == '\\':
sep = r'\\'
pattern_re = pattern_re[len(start): len(pattern_re) - len(end)]
- pattern_re = r'%s\A%s%s.*%s%s' % (start, prefix_re, sep, pattern_re, end)
+ pattern_re = r'%s\A%s%s.*%s%s' % (
+ start, prefix_re, sep, pattern_re, end)
else: # no prefix -- respect anchor flag
if anchor:
pattern_re = r'%s\A%s' % (start, pattern_re[len(start):])