diff --git a/Doc/library/weakref.rst b/Doc/library/weakref.rst index 53b13e56021..7c6f6db56f9 100644 --- a/Doc/library/weakref.rst +++ b/Doc/library/weakref.rst @@ -59,10 +59,10 @@ is exposed by the :mod:`weakref` module for the benefit of advanced uses. Not all objects can be weakly referenced; those objects which can include class instances, functions written in Python (but not in C), instance methods, sets, frozensets, file objects, :term:`generator`\s, type objects, sockets, arrays, -deques, and regular expression pattern objects. +deques, regular expression pattern objects, and code objects. .. versionchanged:: 3.2 - Added support for thread.lock and threading.Lock. + Added support for thread.lock, threading.Lock, and code objects. Several built-in types such as :class:`list` and :class:`dict` do not directly support weak references but can add support through subclassing:: diff --git a/Include/code.h b/Include/code.h index c93d861cf00..f0f88cd8f63 100644 --- a/Include/code.h +++ b/Include/code.h @@ -27,6 +27,7 @@ typedef struct { PyObject *co_lnotab; /* string (encoding addr<->lineno mapping) See Objects/lnotab_notes.txt for details. */ void *co_zombieframe; /* for optimization only (see frameobject.c) */ + PyObject *co_weakreflist; /* to support weakrefs to code objects */ } PyCodeObject; /* Masks for co_flags above */ diff --git a/Lib/test/test_code.py b/Lib/test/test_code.py index 53e787a4089..1a8af0db428 100644 --- a/Lib/test/test_code.py +++ b/Lib/test/test_code.py @@ -103,8 +103,10 @@ consts: ('None',) """ import unittest +import weakref import _testcapi + def consts(t): """Yield a doctest-safe sequence of object reprs.""" for elt in t: @@ -131,12 +133,37 @@ class CodeTest(unittest.TestCase): self.assertEquals(co.co_firstlineno, 15) +class CodeWeakRefTest(unittest.TestCase): + + def test_basic(self): + # Create a code object in a clean environment so that we know we have + # the only reference to it left. + namespace = {} + exec("def f(): pass", globals(), namespace) + f = namespace["f"] + del namespace + + self.called = False + def callback(code): + self.called = True + + # f is now the last reference to the function, and through it, the code + # object. While we hold it, check that we can create a weakref and + # deref it. Then delete it, and check that the callback gets called and + # the reference dies. + coderef = weakref.ref(f.__code__, callback) + self.assertTrue(bool(coderef())) + del f + self.assertFalse(bool(coderef())) + self.assertTrue(self.called) + + def test_main(verbose=None): from test.support import run_doctest, run_unittest from test import test_code run_doctest(test_code, verbose) - run_unittest(CodeTest) + run_unittest(CodeTest, CodeWeakRefTest) -if __name__ == '__main__': +if __name__ == "__main__": test_main() diff --git a/Lib/test/test_sys.py b/Lib/test/test_sys.py index 516fa1544df..eb7d1a42aa7 100644 --- a/Lib/test/test_sys.py +++ b/Lib/test/test_sys.py @@ -588,7 +588,7 @@ class SizeofTest(unittest.TestCase): return inner check(get_cell().__closure__[0], size(h + 'P')) # code - check(get_cell().__code__, size(h + '5i8Pi2P')) + check(get_cell().__code__, size(h + '5i8Pi3P')) # complex check(complex(0,1), size(h + '2d')) # method_descriptor (descriptor object) diff --git a/Misc/NEWS b/Misc/NEWS index 5484d3941b7..5f6920a2ea9 100644 --- a/Misc/NEWS +++ b/Misc/NEWS @@ -233,6 +233,8 @@ Core and Builtins instances from being copied with copy.copy(), and bytes subclasses from being pickled properly. +- Code objects now support weak references. + C-API ----- diff --git a/Objects/codeobject.c b/Objects/codeobject.c index a772c56a5b7..3e71e4cde3d 100644 --- a/Objects/codeobject.c +++ b/Objects/codeobject.c @@ -108,6 +108,7 @@ PyCode_New(int argcount, int kwonlyargcount, Py_INCREF(lnotab); co->co_lnotab = lnotab; co->co_zombieframe = NULL; + co->co_weakreflist = NULL; } return co; } @@ -331,6 +332,8 @@ code_dealloc(PyCodeObject *co) Py_XDECREF(co->co_lnotab); if (co->co_zombieframe != NULL) PyObject_GC_Del(co->co_zombieframe); + if (co->co_weakreflist != NULL) + PyObject_ClearWeakRefs((PyObject*)co); PyObject_DEL(co); } @@ -462,7 +465,7 @@ PyTypeObject PyCode_Type = { 0, /* tp_traverse */ 0, /* tp_clear */ code_richcompare, /* tp_richcompare */ - 0, /* tp_weaklistoffset */ + offsetof(PyCodeObject, co_weakreflist), /* tp_weaklistoffset */ 0, /* tp_iter */ 0, /* tp_iternext */ 0, /* tp_methods */