]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
Added garbage collector overhead and optional default return value to
authorRobert Schuppenies <okkotonushi@googlemail.com>
Thu, 10 Jul 2008 17:13:55 +0000 (17:13 +0000)
committerRobert Schuppenies <okkotonushi@googlemail.com>
Thu, 10 Jul 2008 17:13:55 +0000 (17:13 +0000)
sys.getsizeof.

Doc/library/sys.rst
Lib/test/test_sys.py
Modules/_testcapimodule.c
Python/sysmodule.c

index 22397f2955dbaceb0606ef96811a2501564a3f83..871cec7ea3e16ec9d0c2f3e951d4e5dfc4f8dffa 100644 (file)
@@ -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
 
 
index ff0d7a5df8f9a15fe50a0c04305cb959895e254f..12ba113eaefddd81591bc9ca7e1d1a9dc615aaf8 100644 (file)
@@ -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
index 4a00fb1975bfbf0125dcf7b1082932871f23b0ee..2ed81aa5b844ac7cfe5955ac48d4720752c8838d 100644 (file)
@@ -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);
index a4726bc5beb6cff9652258c3e7c8174790dc9cee..4bd0e01eb91ee9460787cf383a65422132120324 100644 (file)
@@ -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,