diff options
author | Serhiy Storchaka <storchaka@gmail.com> | 2014-09-11 09:17:37 (GMT) |
---|---|---|
committer | Serhiy Storchaka <storchaka@gmail.com> | 2014-09-11 09:17:37 (GMT) |
commit | c2edcdd194356b26873e5304216c313e847b4159 (patch) | |
tree | 4f49e9e163484968b129ae625f5d1dcc9cee43f5 /Lib/glob.py | |
parent | d5fd6188e286a52334e24ef1ce4467618c2c1271 (diff) | |
download | cpython-c2edcdd194356b26873e5304216c313e847b4159.zip cpython-c2edcdd194356b26873e5304216c313e847b4159.tar.gz cpython-c2edcdd194356b26873e5304216c313e847b4159.tar.bz2 |
Issue #13968: The glob module now supports recursive search in
subdirectories using the "**" pattern.
Diffstat (limited to 'Lib/glob.py')
-rw-r--r-- | Lib/glob.py | 56 |
1 files changed, 50 insertions, 6 deletions
diff --git a/Lib/glob.py b/Lib/glob.py index d6eca24..56d6704 100644 --- a/Lib/glob.py +++ b/Lib/glob.py @@ -6,7 +6,7 @@ import fnmatch __all__ = ["glob", "iglob"] -def glob(pathname): +def glob(pathname, *, recursive=False): """Return a list of paths matching a pathname pattern. The pattern may contain simple shell-style wildcards a la @@ -14,10 +14,12 @@ def glob(pathname): dot are special cases that are not matched by '*' and '?' patterns. + If recursive is true, the pattern '**' will match any files and + zero or more directories and subdirectories. """ - return list(iglob(pathname)) + return list(iglob(pathname, recursive=recursive)) -def iglob(pathname): +def iglob(pathname, *, recursive=False): """Return an iterator which yields the paths matching a pathname pattern. The pattern may contain simple shell-style wildcards a la @@ -25,6 +27,8 @@ def iglob(pathname): dot are special cases that are not matched by '*' and '?' patterns. + If recursive is true, the pattern '**' will match any files and + zero or more directories and subdirectories. """ dirname, basename = os.path.split(pathname) if not has_magic(pathname): @@ -37,17 +41,23 @@ def iglob(pathname): yield pathname return if not dirname: - yield from glob1(None, basename) + if recursive and _isrecursive(basename): + yield from glob2(dirname, basename) + else: + yield from glob1(dirname, basename) return # `os.path.split()` returns the argument itself as a dirname if it is a # drive or UNC path. Prevent an infinite recursion if a drive or UNC path # contains magic characters (i.e. r'\\?\C:'). if dirname != pathname and has_magic(dirname): - dirs = iglob(dirname) + dirs = iglob(dirname, recursive=recursive) else: dirs = [dirname] if has_magic(basename): - glob_in_dir = glob1 + if recursive and _isrecursive(basename): + glob_in_dir = glob2 + else: + glob_in_dir = glob1 else: glob_in_dir = glob0 for dirname in dirs: @@ -83,6 +93,34 @@ def glob0(dirname, basename): return [basename] return [] +# This helper function recursively yields relative pathnames inside a literal +# directory. + +def glob2(dirname, pattern): + assert _isrecursive(pattern) + if dirname: + yield pattern[:0] + yield from _rlistdir(dirname) + +# Recursively yields relative pathnames inside a literal directory. + +def _rlistdir(dirname): + if not dirname: + if isinstance(dirname, bytes): + dirname = bytes(os.curdir, 'ASCII') + else: + dirname = os.curdir + try: + names = os.listdir(dirname) + except os.error: + return + for x in names: + if not _ishidden(x): + yield x + path = os.path.join(dirname, x) if dirname else x + for y in _rlistdir(path): + yield os.path.join(x, y) + magic_check = re.compile('([*?[])') magic_check_bytes = re.compile(b'([*?[])') @@ -97,6 +135,12 @@ def has_magic(s): def _ishidden(path): return path[0] in ('.', b'.'[0]) +def _isrecursive(pattern): + if isinstance(pattern, bytes): + return pattern == b'**' + else: + return pattern == '**' + def escape(pathname): """Escape all special characters. """ |