From fa5cc5fb1267f8d9403c2d41e80330557b0a9ce0 Mon Sep 17 00:00:00 2001 From: Petr Viktorin Date: Fri, 10 Jul 2026 12:40:30 +0200 Subject: [PATCH 1/3] gh-154775: Don't accept duplicate plus sign when matching complex number --- Grammar/python.gram | 1 - Lib/test/test_patma.py | 28 ++++++++++++++++++ ...-07-27-16-29-10.gh-issue-154775._ISRIk.rst | 2 ++ Parser/parser.c | 29 +------------------ 4 files changed, 31 insertions(+), 29 deletions(-) create mode 100644 Misc/NEWS.d/next/Core_and_Builtins/2026-07-27-16-29-10.gh-issue-154775._ISRIk.rst diff --git a/Grammar/python.gram b/Grammar/python.gram index ac1b4a64f38c75..2713ba9466a0b1 100644 --- a/Grammar/python.gram +++ b/Grammar/python.gram @@ -568,7 +568,6 @@ real_number[expr_ty]: imaginary_number[expr_ty]: | imag=NUMBER { _PyPegen_ensure_imaginary(p, imag) } - | '+' imag=NUMBER { _PyPegen_ensure_imaginary(p, imag) } capture_pattern[pattern_ty]: | target=pattern_capture_target { _PyAST_MatchAs(NULL, target->v.Name.id, EXTRA) } diff --git a/Lib/test/test_patma.py b/Lib/test/test_patma.py index e3aaea84ea7ce8..41c1901ba81dd7 100644 --- a/Lib/test/test_patma.py +++ b/Lib/test/test_patma.py @@ -3329,6 +3329,34 @@ def test_mapping_pattern_duplicate_key_edge_case3(self): pass """) + def test_duplicate_sign_in_complex_1(self): + self.assert_syntax_error(""" + match ...: + case 0 ++ 0j: + pass + """) + + def test_duplicate_sign_in_complex_2(self): + self.assert_syntax_error(""" + match ...: + case 0 -+ 0j: + pass + """) + + def test_duplicate_sign_in_complex_3(self): + self.assert_syntax_error(""" + match ...: + case 0 +- 0j: + pass + """) + + def test_duplicate_sign_in_complex_4(self): + self.assert_syntax_error(""" + match ...: + case 0 -- 0j: + pass + """) + class TestTypeErrors(unittest.TestCase): def test_accepts_positional_subpatterns_0(self): diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2026-07-27-16-29-10.gh-issue-154775._ISRIk.rst b/Misc/NEWS.d/next/Core_and_Builtins/2026-07-27-16-29-10.gh-issue-154775._ISRIk.rst new file mode 100644 index 00000000000000..b0035c7e1bc064 --- /dev/null +++ b/Misc/NEWS.d/next/Core_and_Builtins/2026-07-27-16-29-10.gh-issue-154775._ISRIk.rst @@ -0,0 +1,2 @@ +When matching a complex literal in :keyword:`case` statements, an extraneous +``+`` sign (for example, ``1++1j`` or ``1-+1j``) is no longer allowed. diff --git a/Parser/parser.c b/Parser/parser.c index 4f4fd3ed46d1f0..800db5b490fea9 100644 --- a/Parser/parser.c +++ b/Parser/parser.c @@ -9355,7 +9355,7 @@ real_number_rule(Parser *p) return _res; } -// imaginary_number: NUMBER | '+' NUMBER +// imaginary_number: NUMBER static expr_ty imaginary_number_rule(Parser *p) { @@ -9392,33 +9392,6 @@ imaginary_number_rule(Parser *p) D(fprintf(stderr, "%*c%s imaginary_number[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "NUMBER")); } - { // '+' NUMBER - if (p->error_indicator) { - p->level--; - return NULL; - } - D(fprintf(stderr, "%*c> imaginary_number[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'+' NUMBER")); - Token * _literal; - expr_ty imag; - if ( - (_literal = _PyPegen_expect_token(p, 14)) // token='+' - && - (imag = _PyPegen_number_token(p)) // NUMBER - ) - { - D(fprintf(stderr, "%*c+ imaginary_number[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'+' NUMBER")); - _res = _PyPegen_ensure_imaginary ( p , imag ); - if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { - p->error_indicator = 1; - p->level--; - return NULL; - } - goto done; - } - p->mark = _mark; - D(fprintf(stderr, "%*c%s imaginary_number[%d-%d]: %s failed!\n", p->level, ' ', - p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "'+' NUMBER")); - } _res = NULL; done: p->level--; From 75a28a19f318b4e17be91c2c7fcce261f29e5add Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bartosz=20S=C5=82awecki?= Date: Tue, 28 Jul 2026 00:26:53 +0200 Subject: [PATCH 2/3] Remove incorrect test --- Lib/test/test_patma.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/Lib/test/test_patma.py b/Lib/test/test_patma.py index 41c1901ba81dd7..70ee9154d413c7 100644 --- a/Lib/test/test_patma.py +++ b/Lib/test/test_patma.py @@ -2850,6 +2850,14 @@ def test_patma_266(self): case 0: y = 1 self.assertEqual(x, 0) + def test_patma_265(self): + x = 0 + match x: + case +1e1000: + y = 0 + case 0: + y = 1 + self.assertEqual(x, 0) self.assertEqual(y, 1) def test_patma_frozendict_class_self(self): From 01b3432f9cab4b27c25e160017349f26508736b0 Mon Sep 17 00:00:00 2001 From: johnslavik Date: Tue, 28 Jul 2026 00:35:38 +0200 Subject: [PATCH 3/3] Remove incorrect test (fixup) --- Lib/test/test_patma.py | 16 ---------------- 1 file changed, 16 deletions(-) diff --git a/Lib/test/test_patma.py b/Lib/test/test_patma.py index 70ee9154d413c7..0825d9d980c5e7 100644 --- a/Lib/test/test_patma.py +++ b/Lib/test/test_patma.py @@ -2834,22 +2834,6 @@ def test_patma_264(self): self.assertEqual(x, 0.25 + 1.75j) self.assertEqual(y, 0) - def test_patma_265(self): - x = 0.25 - 1.75j - match x: - case 0.25 - +1.75j: - y = 0 - self.assertEqual(x, 0.25 - 1.75j) - self.assertEqual(y, 0) - - def test_patma_266(self): - x = 0 - match x: - case +1e1000: - y = 0 - case 0: - y = 1 - self.assertEqual(x, 0) def test_patma_265(self): x = 0 match x: