Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit fbe94c5

Browse files
author
Robert Schuppenies
committed
Merged revisions 64842,64853,64856,64945 via svnmerge from
svn+ssh://[email protected]/python/trunk ........ r64842 | robert.schuppenies | 2008-07-10 15:43:26 +0200 (Thu, 10 Jul 2008) | 2 lines Fixed Issue3122 and extended sys.getsizeof tests for built-in types. ........ r64853 | robert.schuppenies | 2008-07-10 17:24:04 +0200 (Thu, 10 Jul 2008) | 3 lines Added additional __sizeof__ implementations and addressed comments made in Issue3122. ........ r64856 | robert.schuppenies | 2008-07-10 19:13:55 +0200 (Thu, 10 Jul 2008) | 3 lines Added garbage collector overhead and optional default return value to sys.getsizeof. ........ r64945 | robert.schuppenies | 2008-07-14 10:42:18 +0200 (Mon, 14 Jul 2008) | 2 lines Fixed test failure on Win64 machines. ........
1 parent 3065b87 commit fbe94c5

9 files changed

Lines changed: 343 additions & 104 deletions

File tree

Doc/library/sys.rst

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -331,13 +331,20 @@ always available.
331331
:func:`setrecursionlimit`.
332332

333333

334-
.. function:: getsizeof(object)
334+
.. function:: getsizeof(object[, default])
335335

336336
Return the size of an object in bytes. The object can be any type of
337337
object. All built-in objects will return correct results, but this
338-
does not have to hold true for third-party extensions as it is implementation
338+
does not have to hold true for third-party extensions as it is implementation
339339
specific.
340340

341+
The *default* argument allows to define a value which will be returned
342+
if the object type does not provide means to retrieve the size and would
343+
cause a `TypeError`.
344+
345+
func:`getsizeof` calls the object's __sizeof__ method and adds an additional
346+
garbage collector overhead if the object is managed by the garbage collector.
347+
341348
.. versionadded:: 2.6
342349

343350

Lib/test/test_sys.py

Lines changed: 229 additions & 68 deletions
Original file line numberDiff line numberDiff line change
@@ -373,6 +373,9 @@ def test_ioencoding(self):
373373

374374
class SizeofTest(unittest.TestCase):
375375

376+
TPFLAGS_HAVE_GC = 1<<14
377+
TPFLAGS_HEAPTYPE = 1<<9
378+
376379
def setUp(self):
377380
self.c = len(struct.pack('c', ' '))
378381
self.H = len(struct.pack('H', 0))
@@ -382,22 +385,27 @@ def setUp(self):
382385
# due to missing size_t information from struct, it is assumed that
383386
# sizeof(Py_ssize_t) = sizeof(void*)
384387
self.header = 'PP'
388+
self.vheader = self.header + 'P'
385389
if hasattr(sys, "gettotalrefcount"):
386390
self.header += '2P'
391+
self.vheader += '2P'
392+
import _testcapi
393+
self.gc_headsize = _testcapi.SIZEOF_PYGC_HEAD
387394
self.file = open(test.support.TESTFN, 'wb')
388395

389396
def tearDown(self):
390397
self.file.close()
391398
test.support.unlink(test.support.TESTFN)
392399

393-
def check_sizeof(self, o, size, size2=None):
394-
"""Check size of o. Possible are size and optionally size2)."""
400+
def check_sizeof(self, o, size):
395401
result = sys.getsizeof(o)
396-
msg = 'wrong size for %s: got %d, expected ' % (type(o), result)
397-
if (size2 != None) and (result != size):
398-
self.assertEqual(result, size2, msg + str(size2))
399-
else:
400-
self.assertEqual(result, size, msg + str(size))
402+
# add GC header size
403+
if ((type(o) == type) and (o.__flags__ & self.TPFLAGS_HEAPTYPE) or\
404+
((type(o) != type) and (type(o).__flags__ & self.TPFLAGS_HAVE_GC))):
405+
size += self.gc_headsize
406+
msg = 'wrong size for %s: got %d, expected %d' \
407+
% (type(o), result, size)
408+
self.assertEqual(result, size, msg)
401409

402410
def calcsize(self, fmt):
403411
"""Wrapper around struct.calcsize which enforces the alignment of the
@@ -408,29 +416,116 @@ def calcsize(self, fmt):
408416
"""
409417
return struct.calcsize(fmt + '0P')
410418

