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