1 """Filename matching with shell patterns.
3 fnmatch(FILENAME, PATTERN) matches according to the local convention.
4 fnmatchcase(FILENAME, PATTERN) always takes case in account.
6 The functions operate by translating the pattern into a regular
7 expression. They cache the compiled regular expressions for speed.
9 The function translate(PATTERN) returns a regular expression
10 corresponding to PATTERN. (It does not compile it.)
15 __all__
= ["filter", "fnmatch","fnmatchcase","translate"]
17 _cache
= {} # Maps text patterns to compiled regexen.
18 _cacheb
= {} # Ditto for bytes patterns.
20 def fnmatch(name
, pat
):
21 """Test whether FILENAME matches PATTERN.
23 Patterns are Unix shell style:
26 ? matches any single character
27 [seq] matches any character in seq
28 [!seq] matches any char not in seq
30 An initial period in FILENAME is not special.
31 Both FILENAME and PATTERN are first case-normalized
32 if the operating system requires it.
33 If you don't want this, use fnmatchcase(FILENAME, PATTERN).
37 name
= os
.path
.normcase(name
)
38 pat
= os
.path
.normcase(pat
)
39 return fnmatchcase(name
, pat
)
41 def _compile_pattern(pat
):
42 cache
= _cacheb
if isinstance(pat
, bytes
) else _cache
43 regex
= cache
.get(pat
)
45 if isinstance(pat
, bytes
):
46 pat_str
= str(pat
, 'ISO-8859-1')
47 res_str
= translate(pat_str
)
48 res
= bytes(res_str
, 'ISO-8859-1')
51 cache
[pat
] = regex
= re
.compile(res
)
54 def filter(names
, pat
):
55 """Return the subset of the list NAMES that match PAT"""
58 pat
= os
.path
.normcase(pat
)
59 match
= _compile_pattern(pat
)
60 if os
.path
is posixpath
:
61 # normcase on posix is NOP. Optimize it away from the loop.
67 if match(os
.path
.normcase(name
)):
71 def fnmatchcase(name
, pat
):
72 """Test whether FILENAME matches PATTERN, including case.
74 This is a version of fnmatch() which doesn't case-normalize
78 match
= _compile_pattern(pat
)
79 return match(name
) is not None
82 """Translate a shell PATTERN to a regular expression.
84 There is no way to quote meta-characters.
98 if j
< n
and pat
[j
] == '!':
100 if j
< n
and pat
[j
] == ']':
102 while j
< n
and pat
[j
] != ']':
107 stuff
= pat
[i
:j
].replace('\\','\\\\')
110 stuff
= '^' + stuff
[1:]
111 elif stuff
[0] == '^':
113 res
= '%s[%s]' % (res
, stuff
)
115 res
= res
+ re
.escape(c
)
116 return res
+ '\Z(?ms)'