diff --git a/Lib/test/test_capi/test_watchers.py b/Lib/test/test_capi/test_watchers.py index 1922614ef6..af92430e50 100644 --- a/Lib/test/test_capi/test_watchers.py +++ b/Lib/test/test_capi/test_watchers.py @@ -1,7 +1,7 @@ import unittest from contextlib import contextmanager, ExitStack -from test.support import catch_unraisable_exception, import_helper +from test.support import catch_unraisable_exception, import_helper, gc_collect # Skip this test if the _testcapi module isn't available. @@ -347,6 +347,7 @@ def code_watcher(self, which_watcher): def assert_event_counts(self, exp_created_0, exp_destroyed_0, exp_created_1, exp_destroyed_1): + gc_collect() self.assertEqual( exp_created_0, _testcapi.get_code_watcher_num_created_events(0)) self.assertEqual( diff --git a/Lib/test/test_gc.py b/Lib/test/test_gc.py index cf88a57c36..650e36d476 100644 --- a/Lib/test/test_gc.py +++ b/Lib/test/test_gc.py @@ -220,12 +220,12 @@ class B(object): def test_function(self): # Tricky: f -> d -> f, code should call d.clear() after the exec to - # break the cycle. + # break the cycle. May collect f.__code__ as well. d = {} exec("def f(): pass\n", d) gc.collect() del d - self.assertEqual(gc.collect(), 2) + self.assertTrue(2 <= gc.collect() <= 3) def test_function_tp_clear_leaves_consistent_state(self): # https://github.com/python/cpython/issues/91636 diff --git a/Objects/codeobject.c b/Objects/codeobject.c index ab31b6582c..21dc136cde 100644 --- a/Objects/codeobject.c +++ b/Objects/codeobject.c @@ -6,6 +6,7 @@ #include "pycore_code.h" // _PyCodeConstructor #include "pycore_frame.h" // FRAME_SPECIALS_SIZE #include "pycore_interp.h" // PyInterpreterState.co_extra_freefuncs +#include "pycore_object.h" // _PyObject_SET_DEFERRED_REFCOUNT #include "pycore_opcode.h" // _PyOpcode_Deopt #include "pycore_pystate.h" // _PyInterpreterState_GET() #include "pycore_tuple.h" // _PyTuple_ITEMS() @@ -550,13 +551,15 @@ _PyCode_New(struct _PyCodeConstructor *con) } Py_ssize_t size = PyBytes_GET_SIZE(con->code) / sizeof(_Py_CODEUNIT); - PyCodeObject *co = PyObject_NewVar(PyCodeObject, &PyCode_Type, size); + PyCodeObject *co = PyObject_GC_NewVar(PyCodeObject, &PyCode_Type, size); if (co == NULL) { Py_XDECREF(replacement_locations); PyErr_NoMemory(); return NULL; } init_code(co, con); + _PyObject_SET_DEFERRED_REFCOUNT(co); + _PyObject_GC_TRACK(co); Py_XDECREF(replacement_locations); return co; } @@ -1668,6 +1671,7 @@ code_dealloc(PyCodeObject *co) { notify_code_watchers(PY_CODE_EVENT_DESTROY, co); + _PyObject_GC_UNTRACK(co); if (co->co_extra != NULL) { PyInterpreterState *interp = _PyInterpreterState_GET(); _PyCodeObjectExtra *co_extra = co->co_extra; @@ -1705,7 +1709,14 @@ code_dealloc(PyCodeObject *co) if (co->_co_linearray) { PyMem_Free(co->_co_linearray); } - PyObject_Free(co); + PyObject_GC_Del(co); +} + +static int +code_traverse(PyCodeObject *co, visitproc visit, void *arg) +{ + Py_VISIT(co->co_consts); + return 0; } static PyObject * @@ -2114,9 +2125,9 @@ PyTypeObject PyCode_Type = { PyObject_GenericGetAttr, /* tp_getattro */ 0, /* tp_setattro */ 0, /* tp_as_buffer */ - Py_TPFLAGS_DEFAULT, /* tp_flags */ + Py_TPFLAGS_DEFAULT|Py_TPFLAGS_HAVE_GC, /* tp_flags */ code_new__doc__, /* tp_doc */ - 0, /* tp_traverse */ + (traverseproc)code_traverse, /* tp_traverse */ 0, /* tp_clear */ code_richcompare, /* tp_richcompare */ offsetof(PyCodeObject, co_weakreflist), /* tp_weaklistoffset */