Skip to content

Commit ca95366

Browse files
authored
Update fnmatch from 3.13.7 (#6149)
1 parent 43d643a commit ca95366

File tree

3 files changed

+20
-34
lines changed

3 files changed

+20
-34
lines changed

Lib/fnmatch.py

Lines changed: 15 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -16,12 +16,6 @@
1616

1717
__all__ = ["filter", "fnmatch", "fnmatchcase", "translate"]
1818

19-
# Build a thread-safe incrementing counter to help create unique regexp group
20-
# names across calls.
21-
from itertools import count
22-
_nextgroupnum = count().__next__
23-
del count
24-
2519
def fnmatch(name, pat):
2620
"""Test whether FILENAME matches PATTERN.
2721
@@ -41,7 +35,7 @@ def fnmatch(name, pat):
4135
pat = os.path.normcase(pat)
4236
return fnmatchcase(name, pat)
4337

44-
@functools.lru_cache(maxsize=256, typed=True)
38+
@functools.lru_cache(maxsize=32768, typed=True)
4539
def _compile_pattern(pat):
4640
if isinstance(pat, bytes):
4741
pat_str = str(pat, 'ISO-8859-1')
@@ -84,6 +78,11 @@ def translate(pat):
8478
"""
8579

8680
STAR = object()
81+
parts = _translate(pat, STAR, '.')
82+
return _join_translated_parts(parts, STAR)
83+
84+
85+
def _translate(pat, STAR, QUESTION_MARK):
8786
res = []
8887
add = res.append
8988
i, n = 0, len(pat)
@@ -95,7 +94,7 @@ def translate(pat):
9594
if (not res) or res[-1] is not STAR:
9695
add(STAR)
9796
elif c == '?':
98-
add('.')
97+
add(QUESTION_MARK)
9998
elif c == '[':
10099
j = i
101100
if j < n and pat[j] == '!':
@@ -152,9 +151,11 @@ def translate(pat):
152151
else:
153152
add(re.escape(c))
154153
assert i == n
154+
return res
155+
155156

157+
def _join_translated_parts(inp, STAR):
156158
# Deal with STARs.
157-
inp = res
158159
res = []
159160
add = res.append
160161
i, n = 0, len(inp)
@@ -165,17 +166,10 @@ def translate(pat):
165166
# Now deal with STAR fixed STAR fixed ...
166167
# For an interior `STAR fixed` pairing, we want to do a minimal
167168
# .*? match followed by `fixed`, with no possibility of backtracking.
168-
# We can't spell that directly, but can trick it into working by matching
169-
# .*?fixed
170-
# in a lookahead assertion, save the matched part in a group, then
171-
# consume that group via a backreference. If the overall match fails,
172-
# the lookahead assertion won't try alternatives. So the translation is:
173-
# (?=(?P<name>.*?fixed))(?P=name)
174-
# Group names are created as needed: g0, g1, g2, ...
175-
# The numbers are obtained from _nextgroupnum() to ensure they're unique
176-
# across calls and across threads. This is because people rely on the
177-
# undocumented ability to join multiple translate() results together via
178-
# "|" to build large regexps matching "one of many" shell patterns.
169+
# Atomic groups ("(?>...)") allow us to spell that directly.
170+
# Note: people rely on the undocumented ability to join multiple
171+
# translate() results together via "|" to build large regexps matching
172+
# "one of many" shell patterns.
179173
while i < n:
180174
assert inp[i] is STAR
181175
i += 1
@@ -192,8 +186,7 @@ def translate(pat):
192186
add(".*")
193187
add(fixed)
194188
else:
195-
groupnum = _nextgroupnum()
196-
add(f"(?=(?P<g{groupnum}>.*?{fixed}))(?P=g{groupnum})")
189+
add(f"(?>.*?{fixed})")
197190
assert i == n
198191
res = "".join(res)
199192
return fr'(?s:{res})\Z'

Lib/test/test_fnmatch.py

Lines changed: 3 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,7 @@ def test_fnmatchcase(self):
7171
check('usr/bin', 'usr\\bin', False, fnmatchcase)
7272
check('usr\\bin', 'usr\\bin', True, fnmatchcase)
7373

74-
@unittest.expectedFailureIfWindows("TODO: RUSTPYTHON")
74+
@unittest.expectedFailureIfWindows('TODO: RUSTPYTHON')
7575
def test_bytes(self):
7676
self.check_match(b'test', b'te*')
7777
self.check_match(b'test\xff', b'te*\xff')
@@ -239,17 +239,9 @@ def test_translate(self):
239239
self.assertEqual(translate('A*********?[?]?'), r'(?s:A.*.[?].)\Z')
240240
# fancy translation to prevent exponential-time match failure
241241
t = translate('**a*a****a')
242-
digits = re.findall(r'\d+', t)
243-
self.assertEqual(len(digits), 4)
244-
self.assertEqual(digits[0], digits[1])
245-
self.assertEqual(digits[2], digits[3])
246-
g1 = f"g{digits[0]}" # e.g., group name "g4"
247-
g2 = f"g{digits[2]}" # e.g., group name "g5"
248-
self.assertEqual(t,
249-
fr'(?s:(?=(?P<{g1}>.*?a))(?P={g1})(?=(?P<{g2}>.*?a))(?P={g2}).*a)\Z')
242+
self.assertEqual(t, r'(?s:(?>.*?a)(?>.*?a).*a)\Z')
250243
# and try pasting multiple translate results - it's an undocumented
251-
# feature that this works; all the pain of generating unique group
252-
# names across calls exists to support this
244+
# feature that this works
253245
r1 = translate('**a**a**a*')
254246
r2 = translate('**b**b**b*')
255247
r3 = translate('*c*c*c*')

Lib/test/test_shutil.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -196,6 +196,7 @@ def test_rmtree_works_on_bytes(self):
196196
self.assertIsInstance(victim, bytes)
197197
shutil.rmtree(victim)
198198

199+
@unittest.skipIf(sys.platform == 'win32', 'TODO: RUSTPYTHON; flaky')
199200
@os_helper.skip_unless_symlink
200201
def test_rmtree_fails_on_symlink_onerror(self):
201202
tmp = self.mkdtemp()
@@ -1477,7 +1478,7 @@ def test_dont_copy_file_onto_link_to_itself(self):
14771478
finally:
14781479
shutil.rmtree(TESTFN, ignore_errors=True)
14791480

1480-
@unittest.expectedFailureIfWindows('TODO: RUSTPYTHON')
1481+
@unittest.expectedFailureIfWindows('TODO: RUSTPYTHON; AssertionError: SameFileError not raised for copyfile')
14811482
@os_helper.skip_unless_symlink
14821483
def test_dont_copy_file_onto_symlink_to_itself(self):
14831484
# bug 851123.

0 commit comments

Comments
 (0)