Skip to content

Commit f9d0240

Browse files
gh-93671: Avoid exponential backtracking in deeply nested sequence patterns in match statements (GH-93680)
Co-authored-by: Łukasz Langa <lukasz@langa.pl> (cherry picked from commit 53a8b17) Co-authored-by: Pablo Galindo Salgado <Pablogsal@gmail.com>
1 parent 98558a8 commit f9d0240

File tree

4 files changed

+36
-3
lines changed

4 files changed

+36
-3
lines changed

Grammar/python.gram

Lines changed: 3 additions & 3 deletions
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

Lines changed: 21 additions & 0 deletions
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
"""
Lines changed: 2 additions & 0 deletions
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

Lines changed: 10 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy