comparison env/lib/python3.9/site-packages/glob2/fnmatch.py @ 0:4f3585e2f14b draft default tip

"planemo upload commit 60cee0fc7c0cda8592644e1aad72851dec82c959"
author shellac
date Mon, 22 Mar 2021 18:12:50 +0000
parents
children
comparison
equal deleted inserted replaced
-1:000000000000 0:4f3585e2f14b
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 import os
13 import re
14 try:
15 from functools import lru_cache
16 except ImportError:
17 from .compat import lru_cache
18
19 __all__ = ["filter", "fnmatch", "fnmatchcase", "translate"]
20
21
22 def _norm_paths(path, norm_paths, sep):
23 if norm_paths is None:
24 path = re.sub(r'\/', sep or os.sep, path) # cached internally
25 elif norm_paths:
26 path = os.path.normcase(path)
27 return path
28
29
30 def fnmatch(name, pat, norm_paths=True, case_sensitive=True, sep=None):
31 """Test whether FILENAME matches PATTERN.
32
33 Patterns are Unix shell style:
34
35 * matches everything
36 ? matches any single character
37 [seq] matches any character in seq
38 [!seq] matches any char not in seq
39
40 An initial period in FILENAME is not special.
41 Both FILENAME and PATTERN are first case-normalized
42 if the operating system requires it.
43 If you don't want this, use fnmatchcase(FILENAME, PATTERN).
44
45 :param slashes:
46 :param norm_paths:
47 A tri-state boolean:
48 when true, invokes `os.path,.normcase()` on both paths,
49 when `None`, just equalize slashes/backslashes to `os.sep`,
50 when false, does not touch paths at all.
51
52 Note that a side-effect of `normcase()` on *Windows* is that
53 it converts to lower-case all matches of `?glob()` functions.
54 :param case_sensitive:
55 defines the case-sensitiviness of regex doing the matches
56 :param sep:
57 in case only slahes replaced, what sep-char to substitute with;
58 if false, `os.sep` is used.
59
60 Notice that by default, `normcase()` causes insensitive matching
61 on *Windows*, regardless of `case_insensitive` param.
62 Set ``norm_paths=None, case_sensitive=False`` to preserve
63 verbatim mathces.
64 """
65 name, pat = [_norm_paths(p, norm_paths, sep)
66 for p in (name, pat)]
67
68 return fnmatchcase(name, pat, case_sensitive=case_sensitive)
69
70
71 @lru_cache(maxsize=256, typed=True)
72 def _compile_pattern(pat, case_sensitive):
73 if isinstance(pat, bytes):
74 pat_str = pat.decode('ISO-8859-1')
75 res_str = translate(pat_str)
76 res = res_str.encode('ISO-8859-1')
77 else:
78 res = translate(pat)
79 flags = 0 if case_sensitive else re.IGNORECASE
80 return re.compile(res, flags).match
81
82
83 def filter(names, pat, norm_paths=True, case_sensitive=True, sep=None):
84 """Return the subset of the list NAMES that match PAT."""
85 result = []
86 pat = _norm_paths(pat, norm_paths, sep)
87 match = _compile_pattern(pat, case_sensitive)
88 for name in names:
89 m = match(_norm_paths(name, norm_paths, sep))
90 if m:
91 result.append((name,
92 tuple(_norm_paths(p, norm_paths, sep) for p in m.groups())))
93 return result
94
95
96 def fnmatchcase(name, pat, case_sensitive=True):
97 """Test whether FILENAME matches PATTERN, including case.
98
99 This is a version of fnmatch() which doesn't case-normalize
100 its arguments.
101 """
102 match = _compile_pattern(pat, case_sensitive)
103 return match(name) is not None
104
105
106 def translate(pat):
107 """Translate a shell PATTERN to a regular expression.
108
109 There is no way to quote meta-characters.
110 """
111
112 i, n = 0, len(pat)
113 res = ''
114 while i < n:
115 c = pat[i]
116 i = i+1
117 if c == '*':
118 res = res + '(.*)'
119 elif c == '?':
120 res = res + '(.)'
121 elif c == '[':
122 j = i
123 if j < n and pat[j] == '!':
124 j = j+1
125 if j < n and pat[j] == ']':
126 j = j+1
127 while j < n and pat[j] != ']':
128 j = j+1
129 if j >= n:
130 res = res + '\\['
131 else:
132 stuff = pat[i:j].replace('\\','\\\\')
133 i = j+1
134 if stuff[0] == '!':
135 stuff = '^' + stuff[1:]
136 elif stuff[0] == '^':
137 stuff = '\\' + stuff
138 res = '%s([%s])' % (res, stuff)
139 else:
140 res = res + re.escape(c)
141 return '(?ms)' + res + '\Z'