diff --git a/Lib/test/test_re.py b/Lib/test/test_re.py index 8e7611344b9150..3fdb8396c1d976 100644 --- a/Lib/test/test_re.py +++ b/Lib/test/test_re.py @@ -8,6 +8,7 @@ import sys import unittest import warnings +import weakref from re import Scanner from weakref import proxy @@ -1939,6 +1940,27 @@ def test_finditer(self): self.assertEqual([item.group(0) for item in iter], ["::", "::"]) + def test_finditer_gc(self): + class Text(str): + pass + + class Buffer(bytearray): + pass + + text = Text('a') + text.iterator = re.finditer('a', text) + ref = weakref.ref(text) + del text + gc_collect() + self.assertIsNone(ref()) + + buffer = Buffer(b'a') + buffer.iterator = re.finditer(b'a', buffer) + ref = weakref.ref(buffer) + del buffer + gc_collect() + self.assertIsNone(ref()) + def test_bug_926075(self): self.assertIsNot(re.compile('bug_926075'), re.compile(b'bug_926075')) diff --git a/Misc/NEWS.d/next/Library/2026-09-24-14-59-08.gh-issue-158095.lcKOT4.rst b/Misc/NEWS.d/next/Library/2026-09-24-14-59-08.gh-issue-158095.lcKOT4.rst new file mode 100644 index 00000000000000..4fe92b8c8097d8 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2026-09-24-14-59-08.gh-issue-158095.lcKOT4.rst @@ -0,0 +1,2 @@ +Fix a memory leak in :func:`re.finditer` and regular expression scanners when +their input participates in a reference cycle. diff --git a/Modules/_sre/sre.c b/Modules/_sre/sre.c index 031e972aeba031..d094c59370d024 100644 --- a/Modules/_sre/sre.c +++ b/Modules/_sre/sre.c @@ -3017,6 +3017,8 @@ scanner_traverse(PyObject *op, visitproc visit, void *arg) ScannerObject *self = _ScannerObject_CAST(op); Py_VISIT(Py_TYPE(self)); Py_VISIT(self->pattern); + Py_VISIT(self->state.string); + Py_VISIT(self->state.buffer.obj); return 0; } @@ -3024,7 +3026,10 @@ static int scanner_clear(PyObject *op) { ScannerObject *self = _ScannerObject_CAST(op); + self->state.start = NULL; Py_CLEAR(self->pattern); + Py_CLEAR(self->state.string); + PyBuffer_Release(&self->state.buffer); return 0; } @@ -3034,8 +3039,8 @@ scanner_dealloc(PyObject *self) PyTypeObject *tp = Py_TYPE(self); PyObject_GC_UnTrack(self); ScannerObject *scanner = _ScannerObject_CAST(self); - state_fini(&scanner->state); (void)scanner_clear(self); + state_fini(&scanner->state); tp->tp_free(self); Py_DECREF(tp); }