|
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 |
|
19 def fnmatch(name, pat): |
|
20 """Test whether FILENAME matches PATTERN. |
|
21 |
|
22 Patterns are Unix shell style: |
|
23 |
|
24 * matches everything |
|
25 ? matches any single character |
|
26 [seq] matches any character in seq |
|
27 [!seq] matches any char not in seq |
|
28 |
|
29 An initial period in FILENAME is not special. |
|
30 Both FILENAME and PATTERN are first case-normalized |
|
31 if the operating system requires it. |
|
32 If you don't want this, use fnmatchcase(FILENAME, PATTERN). |
|
33 """ |
|
34 |
|
35 import os |
|
36 name = os.path.normcase(name) |
|
37 pat = os.path.normcase(pat) |
|
38 return fnmatchcase(name, pat) |
|
39 |
|
40 def filter(names, pat): |
|
41 """Return the subset of the list NAMES that match PAT""" |
|
42 import os,posixpath |
|
43 result=[] |
|
44 pat=os.path.normcase(pat) |
|
45 if not pat in _cache: |
|
46 res = translate(pat) |
|
47 _cache[pat] = re.compile(res) |
|
48 match=_cache[pat].match |
|
49 if os.path is posixpath: |
|
50 # normcase on posix is NOP. Optimize it away from the loop. |
|
51 for name in names: |
|
52 if match(name): |
|
53 result.append(name) |
|
54 else: |
|
55 for name in names: |
|
56 if match(os.path.normcase(name)): |
|
57 result.append(name) |
|
58 return result |
|
59 |
|
60 def fnmatchcase(name, pat): |
|
61 """Test whether FILENAME matches PATTERN, including case. |
|
62 |
|
63 This is a version of fnmatch() which doesn't case-normalize |
|
64 its arguments. |
|
65 """ |
|
66 |
|
67 if not pat in _cache: |
|
68 res = translate(pat) |
|
69 _cache[pat] = re.compile(res) |
|
70 return _cache[pat].match(name) is not None |
|
71 |
|
72 def translate(pat): |
|
73 """Translate a shell PATTERN to a regular expression. |
|
74 |
|
75 There is no way to quote meta-characters. |
|
76 """ |
|
77 |
|
78 i, n = 0, len(pat) |
|
79 res = '' |
|
80 while i < n: |
|
81 c = pat[i] |
|
82 i = i+1 |
|
83 if c == '*': |
|
84 res = res + '.*' |
|
85 elif c == '?': |
|
86 res = res + '.' |
|
87 elif c == '[': |
|
88 j = i |
|
89 if j < n and pat[j] == '!': |
|
90 j = j+1 |
|
91 if j < n and pat[j] == ']': |
|
92 j = j+1 |
|
93 while j < n and pat[j] != ']': |
|
94 j = j+1 |
|
95 if j >= n: |
|
96 res = res + '\\[' |
|
97 else: |
|
98 stuff = pat[i:j].replace('\\','\\\\') |
|
99 i = j+1 |
|
100 if stuff[0] == '!': |
|
101 stuff = '^' + stuff[1:] |
|
102 elif stuff[0] == '^': |
|
103 stuff = '\\' + stuff |
|
104 res = '%s[%s]' % (res, stuff) |
|
105 else: |
|
106 res = res + re.escape(c) |
|
107 return res + "$" |