Skip to content

Commit

Permalink
gh-93671: Avoid exponential backtracking in deeply nested sequence pa…
Browse files Browse the repository at this point in the history
…tterns in match statements (GH-93680)

Co-authored-by: Łukasz Langa <lukasz@langa.pl>
  • Loading branch information
pablogsal and ambv committed Jun 10, 2022
1 parent 21a9a85 commit 53a8b17
Show file tree
Hide file tree
Showing 4 changed files with 36 additions and 3 deletions.
6 changes: 3 additions & 3 deletions Grammar/python.gram
Expand Up @@ -471,7 +471,7 @@ or_pattern[pattern_ty]:
| patterns[asdl_pattern_seq*]='|'.closed_pattern+ {
asdl_seq_LEN(patterns) == 1 ? asdl_seq_GET(patterns, 0) : _PyAST_MatchOr(patterns, EXTRA) }

closed_pattern[pattern_ty]:
closed_pattern[pattern_ty] (memo):
| literal_pattern
| capture_pattern
| wildcard_pattern
Expand Down Expand Up @@ -558,7 +558,7 @@ maybe_star_pattern[pattern_ty]:
| star_pattern
| pattern

star_pattern[pattern_ty]:
star_pattern[pattern_ty] (memo):
| '*' target=pattern_capture_target {
_PyAST_MatchStar(target->v.Name.id, EXTRA) }
| '*' wildcard_pattern {
Expand Down Expand Up @@ -1312,4 +1312,4 @@ invalid_kvpair:
| a=expression !(':') {
RAISE_ERROR_KNOWN_LOCATION(p, PyExc_SyntaxError, a->lineno, a->end_col_offset - 1, a->end_lineno, -1, "':' expected after dictionary key") }
| expression ':' a='*' bitwise_or { RAISE_SYNTAX_ERROR_STARTING_FROM(a, "cannot use a starred expression in a dictionary value") }
| expression a=':' {RAISE_SYNTAX_ERROR_KNOWN_LOCATION(a, "expression expected after dictionary key and ':'") }
| expression a=':' {RAISE_SYNTAX_ERROR_KNOWN_LOCATION(a, "expression expected after dictionary key and ':'") }
21 changes: 21 additions & 0 deletions Lib/test/test_patma.py
Expand Up @@ -3151,6 +3151,27 @@ def f(command): # 0
self.assertListEqual(self._trace(f, "go x"), [1, 2, 3])
self.assertListEqual(self._trace(f, "spam"), [1, 2, 3])

def test_parser_deeply_nested_patterns(self):
# Deeply nested patterns can cause exponential backtracking when parsing.
# See gh-93671 for more information.

levels = 100

patterns = [
"A" + "(" * levels + ")" * levels,
"{1:" * levels + "1" + "}" * levels,
"[" * levels + "1" + "]" * levels,
]

for pattern in patterns:
with self.subTest(pattern):
code = inspect.cleandoc("""
match None:
case {}:
pass
""".format(pattern))
compile(code, "<string>", "exec")


if __name__ == "__main__":
"""
Expand Down
@@ -0,0 +1,2 @@
Fix some exponential backtrace case happening with deeply nested sequence
patterns in match statements. Patch by Pablo Galindo
10 changes: 10 additions & 0 deletions Parser/parser.c

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

0 comments on commit 53a8b17

Please sign in to comment.