Kaydet (Commit) 47629029 authored tarafından Robert Schuppenies's avatar Robert Schuppenies

Added garbage collector overhead and optional default return value to

sys.getsizeof.
üst 5930d8f0
...@@ -393,13 +393,20 @@ always available. ...@@ -393,13 +393,20 @@ always available.
:func:`setrecursionlimit`. :func:`setrecursionlimit`.
.. function:: getsizeof(object) .. function:: getsizeof(object[, default])
Return the size of an object in bytes. The object can be any type of 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 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. 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 .. versionadded:: 2.6
......
...@@ -389,6 +389,9 @@ class SysModuleTest(unittest.TestCase): ...@@ -389,6 +389,9 @@ class SysModuleTest(unittest.TestCase):
class SizeofTest(unittest.TestCase): class SizeofTest(unittest.TestCase):
TPFLAGS_HAVE_GC = 1<<14
TPFLAGS_HEAPTYPE = 1L<<9
def setUp(self): def setUp(self):
self.c = len(struct.pack('c', ' ')) self.c = len(struct.pack('c', ' '))
self.H = len(struct.pack('H', 0)) self.H = len(struct.pack('H', 0))
...@@ -402,6 +405,8 @@ class SizeofTest(unittest.TestCase): ...@@ -402,6 +405,8 @@ class SizeofTest(unittest.TestCase):
if hasattr(sys, "gettotalrefcount"): if hasattr(sys, "gettotalrefcount"):
self.header += '2P' self.header += '2P'
self.vheader += '2P' self.vheader += '2P'
import _testcapi
self.gc_headsize = _testcapi.SIZEOF_PYGC_HEAD
self.file = open(test.test_support.TESTFN, 'wb') self.file = open(test.test_support.TESTFN, 'wb')
def tearDown(self): def tearDown(self):
...@@ -410,6 +415,9 @@ class SizeofTest(unittest.TestCase): ...@@ -410,6 +415,9 @@ class SizeofTest(unittest.TestCase):
def check_sizeof(self, o, size): def check_sizeof(self, o, size):
result = sys.getsizeof(o) 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' \ msg = 'wrong size for %s: got %d, expected %d' \
% (type(o), result, size) % (type(o), result, size)
self.assertEqual(result, size, msg) self.assertEqual(result, size, msg)
...@@ -423,6 +431,21 @@ class SizeofTest(unittest.TestCase): ...@@ -423,6 +431,21 @@ class SizeofTest(unittest.TestCase):
""" """
return struct.calcsize(fmt + '0P') 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): def test_objecttypes(self):
# check all types defined in Objects/ # check all types defined in Objects/
h = self.header h = self.header
......
...@@ -967,6 +967,7 @@ init_testcapi(void) ...@@ -967,6 +967,7 @@ init_testcapi(void)
PyModule_AddObject(m, "ULLONG_MAX", PyLong_FromUnsignedLongLong(PY_ULLONG_MAX)); 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_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, "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); TestError = PyErr_NewException("_testcapi.error", NULL, NULL);
Py_INCREF(TestError); Py_INCREF(TestError);
......
...@@ -640,9 +640,16 @@ sys_mdebug(PyObject *self, PyObject *args) ...@@ -640,9 +640,16 @@ sys_mdebug(PyObject *self, PyObject *args)
#endif /* USE_MALLOPT */ #endif /* USE_MALLOPT */
static PyObject * 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 */ /* Initialize static variable needed by _PyType_Lookup */
if (str__sizeof__ == NULL) { if (str__sizeof__ == NULL) {
...@@ -651,29 +658,54 @@ sys_getsizeof(PyObject *self, PyObject *args) ...@@ -651,29 +658,54 @@ sys_getsizeof(PyObject *self, PyObject *args)
return NULL; 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 */ /* 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; return NULL;
/* Instance of old-style class */ /* Instance of old-style class */
if (PyInstance_Check(args)) if (PyInstance_Check(o))
return PyInt_FromSsize_t(PyInstance_Type.tp_basicsize); res = PyInt_FromSsize_t(PyInstance_Type.tp_basicsize);
/* all other objects */ /* all other objects */
else { else {
PyObject *method = _PyType_Lookup(Py_TYPE(args), PyObject *method = _PyType_Lookup(Py_TYPE(o),
str__sizeof__); str__sizeof__);
if (method == NULL) { if (method == NULL)
PyErr_Format(PyExc_TypeError, PyErr_Format(PyExc_TypeError,
"Type %.100s doesn't define __sizeof__", "Type %.100s doesn't define __sizeof__",
Py_TYPE(args)->tp_name); Py_TYPE(o)->tp_name);
return NULL; else
} res = PyObject_CallFunctionObjArgs(method, o, NULL);
return PyObject_CallFunctionObjArgs(method, args, 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, PyDoc_STRVAR(getsizeof_doc,
"getsizeof(object) -> int\n\ "getsizeof(object, default) -> int\n\
\n\ \n\
Return the size of object in bytes."); Return the size of object in bytes.");
...@@ -868,7 +900,8 @@ static PyMethodDef sys_methods[] = { ...@@ -868,7 +900,8 @@ static PyMethodDef sys_methods[] = {
{"getrefcount", (PyCFunction)sys_getrefcount, METH_O, getrefcount_doc}, {"getrefcount", (PyCFunction)sys_getrefcount, METH_O, getrefcount_doc},
{"getrecursionlimit", (PyCFunction)sys_getrecursionlimit, METH_NOARGS, {"getrecursionlimit", (PyCFunction)sys_getrecursionlimit, METH_NOARGS,
getrecursionlimit_doc}, 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}, {"_getframe", sys_getframe, METH_VARARGS, getframe_doc},
#ifdef MS_WINDOWS #ifdef MS_WINDOWS
{"getwindowsversion", (PyCFunction)sys_getwindowsversion, METH_NOARGS, {"getwindowsversion", (PyCFunction)sys_getwindowsversion, METH_NOARGS,
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment