diff options
-rw-r--r-- | Doc/library/sys.rst | 11 | ||||
-rw-r--r-- | Lib/test/test_sys.py | 23 | ||||
-rw-r--r-- | Modules/_testcapimodule.c | 1 | ||||
-rw-r--r-- | Python/sysmodule.c | 59 |
4 files changed, 79 insertions, 15 deletions
diff --git a/Doc/library/sys.rst b/Doc/library/sys.rst index 22397f2..871cec7 100644 --- a/Doc/library/sys.rst +++ b/Doc/library/sys.rst @@ -393,13 +393,20 @@ always available. :func:`setrecursionlimit`. -.. function:: getsizeof(object) +.. function:: getsizeof(object[, default]) Return the size of an object in bytes. The object can be any type of object. All built-in objects will return correct results, but this - does not have to hold true for third-party extensions as it is implementation + does not have to hold true for third-party extensions as it is implementation specific. + The *default* argument allows to define a value which will be returned + if the object type does not provide means to retrieve the size and would + cause a `TypeError`. + + func:`getsizeof` calls the object's __sizeof__ method and adds an additional + garbage collector overhead if the object is managed by the garbage collector. + .. versionadded:: 2.6 diff --git a/Lib/test/test_sys.py b/Lib/test/test_sys.py index ff0d7a5..12ba113 100644 --- a/Lib/test/test_sys.py +++ b/Lib/test/test_sys.py @@ -389,6 +389,9 @@ class SysModuleTest(unittest.TestCase): class SizeofTest(unittest.TestCase): + TPFLAGS_HAVE_GC = 1<<14 + TPFLAGS_HEAPTYPE = 1L<<9 + def setUp(self): self.c = len(struct.pack('c', ' ')) self.H = len(struct.pack('H', 0)) @@ -402,6 +405,8 @@ class SizeofTest(unittest.TestCase): if hasattr(sys, "gettotalrefcount"): self.header += '2P' self.vheader += '2P' + import _testcapi + self.gc_headsize = _testcapi.SIZEOF_PYGC_HEAD self.file = open(test.test_support.TESTFN, 'wb') def tearDown(self): @@ -410,6 +415,9 @@ class SizeofTest(unittest.TestCase): def check_sizeof(self, o, size): result = sys.getsizeof(o) + if ((type(o) == type) and (o.__flags__ & self.TPFLAGS_HEAPTYPE) or\ + ((type(o) != type) and (type(o).__flags__ & self.TPFLAGS_HAVE_GC))): + size += self.gc_headsize msg = 'wrong size for %s: got %d, expected %d' \ % (type(o), result, size) self.assertEqual(result, size, msg) @@ -423,6 +431,21 @@ class SizeofTest(unittest.TestCase): """ return struct.calcsize(fmt + '0P') + def test_gc_head_size(self): + # Check that the gc header size is added to objects tracked by the gc. + h = self.header + size = self.calcsize + gc_header_size = self.gc_headsize + # bool objects are not gc tracked + self.assertEqual(sys.getsizeof(True), size(h + 'l')) + # but lists are + self.assertEqual(sys.getsizeof([]), size(h + 'P PP') + gc_header_size) + + def test_default(self): + h = self.header + size = self.calcsize + self.assertEqual(sys.getsizeof(True, -1), size(h + 'l')) + def test_objecttypes(self): # check all types defined in Objects/ h = self.header diff --git a/Modules/_testcapimodule.c b/Modules/_testcapimodule.c index 4a00fb1..2ed81aa 100644 --- a/Modules/_testcapimodule.c +++ b/Modules/_testcapimodule.c @@ -967,6 +967,7 @@ init_testcapi(void) PyModule_AddObject(m, "ULLONG_MAX", PyLong_FromUnsignedLongLong(PY_ULLONG_MAX)); PyModule_AddObject(m, "PY_SSIZE_T_MAX", PyInt_FromSsize_t(PY_SSIZE_T_MAX)); PyModule_AddObject(m, "PY_SSIZE_T_MIN", PyInt_FromSsize_t(PY_SSIZE_T_MIN)); + PyModule_AddObject(m, "SIZEOF_PYGC_HEAD", PyInt_FromSsize_t(sizeof(PyGC_Head))); TestError = PyErr_NewException("_testcapi.error", NULL, NULL); Py_INCREF(TestError); diff --git a/Python/sysmodule.c b/Python/sysmodule.c index a4726bc..4bd0e01 100644 --- a/Python/sysmodule.c +++ b/Python/sysmodule.c @@ -640,9 +640,16 @@ sys_mdebug(PyObject *self, PyObject *args) #endif /* USE_MALLOPT */ static PyObject * -sys_getsizeof(PyObject *self, PyObject *args) +sys_getsizeof(PyObject *self, PyObject *args, PyObject *kwds) { - static PyObject * str__sizeof__ = NULL; + PyObject *res = NULL; + static PyObject *str__sizeof__, *gc_head_size = NULL; + static char *kwlist[] = {"object", "default", 0}; + PyObject *o, *dflt = NULL; + + if (!PyArg_ParseTupleAndKeywords(args, kwds, "O|O:getsizeof", + kwlist, &o, &dflt)) + return NULL; /* Initialize static variable needed by _PyType_Lookup */ if (str__sizeof__ == NULL) { @@ -651,29 +658,54 @@ sys_getsizeof(PyObject *self, PyObject *args) return NULL; } + /* Initialize static variable for GC head size */ + if (gc_head_size == NULL) { + gc_head_size = PyInt_FromSsize_t(sizeof(PyGC_Head)); + if (gc_head_size == NULL) + return NULL; + } + /* Make sure the type is initialized. float gets initialized late */ - if (PyType_Ready(Py_TYPE(args)) < 0) + if (PyType_Ready(Py_TYPE(o)) < 0) return NULL; /* Instance of old-style class */ - if (PyInstance_Check(args)) - return PyInt_FromSsize_t(PyInstance_Type.tp_basicsize); + if (PyInstance_Check(o)) + res = PyInt_FromSsize_t(PyInstance_Type.tp_basicsize); /* all other objects */ else { - PyObject *method = _PyType_Lookup(Py_TYPE(args), + PyObject *method = _PyType_Lookup(Py_TYPE(o), str__sizeof__); - if (method == NULL) { + if (method == NULL) PyErr_Format(PyExc_TypeError, "Type %.100s doesn't define __sizeof__", - Py_TYPE(args)->tp_name); - return NULL; - } - return PyObject_CallFunctionObjArgs(method, args, NULL); + Py_TYPE(o)->tp_name); + else + res = PyObject_CallFunctionObjArgs(method, o, NULL); + } + + /* Has a default value been given? */ + if ((res == NULL) && (dflt != NULL) && + PyErr_ExceptionMatches(PyExc_TypeError)) + { + PyErr_Clear(); + Py_INCREF(dflt); + return dflt; + } + else if (res == NULL) + return res; + + /* add gc_head size */ + if (PyObject_IS_GC(o)) { + PyObject *tmp = res; + res = PyNumber_Add(tmp, gc_head_size); + Py_DECREF(tmp); } + return res; } PyDoc_STRVAR(getsizeof_doc, -"getsizeof(object) -> int\n\ +"getsizeof(object, default) -> int\n\ \n\ Return the size of object in bytes."); @@ -868,7 +900,8 @@ static PyMethodDef sys_methods[] = { {"getrefcount", (PyCFunction)sys_getrefcount, METH_O, getrefcount_doc}, {"getrecursionlimit", (PyCFunction)sys_getrecursionlimit, METH_NOARGS, getrecursionlimit_doc}, - {"getsizeof", sys_getsizeof, METH_O, getsizeof_doc}, + {"getsizeof", (PyCFunction)sys_getsizeof, + METH_VARARGS | METH_KEYWORDS, getsizeof_doc}, {"_getframe", sys_getframe, METH_VARARGS, getframe_doc}, #ifdef MS_WINDOWS {"getwindowsversion", (PyCFunction)sys_getwindowsversion, METH_NOARGS, |