[Python-checkins] r64856 - in python/trunk: Doc/library/sys.rst Lib/test/test_sys.py Modules/_testcapimodule.c Python/sysmodule.c

robert.schuppenies python-checkins at python.org
Thu Jul 10 19:13:55 CEST 2008


Author: robert.schuppenies
Date: Thu Jul 10 19:13:55 2008
New Revision: 64856

Log:
Added garbage collector overhead and optional default return value to
sys.getsizeof.


Modified:
   python/trunk/Doc/library/sys.rst
   python/trunk/Lib/test/test_sys.py
   python/trunk/Modules/_testcapimodule.c
   python/trunk/Python/sysmodule.c

Modified: python/trunk/Doc/library/sys.rst
==============================================================================
--- python/trunk/Doc/library/sys.rst	(original)
+++ python/trunk/Doc/library/sys.rst	Thu Jul 10 19:13:55 2008
@@ -393,13 +393,20 @@
    :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
 
 

Modified: python/trunk/Lib/test/test_sys.py
==============================================================================
--- python/trunk/Lib/test/test_sys.py	(original)
+++ python/trunk/Lib/test/test_sys.py	Thu Jul 10 19:13:55 2008
@@ -389,6 +389,9 @@
 
 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 @@
         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 @@
 
     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 @@
         """
         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

Modified: python/trunk/Modules/_testcapimodule.c
==============================================================================
--- python/trunk/Modules/_testcapimodule.c	(original)
+++ python/trunk/Modules/_testcapimodule.c	Thu Jul 10 19:13:55 2008
@@ -967,6 +967,7 @@
 	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);

Modified: python/trunk/Python/sysmodule.c
==============================================================================
--- python/trunk/Python/sysmodule.c	(original)
+++ python/trunk/Python/sysmodule.c	Thu Jul 10 19:13:55 2008
@@ -640,9 +640,16 @@
 #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 @@
 			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 @@
 	{"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,


More information about the Python-checkins mailing list