1 """Filename matching with shell patterns. 2 3 fnmatch(FILENAME, PATTERN) matches according to the local convention. 4 fnmatchcase(FILENAME, PATTERN) always takes case in account. 5 6 The functions operate by translating the pattern into a regular 7 expression. They cache the compiled regular expressions for speed. 8 9 The function translate(PATTERN) returns a regular expression 10 corresponding to PATTERN. (It does not compile it.) 11 """ 12 13 import re 14 15 __all__ = ["filter", "fnmatch", "fnmatchcase", "translate"] 16 17 _cache = {} 18 _MAXCACHE = 100 19 20 def _purge(): 21 """Clear the pattern cache""" 22 _cache.clear() 23 24 def fnmatch(name, pat): 25 """Test whether FILENAME matches PATTERN. 26 27 Patterns are Unix shell style: 28 29 * matches everything 30 ? matches any single character 31 [seq] matches any character in seq 32 [!seq] matches any char not in seq 33 34 An initial period in FILENAME is not special. 35 Both FILENAME and PATTERN are first case-normalized 36 if the operating system requires it. 37 If you don't want this, use fnmatchcase(FILENAME, PATTERN). 38 """ 39 40 import os 41 name = os.path.normcase(name) 42 pat = os.path.normcase(pat) 43 return fnmatchcase(name, pat) 44 45 def filter(names, pat): 46 """Return the subset of the list NAMES that match PAT""" 47 import os,posixpath 48 result=[] 49 pat=os.path.normcase(pat) 50 if not pat in _cache: 51 res = translate(pat) 52 if len(_cache) >= _MAXCACHE: 53 _cache.clear() 54 _cache[pat] = re.compile(res) 55 match=_cache[pat].match 56 if os.path is posixpath: 57 # normcase on posix is NOP. Optimize it away from the loop. 58 for name in names: 59 if match(name): 60 result.append(name) 61 else: 62 for name in names: 63 if match(os.path.normcase(name)): 64 result.append(name) 65 return result 66 67 def fnmatchcase(name, pat): 68 """Test whether FILENAME matches PATTERN, including case. 69 70 This is a version of fnmatch() which doesn't case-normalize 71 its arguments. 72 """ 73 74 if not pat in _cache: 75 res = translate(pat) 76 if len(_cache) >= _MAXCACHE: 77 _cache.clear() 78 _cache[pat] = re.compile(res) 79 return _cache[pat].match(name) is not None 80 81 def translate(pat): 82 """Translate a shell PATTERN to a regular expression. 83 84 There is no way to quote meta-characters. 85 """ 86 87 i, n = 0, len(pat) 88 res = '' 89 while i < n: 90 c = pat[i] 91 i = i+1 92 if c == '*': 93 res = res + '.*' 94 elif c == '?': 95 res = res + '.' 96 elif c == '[': 97 j = i 98 if j < n and pat[j] == '!': 99 j = j+1 100 if j < n and pat[j] == ']': 101 j = j+1 102 while j < n and pat[j] != ']': 103 j = j+1 104 if j >= n: 105 res = res + '\\[' 106 else: 107 stuff = pat[i:j].replace('\\','\\\\') 108 i = j+1 109 if stuff[0] == '!': 110 stuff = '^' + stuff[1:] 111 elif stuff[0] == '^': 112 stuff = '\\' + stuff 113 res = '%s[%s]' % (res, stuff) 114 else: 115 res = res + re.escape(c) 116 return res + '\Z(?ms)' 117