Skip to content
12 changes: 12 additions & 0 deletions Lib/test/test_builtin.py
Original file line number Diff line number Diff line change
Expand Up @@ -1132,6 +1132,18 @@ def test_filter_dealloc(self):
del i
gc.collect()


Copy link
Member

@picnixz picnixz Aug 18, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please don't simply remove things without answering my question. How long does the test take? does it take minutes, seconds or milliseconds? it it's more than 10 seconds or if the CPU is really strained, then we should keep the CPU resource. Otherwise we shouldn't.

Copy link
Contributor Author

@tangyuan0821 tangyuan0821 Aug 18, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

According to my performance test, the setup phase of this test (creating 100,000 nested filters) is very fast (approximately 0.02-0.03 seconds), but the execution phase (triggering recursion by calling list()) consumes a lot of time and CPU resources, and it may take several minutes to complete or trigger a RecursionError. Therefore, I think the@support.requires_resource('cpu')decorator should be retained, because although the setup of this test is fast, its actual execution will become a resource-intensive stress test.

def test_filter_deep_nesting_recursion_error(self):
# gh-137894: Test that deeply nested filter() iterator chains
# raise RecursionError instead of causing segmentation fault.
# This verifies that the tp_clear method prevents stack overflow
# during garbage collection of cyclic references.
i = filter(bool, range(1000000))
for _ in range(100000):
i = filter(bool, i)

self.assertRaises(RecursionError, list, i)

def test_getattr(self):
self.assertTrue(getattr(sys, 'stdout') is sys.stdout)
self.assertRaises(TypeError, getattr)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
Fix segmentation fault in deeply nested :func:`filter` iterator chains.
Deeply nested filter iterators now properly raise :exc:`RecursionError`
instead of crashing the interpreter.
11 changes: 10 additions & 1 deletion Python/bltinmodule.c
Original file line number Diff line number Diff line change
Expand Up @@ -579,6 +579,15 @@ filter_traverse(PyObject *self, visitproc visit, void *arg)
return 0;
}

static int
filter_clear(PyObject *self)
{
filterobject *lz = _filterobject_CAST(self);
Py_CLEAR(lz->it);
Py_CLEAR(lz->func);
return 0;
}

static PyObject *
filter_next(PyObject *self)
{
Expand Down Expand Up @@ -661,7 +670,7 @@ PyTypeObject PyFilter_Type = {
Py_TPFLAGS_BASETYPE, /* tp_flags */
filter_doc, /* tp_doc */
filter_traverse, /* tp_traverse */
0, /* tp_clear */
filter_clear, /* tp_clear */
0, /* tp_richcompare */
0, /* tp_weaklistoffset */
PyObject_SelfIter, /* tp_iter */
Expand Down
Loading