| OLD | NEW |
|---|---|
| 1 """Filename matching with shell patterns. | 1 """Filename matching with shell patterns. |
| 2 | 2 |
| 3 fnmatch(FILENAME, PATTERN) matches according to the local convention. | 3 fnmatch(FILENAME, PATTERN) matches according to the local convention. |
| 4 fnmatchcase(FILENAME, PATTERN) always takes case in account. | 4 fnmatchcase(FILENAME, PATTERN) always takes case in account. |
| 5 | 5 |
| 6 The functions operate by translating the pattern into a regular | 6 The functions operate by translating the pattern into a regular |
| 7 expression. They cache the compiled regular expressions for speed. | 7 expression. They cache the compiled regular expressions for speed. |
| 8 | 8 |
| 9 The function translate(PATTERN) returns a regular expression | 9 The function translate(PATTERN) returns a regular expression |
| 10 corresponding to PATTERN. (It does not compile it.) | 10 corresponding to PATTERN. (It does not compile it.) |
| 11 """ | 11 """ |
| 12 | 12 |
| 13 import re | 13 import re |
| 14 import sys | |
| 14 | 15 |
| 15 __all__ = ["filter", "fnmatch","fnmatchcase","translate"] | 16 __all__ = ["filter", "fnmatch","fnmatchcase","translate"] |
| 16 | 17 |
| 17 _cache = {} | 18 _cache = {} |
| 18 | 19 |
| 19 def fnmatch(name, pat): | 20 def fnmatch(name, pat): |
| 20 """Test whether FILENAME matches PATTERN. | 21 """Test whether FILENAME matches PATTERN. |
| 21 | 22 |
| 22 Patterns are Unix shell style: | 23 Patterns are Unix shell style: |
| 23 | 24 |
| 24 * matches everything | 25 * matches everything |
| 25 ? matches any single character | 26 ? matches any single character |
| 26 [seq] matches any character in seq | 27 [seq] matches any character in seq |
| 27 [!seq] matches any char not in seq | 28 [!seq] matches any char not in seq |
| 28 | 29 |
| 29 An initial period in FILENAME is not special. | 30 An initial period in FILENAME is not special. |
| 30 Both FILENAME and PATTERN are first case-normalized | 31 Both FILENAME and PATTERN are first case-normalized |
| 31 if the operating system requires it. | 32 if the operating system requires it. |
| 32 If you don't want this, use fnmatchcase(FILENAME, PATTERN). | 33 If you don't want this, use fnmatchcase(FILENAME, PATTERN). |
| 33 """ | 34 """ |
| 34 | 35 |
| 35 import os | 36 import os |
| 36 name = os.path.normcase(name) | 37 name = os.path.normcase(name) |
| 37 pat = os.path.normcase(pat) | 38 pat = os.path.normcase(pat) |
| 38 return fnmatchcase(name, pat) | 39 return fnmatchcase(name, pat) |
| 39 | 40 |
| 40 def filter(names, pat): | 41 def filter(names, pat): |
|
GvR
2008/08/22 19:04:51
I wonder if we shouldn't also support isinstance(p
| |
| 41 """Return the subset of the list NAMES that match PAT""" | 42 """Return the subset of the list NAMES that match PAT""" |
| 42 import os,posixpath | 43 import os,posixpath |
| 43 result=[] | 44 result=[] |
| 44 pat=os.path.normcase(pat) | 45 pat=os.path.normcase(pat) |
| 45 if not pat in _cache: | 46 if not pat in _cache: |
| 46 res = translate(pat) | 47 res = translate(pat) |
| 47 _cache[pat] = re.compile(res) | 48 _cache[pat] = re.compile(res) |
| 49 else: | |
| 50 res = None | |
| 48 match=_cache[pat].match | 51 match=_cache[pat].match |
| 49 if os.path is posixpath: | 52 match_bytes = None |
| 50 # normcase on posix is NOP. Optimize it away from the loop. | 53 for name in names: |
|
GvR
2008/08/22 19:04:51
Why'd you get rid of this optimization?
| |
| 51 for name in names: | 54 if isinstance(name, bytes): |
| 52 if match(name): | 55 if not match_bytes: |
| 56 # create match regex for bytes string | |
| 57 charset = sys.getfilesystemencoding() | |
| 58 pat = pat.encode(charset) | |
| 59 if not pat in _cache: | |
| 60 if res is None: | |
| 61 res = translate(pat) | |
| 62 res = res.encode(charset) | |
| 63 _cache[pat] = re.compile(res) | |
| 64 match_bytes = _cache[pat].match | |
| 65 if match_bytes(os.path.normcase(name)): | |
| 53 result.append(name) | 66 result.append(name) |
| 54 else: | 67 elif match(os.path.normcase(name)): |
| 55 for name in names: | |
| 56 if match(os.path.normcase(name)): | |
| 57 result.append(name) | 68 result.append(name) |
| 58 return result | 69 return result |
| 59 | 70 |
| 60 def fnmatchcase(name, pat): | 71 def fnmatchcase(name, pat): |
| 61 """Test whether FILENAME matches PATTERN, including case. | 72 """Test whether FILENAME matches PATTERN, including case. |
| 62 | 73 |
| 63 This is a version of fnmatch() which doesn't case-normalize | 74 This is a version of fnmatch() which doesn't case-normalize |
| 64 its arguments. | 75 its arguments. |
| 65 """ | 76 """ |
| 66 | 77 |
| (...skipping 31 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 98 stuff = pat[i:j].replace('\\','\\\\') | 109 stuff = pat[i:j].replace('\\','\\\\') |
| 99 i = j+1 | 110 i = j+1 |
| 100 if stuff[0] == '!': | 111 if stuff[0] == '!': |
| 101 stuff = '^' + stuff[1:] | 112 stuff = '^' + stuff[1:] |
| 102 elif stuff[0] == '^': | 113 elif stuff[0] == '^': |
| 103 stuff = '\\' + stuff | 114 stuff = '\\' + stuff |
| 104 res = '%s[%s]' % (res, stuff) | 115 res = '%s[%s]' % (res, stuff) |
| 105 else: | 116 else: |
| 106 res = res + re.escape(c) | 117 res = res + re.escape(c) |
| 107 return res + "$" | 118 return res + "$" |
| OLD | NEW |