From 5f7a0d50f606d72b5b7cfb746b2af1b5b724a77b Mon Sep 17 00:00:00 2001 From: wvmscs <48345137+wvmscs@users.noreply.github.com> Date: Tue, 17 Feb 2026 20:35:11 +0100 Subject: [PATCH 1/7] closes #7183 --- crates/sre_engine/Cargo.toml | 4 ++++ crates/sre_engine/src/engine.rs | 14 ++++++++++---- crates/sre_engine/tests/tests.rs | 12 ++++++++++++ 3 files changed, 26 insertions(+), 4 deletions(-) diff --git a/crates/sre_engine/Cargo.toml b/crates/sre_engine/Cargo.toml index 4f899e6b3e9..4ffd6ab3960 100644 --- a/crates/sre_engine/Cargo.toml +++ b/crates/sre_engine/Cargo.toml @@ -14,6 +14,10 @@ license.workspace = true name = "benches" harness = false +[[test]] +name = "tests" + + [dependencies] rustpython-wtf8 = { workspace = true } num_enum = { workspace = true } diff --git a/crates/sre_engine/src/engine.rs b/crates/sre_engine/src/engine.rs index 36a7a9d485e..2bdcaa2a3de 100644 --- a/crates/sre_engine/src/engine.rs +++ b/crates/sre_engine/src/engine.rs @@ -469,8 +469,10 @@ fn _match(req: &Request<'_, S>, state: &mut State, mut ctx: MatchCo } Jump::PossessiveRepeat1 => { let min_count = ctx.peek_code(req, 2) as isize; - if ctx.count < min_count { - break 'context ctx.next_offset(4, Jump::PossessiveRepeat2); + if ctx.count < min_count { // modified next.toplevel from herited to false + let mut next = ctx.next_offset(4, Jump::PossessiveRepeat2); + next.toplevel = false; + break 'context next; } // zero match protection ctx.cursor.position = usize::MAX; @@ -494,7 +496,9 @@ fn _match(req: &Request<'_, S>, state: &mut State, mut ctx: MatchCo { state.marks.push(); ctx.cursor = state.cursor; - break 'context ctx.next_offset(4, Jump::PossessiveRepeat4); + let mut next = ctx.next_offset(4, Jump::PossessiveRepeat4); + next.toplevel = false; // modified next.toplevel from herited to false + break 'context next; } ctx.cursor = state.cursor; ctx.skip_code_from(req, 1); @@ -832,7 +836,9 @@ fn _match(req: &Request<'_, S>, state: &mut State, mut ctx: MatchCo /* pattern tail */ SreOpcode::ATOMIC_GROUP => { state.cursor = ctx.cursor; - break 'context ctx.next_offset(2, Jump::AtomicGroup1); + let mut next_ctx = ctx.next_offset(2, Jump::AtomicGroup1); + next_ctx.toplevel = false; // modified next.toplevel from herited to false + break 'context next_ctx; } /* <1=min> <2=max> pattern tail */ diff --git a/crates/sre_engine/tests/tests.rs b/crates/sre_engine/tests/tests.rs index 72a11c55b13..fc5c1409142 100644 --- a/crates/sre_engine/tests/tests.rs +++ b/crates/sre_engine/tests/tests.rs @@ -145,6 +145,18 @@ fn test_possessive_quantifier() { assert!(state.py_match(&req)); } +#[test] +fn test_possessive_repeat_fullmatch() { + // pattern p = re.compile("([0-9]++(?:\.[0-9]+)*+)", re.I ) + // [INFO, 4, 0, 1, 4294967295, MARK, 0, POSSESSIVE_REPEAT_ONE, 10, 1, MAXREPEAT, IN, 5, RANGE, 48, 57, FAILURE, SUCCESS, POSSESSIVE_REPEAT, 16, 0, MAXREPEAT, LITERAL, 46, REPEAT_ONE, 10, 1, MAXREPEAT, IN, 5, RANGE, 48, 57, FAILURE, SUCCESS, SUCCESS, MARK, 1, SUCCESS] + // START GENERATED by generate_tests.py + #[rustfmt::skip] let p = Pattern { pattern: "([0-9]++(?:\\.[0-9]+)*+)", code: &[14, 4, 0, 1, 4294967295, 17, 0, 29, 10, 1, 4294967295, 13, 5, 22, 48, 57, 0, 1, 28, 16, 0, 4294967295, 16, 46, 24, 10, 1, 4294967295, 13, 5, 22, 48, 57, 0, 1, 1, 17, 1, 1] }; + // END GENERATED + let (mut req, mut state) = p.state("1.25.38"); + req.match_all = true; + assert!(state.py_match(&req), "should match"); +} + #[test] fn test_possessive_atomic_group() { // pattern p = re.compile('(?>x)++x') From b741934ea8063fd9bc2b5e9072ffb6e12a3e94de Mon Sep 17 00:00:00 2001 From: wvmscs <48345137+wvmscs@users.noreply.github.com> Date: Tue, 17 Feb 2026 23:05:00 +0100 Subject: [PATCH 2/7] closes #7183 - tests modifications --- Lib/test/test_re.py | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/Lib/test/test_re.py b/Lib/test/test_re.py index b415c5907fa..9090e023164 100644 --- a/Lib/test/test_re.py +++ b/Lib/test/test_re.py @@ -2537,7 +2537,8 @@ def test_possessive_quantifiers(self): self.assertIsNone(re.match("^x{}+$", "xxx")) self.assertTrue(re.match("^x{}+$", "x{}")) - @unittest.expectedFailure # TODO: RUSTPYTHON + # correction #7183 make it successful as in Cpython3.14 + #@unittest.expectedFailure # TODO: RUSTPYTHON def test_fullmatch_possessive_quantifiers(self): self.assertTrue(re.fullmatch(r'a++', 'a')) self.assertTrue(re.fullmatch(r'a*+', 'a')) @@ -2565,6 +2566,11 @@ def test_fullmatch_possessive_quantifiers(self): self.assertTrue(re.fullmatch(r'(?:ab)?+c', 'abc')) self.assertTrue(re.fullmatch(r'(?:ab){1,3}+c', 'abc')) + # added for test of correction #7183 + def test_possessive_repeat(self): + self.assertTrue(re.fullmatch("([0-9]++(?:\.[0-9]+)*+)", "1.25.38")) + self.assertEqual(re.fullmatch("([0-9]++(?:\.[0-9]+)*+)", "1.25.38").groups()[0], "1.25.38") + def test_findall_possessive_quantifiers(self): self.assertEqual(re.findall(r'a++', 'aab'), ['aa']) self.assertEqual(re.findall(r'a*+', 'aab'), ['aa', '', '']) @@ -2590,7 +2596,8 @@ def test_atomic_grouping(self): self.assertIsNone(re.match(r'(?>x)++x', 'xxx')) self.assertIsNone(re.match(r'(?>x++)x', 'xxx')) - @unittest.expectedFailure # TODO: RUSTPYTHON + # correction #7183 make it successful as in Cpython3.14 + #@unittest.expectedFailure # TODO: RUSTPYTHON def test_fullmatch_atomic_grouping(self): self.assertTrue(re.fullmatch(r'(?>a+)', 'a')) self.assertTrue(re.fullmatch(r'(?>a*)', 'a')) @@ -2629,7 +2636,8 @@ def test_findall_atomic_grouping(self): self.assertEqual(re.findall(r'(?>(?:ab)?)', 'ababc'), ['ab', 'ab', '', '']) self.assertEqual(re.findall(r'(?>(?:ab){1,3})', 'ababc'), ['abab']) - @unittest.expectedFailure # TODO: RUSTPYTHON + # correction #7183 make it success as in Cpython3.14 + #@unittest.expectedFailure # TODO: RUSTPYTHON def test_bug_gh91616(self): self.assertTrue(re.fullmatch(r'(?s:(?>.*?\.).*)\z', "a.txt")) # reproducer self.assertTrue(re.fullmatch(r'(?s:(?=(?P.*?\.))(?P=g0).*)\z', "a.txt")) From 5602e75c0a6d9b29c2baf4eccfc58c31799ed245 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Tue, 17 Feb 2026 22:20:12 +0000 Subject: [PATCH 3/7] Auto-format: cargo fmt --all --- crates/sre_engine/src/engine.rs | 5 +++-- crates/sre_engine/tests/tests.rs | 2 +- crates/vm/src/stdlib/sys.rs | 3 +-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/crates/sre_engine/src/engine.rs b/crates/sre_engine/src/engine.rs index 2bdcaa2a3de..cc4f846e03b 100644 --- a/crates/sre_engine/src/engine.rs +++ b/crates/sre_engine/src/engine.rs @@ -469,7 +469,8 @@ fn _match(req: &Request<'_, S>, state: &mut State, mut ctx: MatchCo } Jump::PossessiveRepeat1 => { let min_count = ctx.peek_code(req, 2) as isize; - if ctx.count < min_count { // modified next.toplevel from herited to false + if ctx.count < min_count { + // modified next.toplevel from herited to false let mut next = ctx.next_offset(4, Jump::PossessiveRepeat2); next.toplevel = false; break 'context next; @@ -837,7 +838,7 @@ fn _match(req: &Request<'_, S>, state: &mut State, mut ctx: MatchCo SreOpcode::ATOMIC_GROUP => { state.cursor = ctx.cursor; let mut next_ctx = ctx.next_offset(2, Jump::AtomicGroup1); - next_ctx.toplevel = false; // modified next.toplevel from herited to false + next_ctx.toplevel = false; // modified next.toplevel from herited to false break 'context next_ctx; } /* <1=min> <2=max> pattern diff --git a/crates/sre_engine/tests/tests.rs b/crates/sre_engine/tests/tests.rs index fc5c1409142..795c9a05d42 100644 --- a/crates/sre_engine/tests/tests.rs +++ b/crates/sre_engine/tests/tests.rs @@ -150,7 +150,7 @@ fn test_possessive_repeat_fullmatch() { // pattern p = re.compile("([0-9]++(?:\.[0-9]+)*+)", re.I ) // [INFO, 4, 0, 1, 4294967295, MARK, 0, POSSESSIVE_REPEAT_ONE, 10, 1, MAXREPEAT, IN, 5, RANGE, 48, 57, FAILURE, SUCCESS, POSSESSIVE_REPEAT, 16, 0, MAXREPEAT, LITERAL, 46, REPEAT_ONE, 10, 1, MAXREPEAT, IN, 5, RANGE, 48, 57, FAILURE, SUCCESS, SUCCESS, MARK, 1, SUCCESS] // START GENERATED by generate_tests.py - #[rustfmt::skip] let p = Pattern { pattern: "([0-9]++(?:\\.[0-9]+)*+)", code: &[14, 4, 0, 1, 4294967295, 17, 0, 29, 10, 1, 4294967295, 13, 5, 22, 48, 57, 0, 1, 28, 16, 0, 4294967295, 16, 46, 24, 10, 1, 4294967295, 13, 5, 22, 48, 57, 0, 1, 1, 17, 1, 1] }; + #[rustfmt::skip] let p = Pattern { pattern: "([0-9]++(?:\\.[0-9]+)*+)", code: &[14, 4, 0, 1, 4294967295, 17, 0, 29, 10, 1, 4294967295, 13, 5, 22, 48, 57, 0, 1, 28, 16, 0, 4294967295, 16, 46, 24, 10, 1, 4294967295, 13, 5, 22, 48, 57, 0, 1, 1, 17, 1, 1] }; // END GENERATED let (mut req, mut state) = p.state("1.25.38"); req.match_all = true; diff --git a/crates/vm/src/stdlib/sys.rs b/crates/vm/src/stdlib/sys.rs index 22b720a1cd2..3ee6caf2eae 100644 --- a/crates/vm/src/stdlib/sys.rs +++ b/crates/vm/src/stdlib/sys.rs @@ -827,8 +827,7 @@ mod sys { Ok(exc) => { // PyErr_Display: try traceback._print_exception_bltin first if let Ok(tb_mod) = vm.import("traceback", 0) - && let Ok(print_exc_builtin) = - tb_mod.get_attr("_print_exception_bltin", vm) + && let Ok(print_exc_builtin) = tb_mod.get_attr("_print_exception_bltin", vm) && print_exc_builtin .call((exc.as_object().to_owned(),), vm) .is_ok() From d3dff2ec11d496c94299e97ae69caac98c71fe0d Mon Sep 17 00:00:00 2001 From: wvmscs <48345137+wvmscs@users.noreply.github.com> Date: Wed, 18 Feb 2026 14:50:42 +0100 Subject: [PATCH 4/7] closes #7183 - complements --- Lib/test/test_re.py | 11 ----------- crates/sre_engine/Cargo.toml | 3 --- crates/sre_engine/src/engine.rs | 6 +++--- extra_tests/snippets/stdlib_re.py | 4 ++++ 4 files changed, 7 insertions(+), 17 deletions(-) diff --git a/Lib/test/test_re.py b/Lib/test/test_re.py index 9090e023164..df132aa787d 100644 --- a/Lib/test/test_re.py +++ b/Lib/test/test_re.py @@ -2537,8 +2537,6 @@ def test_possessive_quantifiers(self): self.assertIsNone(re.match("^x{}+$", "xxx")) self.assertTrue(re.match("^x{}+$", "x{}")) - # correction #7183 make it successful as in Cpython3.14 - #@unittest.expectedFailure # TODO: RUSTPYTHON def test_fullmatch_possessive_quantifiers(self): self.assertTrue(re.fullmatch(r'a++', 'a')) self.assertTrue(re.fullmatch(r'a*+', 'a')) @@ -2566,11 +2564,6 @@ def test_fullmatch_possessive_quantifiers(self): self.assertTrue(re.fullmatch(r'(?:ab)?+c', 'abc')) self.assertTrue(re.fullmatch(r'(?:ab){1,3}+c', 'abc')) - # added for test of correction #7183 - def test_possessive_repeat(self): - self.assertTrue(re.fullmatch("([0-9]++(?:\.[0-9]+)*+)", "1.25.38")) - self.assertEqual(re.fullmatch("([0-9]++(?:\.[0-9]+)*+)", "1.25.38").groups()[0], "1.25.38") - def test_findall_possessive_quantifiers(self): self.assertEqual(re.findall(r'a++', 'aab'), ['aa']) self.assertEqual(re.findall(r'a*+', 'aab'), ['aa', '', '']) @@ -2596,8 +2589,6 @@ def test_atomic_grouping(self): self.assertIsNone(re.match(r'(?>x)++x', 'xxx')) self.assertIsNone(re.match(r'(?>x++)x', 'xxx')) - # correction #7183 make it successful as in Cpython3.14 - #@unittest.expectedFailure # TODO: RUSTPYTHON def test_fullmatch_atomic_grouping(self): self.assertTrue(re.fullmatch(r'(?>a+)', 'a')) self.assertTrue(re.fullmatch(r'(?>a*)', 'a')) @@ -2636,8 +2627,6 @@ def test_findall_atomic_grouping(self): self.assertEqual(re.findall(r'(?>(?:ab)?)', 'ababc'), ['ab', 'ab', '', '']) self.assertEqual(re.findall(r'(?>(?:ab){1,3})', 'ababc'), ['abab']) - # correction #7183 make it success as in Cpython3.14 - #@unittest.expectedFailure # TODO: RUSTPYTHON def test_bug_gh91616(self): self.assertTrue(re.fullmatch(r'(?s:(?>.*?\.).*)\z', "a.txt")) # reproducer self.assertTrue(re.fullmatch(r'(?s:(?=(?P.*?\.))(?P=g0).*)\z', "a.txt")) diff --git a/crates/sre_engine/Cargo.toml b/crates/sre_engine/Cargo.toml index 4ffd6ab3960..ba6865e241c 100644 --- a/crates/sre_engine/Cargo.toml +++ b/crates/sre_engine/Cargo.toml @@ -14,9 +14,6 @@ license.workspace = true name = "benches" harness = false -[[test]] -name = "tests" - [dependencies] rustpython-wtf8 = { workspace = true } diff --git a/crates/sre_engine/src/engine.rs b/crates/sre_engine/src/engine.rs index cc4f846e03b..73e263012fc 100644 --- a/crates/sre_engine/src/engine.rs +++ b/crates/sre_engine/src/engine.rs @@ -470,7 +470,7 @@ fn _match(req: &Request<'_, S>, state: &mut State, mut ctx: MatchCo Jump::PossessiveRepeat1 => { let min_count = ctx.peek_code(req, 2) as isize; if ctx.count < min_count { - // modified next.toplevel from herited to false + // modified next.toplevel from inherited to false let mut next = ctx.next_offset(4, Jump::PossessiveRepeat2); next.toplevel = false; break 'context next; @@ -498,7 +498,7 @@ fn _match(req: &Request<'_, S>, state: &mut State, mut ctx: MatchCo state.marks.push(); ctx.cursor = state.cursor; let mut next = ctx.next_offset(4, Jump::PossessiveRepeat4); - next.toplevel = false; // modified next.toplevel from herited to false + next.toplevel = false; // modified next.toplevel from inherited to false break 'context next; } ctx.cursor = state.cursor; @@ -838,7 +838,7 @@ fn _match(req: &Request<'_, S>, state: &mut State, mut ctx: MatchCo SreOpcode::ATOMIC_GROUP => { state.cursor = ctx.cursor; let mut next_ctx = ctx.next_offset(2, Jump::AtomicGroup1); - next_ctx.toplevel = false; // modified next.toplevel from herited to false + next_ctx.toplevel = false; // modified next.toplevel from inherited to false break 'context next_ctx; } /* <1=min> <2=max> pattern diff --git a/extra_tests/snippets/stdlib_re.py b/extra_tests/snippets/stdlib_re.py index 89d729f2b25..9de53709d3d 100644 --- a/extra_tests/snippets/stdlib_re.py +++ b/extra_tests/snippets/stdlib_re.py @@ -75,3 +75,7 @@ assert re.compile("(?:\w+(?:\s|/(?!>))*)*").match("a /bb />ccc").group() == "a /bb " assert re.compile("(?:(1)?)*").match("111").group() == "111" + +# Test of fix re.fullmatch POSSESSIVE_REPEAT, issue #7183 +assert re.fullmatch(r'([0-9]++(?:.[0-9]+)*+)', '1.25.38') +assert re.fullmatch(r'([0-9]++(?:.[0-9]+)*+)', '1.25.38').groups(0) == '1.25.38' From f62f1172e8b90bc4dac7270d600775572c09748d Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Wed, 18 Feb 2026 13:52:07 +0000 Subject: [PATCH 5/7] Auto-format: ruff format --- extra_tests/snippets/stdlib_re.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/extra_tests/snippets/stdlib_re.py b/extra_tests/snippets/stdlib_re.py index 9de53709d3d..9b0c8efef10 100644 --- a/extra_tests/snippets/stdlib_re.py +++ b/extra_tests/snippets/stdlib_re.py @@ -77,5 +77,5 @@ assert re.compile("(?:(1)?)*").match("111").group() == "111" # Test of fix re.fullmatch POSSESSIVE_REPEAT, issue #7183 -assert re.fullmatch(r'([0-9]++(?:.[0-9]+)*+)', '1.25.38') -assert re.fullmatch(r'([0-9]++(?:.[0-9]+)*+)', '1.25.38').groups(0) == '1.25.38' +assert re.fullmatch(r"([0-9]++(?:.[0-9]+)*+)", "1.25.38") +assert re.fullmatch(r"([0-9]++(?:.[0-9]+)*+)", "1.25.38").groups(0) == "1.25.38" From 86533ffd7b4aaba9cbe1522408648c3270260d27 Mon Sep 17 00:00:00 2001 From: wvmscs <48345137+wvmscs@users.noreply.github.com> Date: Wed, 18 Feb 2026 16:56:49 +0100 Subject: [PATCH 6/7] Fix regex assertions in stdlib_re.py tests --- extra_tests/snippets/stdlib_re.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/extra_tests/snippets/stdlib_re.py b/extra_tests/snippets/stdlib_re.py index 9b0c8efef10..53f21f91734 100644 --- a/extra_tests/snippets/stdlib_re.py +++ b/extra_tests/snippets/stdlib_re.py @@ -77,5 +77,5 @@ assert re.compile("(?:(1)?)*").match("111").group() == "111" # Test of fix re.fullmatch POSSESSIVE_REPEAT, issue #7183 -assert re.fullmatch(r"([0-9]++(?:.[0-9]+)*+)", "1.25.38") -assert re.fullmatch(r"([0-9]++(?:.[0-9]+)*+)", "1.25.38").groups(0) == "1.25.38" +assert re.fullmatch(r"([0-9]++(?:\.[0-9]+)*+)", "1.25.38") +assert re.fullmatch(r"([0-9]++(?:\.[0-9]+)*+)", "1.25.38").group(0) == "1.25.38" From ad109352dbc2fd40a8e26df9f9fdcd7f419af05d Mon Sep 17 00:00:00 2001 From: "Jeong, YunWon" <69878+youknowone@users.noreply.github.com> Date: Fri, 20 Feb 2026 08:18:10 +0900 Subject: [PATCH 7/7] Remove whitespace change --- crates/sre_engine/Cargo.toml | 1 - 1 file changed, 1 deletion(-) diff --git a/crates/sre_engine/Cargo.toml b/crates/sre_engine/Cargo.toml index ba6865e241c..4f899e6b3e9 100644 --- a/crates/sre_engine/Cargo.toml +++ b/crates/sre_engine/Cargo.toml @@ -14,7 +14,6 @@ license.workspace = true name = "benches" harness = false - [dependencies] rustpython-wtf8 = { workspace = true } num_enum = { workspace = true }