diff --git a/Lib/test/test_re.py b/Lib/test/test_re.py index 8e7611344b9150d..00fc5102c63e3b2 100644 --- a/Lib/test/test_re.py +++ b/Lib/test/test_re.py @@ -2427,6 +2427,25 @@ def test_keyword_parameters(self): pat.scanner(string='abracadabra', pos=3, endpos=10).search().span(), (7, 9)) + def test_pos_greater_than_endpos(self): + # If endpos is less than pos, no match will be found. + for pattern in ['', r'$', r'^$', r'\b$', r'(?:x|$)']: + for string in ['abc\n', b'abc\n', bytearray(b'abc\n')]: + with self.subTest(pattern=pattern, string=string): + if isinstance(string, str): + p = re.compile(pattern, re.MULTILINE) + else: + p = re.compile(pattern.encode(), re.MULTILINE) + for pos, endpos in [(3, 0), (4, 0), (4, 3), (2, 1)]: + self.assertIsNone(p.match(string, pos, endpos)) + self.assertIsNone(p.fullmatch(string, pos, endpos)) + self.assertIsNone(p.search(string, pos, endpos)) + self.assertIsNone( + p.scanner(string, pos, endpos).match()) + self.assertIsNone( + p.scanner(string, pos, endpos).search()) + self.assertEqual(p.findall(string, pos, endpos), []) + def test_bug_20998(self): # Issue #20998: Fullmatch of repeated single character pattern # with ignore case. diff --git a/Misc/NEWS.d/next/Library/2026-10-22-08-17-51.gh-issue-160316.sRe9pQ.rst b/Misc/NEWS.d/next/Library/2026-10-22-08-17-51.gh-issue-160316.sRe9pQ.rst new file mode 100644 index 000000000000000..0e52137bde01ed5 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2026-10-22-08-17-51.gh-issue-160316.sRe9pQ.rst @@ -0,0 +1,5 @@ +Fix :meth:`re.Pattern.match`, :meth:`re.Pattern.prefixmatch`, +:meth:`re.Pattern.fullmatch` and the ``match()`` and ``prefixmatch()`` methods +of scanner objects returning an empty match, or reading past the end of the +string, when *pos* is greater than *endpos*. They now return ``None``, as +documented. diff --git a/Modules/_sre/sre.c b/Modules/_sre/sre.c index 47c2c4abc3ef24e..8d4b46c0fd14b0e 100644 --- a/Modules/_sre/sre.c +++ b/Modules/_sre/sre.c @@ -899,6 +899,10 @@ pattern_dealloc(PyObject *self) LOCAL(Py_ssize_t) sre_match(SRE_STATE* state, SRE_CODE* pattern) { + /* No match if pos > endpos. */ + if (state->ptr > state->end) { + return 0; + } if (state->charsize == 1) return sre_ucs1_match(state, pattern, 1); if (state->charsize == 2)