diff --git a/Lib/ctypes/test/test_arrays.py b/Lib/ctypes/test/test_arrays.py index e635ae1741d..7ee16c1f02d 100644 --- a/Lib/ctypes/test/test_arrays.py +++ b/Lib/ctypes/test/test_arrays.py @@ -116,5 +116,19 @@ class ArrayTestCase(unittest.TestCase): self.failUnlessEqual(sz[1:4:2], "o") self.failUnlessEqual(sz.value, "foo") + def test_cache(self): + # Array types are cached internally in the _ctypes extension, + # in a WeakValueDictionary. Make sure the array type is + # removed from the cache when the itemtype goes away. This + # test will not fail, but will show a leak in the testsuite. + + # Create a new type: + class my_int(c_int): + pass + # Create a new array type based on it: + t1 = my_int * 1 + t2 = my_int * 1 + self.failUnless(t1 is t2) + if __name__ == '__main__': unittest.main() diff --git a/Misc/NEWS b/Misc/NEWS index 0be1934f862..0a7d00c8031 100644 --- a/Misc/NEWS +++ b/Misc/NEWS @@ -364,6 +364,9 @@ Core and builtins Library ------- +- Convert the internal ctypes array type cache to a WeakValueDict so + that array types do not live longer than needed. + - Issue #1786: pdb should use its own stdin/stdout around an exec call and when creating a recursive instance. diff --git a/Modules/_ctypes/_ctypes.c b/Modules/_ctypes/_ctypes.c index 9d7290f8e37..1ff8e12ce5c 100644 --- a/Modules/_ctypes/_ctypes.c +++ b/Modules/_ctypes/_ctypes.c @@ -127,6 +127,7 @@ bytes(cdata) PyObject *PyExc_ArgError; static PyTypeObject Simple_Type; +PyObject *array_types_cache; char *conversion_mode_encoding = NULL; char *conversion_mode_errors = NULL; @@ -4086,16 +4087,10 @@ PyTypeObject Array_Type = { PyObject * CreateArrayType(PyObject *itemtype, Py_ssize_t length) { - static PyObject *cache; PyObject *key; PyObject *result; char name[256]; - if (cache == NULL) { - cache = PyDict_New(); - if (cache == NULL) - return NULL; - } #if (PY_VERSION_HEX < 0x02050000) key = Py_BuildValue("(Oi)", itemtype, length); #else @@ -4103,12 +4098,12 @@ CreateArrayType(PyObject *itemtype, Py_ssize_t length) #endif if (!key) return NULL; - result = PyDict_GetItem(cache, key); + result = PyObject_GetItem(array_types_cache, key); if (result) { - Py_INCREF(result); Py_DECREF(key); return result; - } + } else + PyErr_Clear(); if (!PyType_Check(itemtype)) { PyErr_SetString(PyExc_TypeError, @@ -4138,7 +4133,11 @@ CreateArrayType(PyObject *itemtype, Py_ssize_t length) ); if (!result) return NULL; - PyDict_SetItem(cache, key, result); + if (-1 == PyObject_SetItem(array_types_cache, key, result)) { + Py_DECREF(key); + Py_DECREF(result); + return NULL; + } Py_DECREF(key); return result; } @@ -4951,6 +4950,7 @@ PyMODINIT_FUNC init_ctypes(void) { PyObject *m; + PyObject *weakref; /* Note: ob_type is the metatype (the 'type'), defaults to PyType_Type, @@ -4963,6 +4963,16 @@ init_ctypes(void) if (!m) return; + weakref = PyImport_ImportModule("weakref"); + if (weakref == NULL) + return; + array_types_cache = PyObject_CallMethod(weakref, + "WeakValueDictionary", + NULL); + if (array_types_cache == NULL) + return; + Py_DECREF(weakref); + if (PyType_Ready(&PyCArg_Type) < 0) return;