@@ -373,6 +373,9 @@ def test_ioencoding(self):
373373
374374class 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
510671def test_main ():
0 commit comments