411-
def test_standardtypes(self):
419+
def test_gc_head_size(self):
420+
# Check that the gc header size is added to objects tracked by the gc.
421+
h = self.header
422+
vh = self.vheader
423+
size = self.calcsize
424+
gc_header_size = self.gc_headsize
425+
# bool objects are not gc tracked
426+
self.assertEqual(sys.getsizeof(True), size(vh) + self.H)
427+
# but lists are
428+
self.assertEqual(sys.getsizeof([]), size(vh + 'PP') + gc_header_size)
429+
430+
def test_default(self):
412431
h = self.header
432+
vh = self.vheader
413433
size = self.calcsize
434+
self.assertEqual(sys.getsizeof(True), size(vh) + self.H)
435+
self.assertEqual(sys.getsizeof(True, -1), size(vh) + self.H)
436+
437+
def test_objecttypes(self):
438+
# check all types defined in Objects/
439+
h = self.header
440+
vh = self.vheader
441+
size = self.calcsize
442+
check = self.check_sizeof
443+
# bool
444+
check(True, size(vh) + self.H)
445+
# buffer
446+
# XXX
447+
# builtin_function_or_method
448+
check(len, size(h + '3P'))
449+
# bytearray
450+
samples = [b'', b'u'*100000]
451+
for sample in samples:
452+
x = bytearray(sample)
453+
check(x, size(vh + 'iPP') + x.__alloc__() * self.c)
454+
# bytearray_iterator
455+
check(iter(bytearray()), size(h + 'PP'))
414456
# cell
415457
def get_cell():
416458
x = 42
417459
def inner():
418460
return x
419461
return inner
420-
self.check_sizeof(get_cell().__closure__[0], size(h + 'P'))
462+
check(get_cell().__closure__[0], size(h + 'P'))
421463
# code
422-
self.check_sizeof(get_cell().__code__, size(h + '5i8Pi2P'))
464+
check(get_cell().__code__, size(h + '5i8Pi2P'))
423465
# complex
424-
self.check_sizeof(complex(0,1), size(h + '2d'))
466+
check(complex(0,1), size(h + '2d'))
467+
# method_descriptor (descriptor object)
468+
check(str.lower, size(h + '2PP'))
469+
# classmethod_descriptor (descriptor object)
470+
# XXX
471+
# member_descriptor (descriptor object)
472+
import datetime
473+
check(datetime.timedelta.days, size(h + '2PP'))
474+
# getset_descriptor (descriptor object)
475+
import collections
476+
check(collections.defaultdict.default_factory, size(h + '2PP'))
477+
# wrapper_descriptor (descriptor object)
478+
check(int.__add__, size(h + '2P2P'))
479+
# method-wrapper (descriptor object)
480+
check({}.__iter__, size(h + '2P'))
481+
# dict
482+
check({}, size(h + '3P2P' + 8*'P2P'))
483+
longdict = {1:1, 2:2, 3:3, 4:4, 5:5, 6:6, 7:7, 8:8}
484+
check(longdict, size(h + '3P2P' + 8*'P2P') + 16*size('P2P'))
485+
# dictionary-keyiterator
486+
check({}.keys(), size(h + 'P'))
487+
# dictionary-valueiterator
488+
check({}.values(), size(h + 'P'))
489+
# dictionary-itemiterator
490+
check({}.items(), size(h + 'P'))
491+
# dictproxy
492+
class C(object): pass
493+
check(C.__dict__, size(h + 'P'))
494+
# BaseException
495+
check(BaseException(), size(h + '5P'))
496+
# UnicodeEncodeError
497+
check(UnicodeEncodeError("", "", 0, 0, ""), size(h + '5P 2P2PP'))
498+
# UnicodeDecodeError
499+
# XXX
500+
# check(UnicodeDecodeError("", "", 0, 0, ""), size(h + '5P2PP'))
501+
# UnicodeTranslateError
502+
check(UnicodeTranslateError("", 0, 1, ""), size(h + '5P 2P2PP'))
503+
# ellipses
504+
check(Ellipsis, size(h + ''))
505+
# EncodingMap
506+
import codecs, encodings.iso8859_3
507+
x = codecs.charmap_build(encodings.iso8859_3.decoding_table)
508+
check(x, size(h + '32B2iB'))
425509
# enumerate
426-
self.check_sizeof(enumerate([]), size(h + 'l3P'))
510+
check(enumerate([]), size(h + 'l3P'))
427511
# reverse
428-
self.check_sizeof(reversed(''), size(h + 'PP'))
512+
check(reversed(''), size(h + 'PP'))
429513
# float
430-
self.check_sizeof(float(0), size(h + 'd'))
514+
check(float(0), size(h + 'd'))
515+
# sys.floatinfo
516+
check(sys.float_info, size(vh) + self.P * len(sys.float_info))
517+
# frame
518+
import inspect
519+
CO_MAXBLOCKS = 20
520+
x = inspect.currentframe()
521+
ncells = len(x.f_code.co_cellvars)
522+
nfrees = len(x.f_code.co_freevars)
523+
extras = x.f_code.co_stacksize + x.f_code.co_nlocals +\
524+
ncells + nfrees - 1
525+
check(x, size(vh + '12P3i' + CO_MAXBLOCKS*'3i' + 'P' + extras*'P'))
431526
# function
432527
def func(): pass
433-
self.check_sizeof(func, size(h + '11P'))
528+
check(func, size(h + '11P'))
434529
class c():
435530
@staticmethod
436531
def foo():
@@ -439,72 +534,138 @@ def foo():
439534
def bar(cls):
440535
pass
441536
# staticmethod
442-
self.check_sizeof(foo, size(h + 'P'))
537+
check(foo, size(h + 'P'))
443538
# classmethod
444-
self.check_sizeof(bar, size(h + 'P'))
539+
check(bar, size(h + 'P'))
445540
# generator
446541
def get_gen(): yield 1
447-
self.check_sizeof(get_gen(), size(h + 'Pi2P'))
448-
# builtin_function_or_method
449-
self.check_sizeof(abs, size(h + '3P'))
542+
check(get_gen(), size(h + 'Pi2P'))
543+
# iterator
544+
check(iter('abc'), size(h + 'lP'))
545+
# callable-iterator
546+
import re
547+
check(re.finditer('',''), size(h + '2P'))
548+
# list
549+
samples = [[], [1,2,3], ['1', '2', '3']]
550+
for sample in samples:
551+
check(sample, size(vh + 'PP') + len(sample)*self.P)
552+
# sortwrapper (list)
553+
# XXX
554+
# cmpwrapper (list)
555+
# XXX
556+
# listiterator (list)
557+
check(iter([]), size(h + 'lP'))
558+
# listreverseiterator (list)
559+
check(reversed([]), size(h + 'lP'))
560+
# long
561+
check(0, size(vh))
562+
check(1, size(vh) + self.H)
563+
check(-1, size(vh) + self.H)
564+
check(32768, size(vh) + 2*self.H)
565+
check(32768*32768-1, size(vh) + 2*self.H)
566+
check(32768*32768, size(vh) + 3*self.H)
567+
# memory
568+
check(memoryview(b''), size(h + 'P P2P2i5P'))
450569
# module
451-
self.check_sizeof(unittest, size(h + '3P'))
570+
check(unittest, size(h + '3P'))
571+
# None
572+
check(None, size(h + ''))
573+
# NotImplementedType
574+
check(NotImplemented, size(h))
575+
# object
576+
check(object(), size(h + ''))
577+
# property (descriptor object)
578+
class C(object):
579+
def getx(self): return self.__x
580+
def setx(self, value): self.__x = value
581+
def delx(self): del self.__x
582+
x = property(getx, setx, delx, "")
583+
check(x, size(h + '4Pi'))
584+
# PyCObject
585+
# XXX
586+
# rangeiterator
587+
check(iter(range(1)), size(h + '4l'))
588+
# reverse
589+
check(reversed(''), size(h + 'PP'))
452590
# range
453-
self.check_sizeof(range(1), size(h + '3P'))
591+
check(range(1), size(h + '3P'))
592+
check(range(66000), size(h + '3l'))
593+
# set
594+
# frozenset
595+
PySet_MINSIZE = 8
596+
samples = [[], range(10), range(50)]
597+
s = size(h + '3P2P' + PySet_MINSIZE*'lP' + 'lP')
598+
for sample in samples:
599+
minused = len(sample)
600+
if minused == 0: tmp = 1
601+
# the computation of minused is actually a bit more complicated
602+
# but this suffices for the sizeof test
603+
minused = minused*2
604+
newsize = PySet_MINSIZE
605+
while newsize <= minused:
606+
newsize = newsize << 1
607+
if newsize <= 8:
608+
check(set(sample), s)
609+
check(frozenset(sample), s)
610+
else:
611+
check(set(sample), s + newsize*struct.calcsize('lP'))
612+
check(frozenset(sample), s + newsize*struct.calcsize('lP'))
613+
# setiterator
614+
check(iter(set()), size(h + 'P3P'))
454615
# slice
455-
self.check_sizeof(slice(0), size(h + '3P'))
456-
457-
h += 'P'
458-
# bool
459-
self.check_sizeof(True, size(h + 'H'))
460-
# new-style class
461-
class class_newstyle(object):
462-
def method():
463-
pass
464-
# type (PyTypeObject + PyNumberMethods + PyMappingMethods +
465-
# PySequenceMethods + PyBufferProcs)
466-
self.check_sizeof(class_newstyle, size(h + 'P2P15Pl4PP9PP11PI') +\
467-
size('16Pi17P 3P 10P 2P 2P'))
468-
469-
def test_specialtypes(self):
470-
h = self.header
471-
size = self.calcsize
472-
# dict
473-
self.check_sizeof({}, size(h + '3P2P') + 8*size('P2P'))
474-
longdict = {1:1, 2:2, 3:3, 4:4, 5:5, 6:6, 7:7, 8:8}
475-
self.check_sizeof(longdict, size(h + '3P2P') + (8+16)*size('P2P'))
616+
check(slice(0), size(h + '3P'))
617+
# super
618+
check(super(int), size(h + '3P'))
619+
# tuple
620+
check((), size(vh))
621+
check((1,2,3), size(vh) + 3*self.P)
622+
# type
623+
# (PyTypeObject + PyNumberMethods + PyMappingMethods +
624+
# PySequenceMethods + PyBufferProcs)
625+
s = size(vh + 'P2P15Pl4PP9PP11PI') + size('16Pi17P 3P 10P 2P 2P')
626+
check(int, s)
627+
# class
628+
class newstyleclass(object): pass
629+
check(newstyleclass, s)
476630
# unicode
477631
usize = len('\0'.encode('unicode-internal'))
478632
samples = ['', '1'*100]
479633
# we need to test for both sizes, because we don't know if the string
480634
# has been cached
481635
for s in samples:
482636
basicsize = size(h + 'PPliP') + usize * (len(s) + 1)
483-
defenc = bytes(s, 'ascii')
484-
self.check_sizeof(s, basicsize,
485-
size2=basicsize + sys.getsizeof(defenc))
486-
# trigger caching encoded version as bytes object
487-
try:
488-
getattr(sys, s)
489-
except AttributeError:
490-
pass
491-
finally:
492-
self.check_sizeof(s, basicsize + sys.getsizeof(defenc))
493-
494-
h += 'P'
495-
# list
496-
self.check_sizeof([], size(h + 'PP'))
497-
self.check_sizeof([1, 2, 3], size(h + 'PP') + 3*self.P)
498-
# long
499-
self.check_sizeof(0, size(h + 'H'))
500-
self.check_sizeof(1, size(h + 'H'))
501-
self.check_sizeof(-1, size(h + 'H'))
502-
self.check_sizeof(32768, size(h + 'H') + self.H)
503-
self.check_sizeof(32768*32768-1, size(h + 'H') + self.H)
504-
self.check_sizeof(32768*32768, size(h + 'H') + 2*self.H)
505-
# tuple
506-
self.check_sizeof((), size(h))
507-
self.check_sizeof((1,2,3), size(h) + 3*self.P)
637+
check(s, basicsize)
638+
# weakref
639+
import weakref
640+
check(weakref.ref(int), size(h + '2Pl2P'))
641+
# weakproxy
642+
# XXX
643+
# weakcallableproxy
644+
check(weakref.proxy(int), size(h + '2Pl2P'))
645+
646+
def test_pythontypes(self):
647+
# check all types defined in Python/
648+
h = self.header
649+
vh = self.vheader
650+
size = self.calcsize
651+
check = self.check_sizeof
652+
# _ast.AST
653+
import _ast
654+
check(_ast.AST(), size(h + ''))
655+
# imp.NullImporter
656+
import imp
657+
check(imp.NullImporter(self.file.name), size(h + ''))
658+
try:
659+
raise TypeError
660+
except TypeError:
661+
tb = sys.exc_info()[2]
662+
# traceback
663+
if tb != None:
664+
check(tb, size(h + '2P2i'))
665+
# symtable entry
666+
# XXX
667+
# sys.flags
668+
check(sys.flags, size(vh) + self.P * len(sys.flags))
508669

509670

510671
def test_main():

Modules/_testcapimodule.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1182,6 +1182,7 @@ PyInit__testcapi(void)
11821182
PyModule_AddObject(m, "ULLONG_MAX", PyLong_FromUnsignedLongLong(PY_ULLONG_MAX));
11831183
PyModule_AddObject(m, "PY_SSIZE_T_MAX", PyLong_FromSsize_t(PY_SSIZE_T_MAX));
11841184
PyModule_AddObject(m, "PY_SSIZE_T_MIN", PyLong_FromSsize_t(PY_SSIZE_T_MIN));
1185+
PyModule_AddObject(m, "SIZEOF_PYGC_HEAD", PyLong_FromSsize_t(sizeof(PyGC_Head)));
11851186

11861187
TestError = PyErr_NewException("_testcapi.error", NULL, NULL);
11871188
Py_INCREF(TestError);

0 commit comments

Comments
 (0)