Skip to content

Commit 53a8b17

Browse files
pablogsalambv
andauthored
gh-93671: Avoid exponential backtracking in deeply nested sequence patterns in match statements (GH-93680)
Co-authored-by: Łukasz Langa <[email protected]>
1 parent 21a9a85 commit 53a8b17

File tree

4 files changed

+36
-3
lines changed

4 files changed

+36
-3
lines changed

Grammar/python.gram

+3-3
Original file line numberDiff line numberDiff line change
@@ -471,7 +471,7 @@ or_pattern[pattern_ty]:
471471
| patterns[asdl_pattern_seq*]='|'.closed_pattern+ {
472472
asdl_seq_LEN(patterns) == 1 ? asdl_seq_GET(patterns, 0) : _PyAST_MatchOr(patterns, EXTRA) }
473473

474-
closed_pattern[pattern_ty]:
474+
closed_pattern[pattern_ty] (memo):
475475
| literal_pattern
476476
| capture_pattern
477477
| wildcard_pattern
@@ -558,7 +558,7 @@ maybe_star_pattern[pattern_ty]:
558558
| star_pattern
559559
| pattern
560560

561-
star_pattern[pattern_ty]:
561+
star_pattern[pattern_ty] (memo):
562562
| '*' target=pattern_capture_target {
563563
_PyAST_MatchStar(target->v.Name.id, EXTRA) }
564564
| '*' wildcard_pattern {
@@ -1312,4 +1312,4 @@ invalid_kvpair:
13121312
| a=expression !(':') {
13131313
RAISE_ERROR_KNOWN_LOCATION(p, PyExc_SyntaxError, a->lineno, a->end_col_offset - 1, a->end_lineno, -1, "':' expected after dictionary key") }
13141314
| expression ':' a='*' bitwise_or { RAISE_SYNTAX_ERROR_STARTING_FROM(a, "cannot use a starred expression in a dictionary value") }
1315-
| expression a=':' {RAISE_SYNTAX_ERROR_KNOWN_LOCATION(a, "expression expected after dictionary key and ':'") }
1315+
| expression a=':' {RAISE_SYNTAX_ERROR_KNOWN_LOCATION(a, "expression expected after dictionary key and ':'") }

Lib/test/test_patma.py

+21
Original file line numberDiff line numberDiff line change
@@ -3151,6 +3151,27 @@ def f(command): # 0
31513151
self.assertListEqual(self._trace(f, "go x"), [1, 2, 3])
31523152
self.assertListEqual(self._trace(f, "spam"), [1, 2, 3])
31533153

3154+
def test_parser_deeply_nested_patterns(self):
3155+
# Deeply nested patterns can cause exponential backtracking when parsing.
3156+
# See gh-93671 for more information.
3157+
3158+
levels = 100
3159+
3160+
patterns = [
3161+
"A" + "(" * levels + ")" * levels,
3162+
"{1:" * levels + "1" + "}" * levels,
3163+
"[" * levels + "1" + "]" * levels,
3164+
]
3165+
3166+
for pattern in patterns:
3167+
with self.subTest(pattern):
3168+
code = inspect.cleandoc("""
3169+
match None:
3170+
case {}:
3171+
pass
3172+
""".format(pattern))
3173+
compile(code, "<string>", "exec")
3174+
31543175

31553176
if __name__ == "__main__":
31563177
"""
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Fix some exponential backtrace case happening with deeply nested sequence
2+
patterns in match statements. Patch by Pablo Galindo

Parser/parser.c

+10
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)