https://github.com/python/cpython/commit/8c3b566891c4bf8a071588e3377ba933850d9501
commit: 8c3b566891c4bf8a071588e3377ba933850d9501
branch: main
author: sijun-yang <[email protected]>
committer: eendebakpt <[email protected]>
date: 2026-11-03T10:44:16+01:00
summary:
gh-158095: Fix reference cycles in re scanners (#158100)
* Fix reference cycles in re scanners
* 📜🤖 Added by blurb_it.
* gh-158095: Normalize NEWS line endings
* gh-158095: Clear input references in re scanners
* ci: rerun checks
Assisted-by: Codex:GPT-6
---------
Co-authored-by: blurb-it[bot] <43283697+blurb-it[bot]@users.noreply.github.com>
files:
A Misc/NEWS.d/next/Library/2026-09-24-14-59-08.gh-issue-158095.lcKOT4.rst
M Lib/test/test_re.py
M Modules/_sre/sre.c
diff --git a/Lib/test/test_re.py b/Lib/test/test_re.py
index 26c30028665c408..41ee35958b77314 100644
--- a/Lib/test/test_re.py
+++ b/Lib/test/test_re.py
@@ -9,6 +9,7 @@
import sys
import unittest
import warnings
+import weakref
from re import Scanner
from weakref import proxy
@@ -1940,6 +1941,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 000000000000000..4fe92b8c8097d85
--- /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 8d4b46c0fd14b0e..588d48e84cb629c 100644
--- a/Modules/_sre/sre.c
+++ b/Modules/_sre/sre.c
@@ -3023,6 +3023,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;
}
@@ -3030,7 +3032,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;
}
@@ -3040,8 +3045,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);
}
_______________________________________________
Python-checkins mailing list -- [email protected]
To unsubscribe send an email to [email protected]
https://mail.python.org/mailman3//lists/python-checkins.python.org
Member address: [email protected]