1""" 2Filename globbing utility. 3""" 4 5from __future__ import absolute_import 6 7import glob as _glob 8import os 9import os.path 10import re 11 12 13_GLOBSTAR = "**" 14_CONTAINS_GLOB_PATTERN = re.compile("[*?[]") 15 16 17def is_glob_pattern(s): 18 """ 19 Returns true if 's' represents a glob pattern, and false otherwise. 20 """ 21 22 # Copied from glob.has_magic(). 23 return _CONTAINS_GLOB_PATTERN.search(s) is not None 24 25 26def glob(globbed_pathname): 27 """ 28 Return a list of pathnames matching the 'globbed_pathname' pattern. 29 30 In addition to containing simple shell-style wildcards a la fnmatch, 31 the pattern may also contain globstars ("**"), which is recursively 32 expanded to match zero or more subdirectories. 33 """ 34 35 return list(iglob(globbed_pathname)) 36 37 38def iglob(globbed_pathname): 39 """ 40 Emit a list of pathnames matching the 'globbed_pathname' pattern. 41 42 In addition to containing simple shell-style wildcards a la fnmatch, 43 the pattern may also contain globstars ("**"), which is recursively 44 expanded to match zero or more subdirectories. 45 """ 46 47 parts = _split_path(globbed_pathname) 48 parts = _canonicalize(parts) 49 50 index = _find_globstar(parts) 51 if index == -1: 52 for pathname in _glob.iglob(globbed_pathname): 53 # Normalize 'pathname' so exact string comparison can be used later. 54 yield os.path.normpath(pathname) 55 return 56 57 # **, **/, or **/a 58 if index == 0: 59 expand = _expand_curdir 60 61 # a/** or a/**/ or a/**/b 62 else: 63 expand = _expand 64 65 prefix_parts = parts[:index] 66 suffix_parts = parts[index + 1:] 67 68 prefix = os.path.join(*prefix_parts) if prefix_parts else os.curdir 69 suffix = os.path.join(*suffix_parts) if suffix_parts else "" 70 71 for (kind, path) in expand(prefix): 72 if not suffix_parts: 73 yield path 74 75 # Avoid following symlinks to avoid an infinite loop 76 elif suffix_parts and kind == "dir" and not os.path.islink(path): 77 path = os.path.join(path, suffix) 78 for pathname in iglob(path): 79 yield pathname 80 81 82def _split_path(pathname): 83 """ 84 Return 'pathname' as a list of path components. 85 """ 86 87 parts = [] 88 89 while True: 90 (dirname, basename) = os.path.split(pathname) 91 parts.append(basename) 92 if pathname == dirname: 93 parts.append(dirname) 94 break 95 if not dirname: 96 break 97 pathname = dirname 98 99 parts.reverse() 100 return parts 101 102 103def _canonicalize(parts): 104 """ 105 Return a copy of 'parts' with consecutive "**"s coalesced. 106 Raise a ValueError for unsupported uses of "**". 107 """ 108 109 res = [] 110 111 prev_was_globstar = False 112 for p in parts: 113 if p == _GLOBSTAR: 114 # Skip consecutive **'s 115 if not prev_was_globstar: 116 prev_was_globstar = True 117 res.append(p) 118 elif _GLOBSTAR in p: # a/b**/c or a/**b/c 119 raise ValueError("Can only specify glob patterns of the form a/**/b") 120 else: 121 prev_was_globstar = False 122 res.append(p) 123 124 return res 125 126 127def _find_globstar(parts): 128 """ 129 Return the index of the first occurrence of "**" in 'parts'. 130 Return -1 if "**" is not found in the list. 131 """ 132 133 for (i, p) in enumerate(parts): 134 if p == _GLOBSTAR: 135 return i 136 return -1 137 138 139def _list_dir(pathname): 140 """ 141 Return a pair of the subdirectory names and filenames immediately 142 contained within the 'pathname' directory. 143 144 If 'pathname' does not exist, then None is returned. 145 """ 146 147 try: 148 (_root, dirs, files) = os.walk(pathname).next() 149 return (dirs, files) 150 except StopIteration: 151 return None # 'pathname' directory does not exist 152 153 154def _expand(pathname): 155 """ 156 Emit tuples of the form ("dir", dirname) and ("file", filename) 157 of all directories and files contained within the 'pathname' directory. 158 """ 159 160 res = _list_dir(pathname) 161 if res is None: 162 return 163 164 (dirs, files) = res 165 166 # Zero expansion 167 if os.path.basename(pathname): 168 yield ("dir", os.path.join(pathname, "")) 169 170 for f in files: 171 path = os.path.join(pathname, f) 172 yield ("file", path) 173 174 for d in dirs: 175 path = os.path.join(pathname, d) 176 for x in _expand(path): 177 yield x 178 179 180def _expand_curdir(pathname): 181 """ 182 Emit tuples of the form ("dir", dirname) and ("file", filename) 183 of all directories and files contained within the 'pathname' directory. 184 185 The returned pathnames omit a "./" prefix. 186 """ 187 188 res = _list_dir(pathname) 189 if res is None: 190 return 191 192 (dirs, files) = res 193 194 # Zero expansion 195 yield ("dir", "") 196 197 for f in files: 198 yield ("file", f) 199 200 for d in dirs: 201 for x in _expand(d): 202 yield x 203