1#!/usr/local/bin/python3.8
2
3# Copyright, license and disclaimer are at the very end of this file.
4
5# This is the latest, enhanced version of the asizeof.py recipes at
6# <http://ASPN.ActiveState.com/ASPN/Cookbook/Python/Recipe/546530>
7# <http://ASPN.ActiveState.com/ASPN/Cookbook/Python/Recipe/544288>
8
9# Note, objects like ``namedtuples``, ``closure``, and NumPy data
10# ``arange``, ``array``, ``matrix``, etc. are only handled by recent
11# versions of this module.  Sizing of ``__slots__`` has been incorrect
12# in versions before this one.  Also, property ``Asizer.duplicate`` gave
13# incorrect values before this release.  Several other properties
14# have been added to the ``Asizer`` class and the ``print_summary``
15# method has been updated.
16
17'''
18This module exposes 9 functions and 2 classes to obtain lengths and
19sizes of Python objects (for Python 2.6 or later).
20
21Earlier versions of this module supported Python versions down to
22Python 2.2.  If you are using Python 2.5 or older, please consider
23downgrading Pympler to version 0.3.x.
24
25**Public Functions** [#unsafe]_
26
27   Function **asizeof** calculates the combined (approximate) size
28   in bytes of one or several Python objects.
29
30   Function **asizesof** returns a tuple containing the (approximate)
31   size in bytes for each given Python object separately.
32
33   Function **asized** returns for each object an instance of class
34   **Asized** containing all the size information of the object and
35   a tuple with the referents [#refs]_.
36
37   Functions **basicsize** and **itemsize** return the *basic-*
38   respectively *itemsize* of the given object, both in bytes.  For
39   objects as ``array.array``, ``numpy.array``, ``numpy.matrix``,
40   etc. where the item size varies depending on the instance-specific
41   data type, function **itemsize** returns that item size.
42
43   Function **flatsize** returns the *flat size* of a Python object
44   in bytes defined as the *basic size* plus the *item size* times
45   the *length* of the given object.
46
47   Function **leng** returns the *length* of an object, like standard
48   function ``len`` but extended for several types.  E.g. the **leng**
49   of a multi-precision int (or long) is the number of ``digits``
50   [#digit]_.  The length of most *mutable* sequence objects includes
51   an estimate of the over-allocation and therefore, the **leng** value
52   may differ from the standard ``len`` result.  For objects like
53   ``array.array``, ``numpy.array``, ``numpy.matrix``, etc. function
54   **leng** returns the proper number of items.
55
56   Function **refs** returns (a generator for) the referents [#refs]_
57   of the given object.
58
59   Certain classes are known to be sub-classes of or to behave as
60   ``dict`` objects.  Function **adict** can be used to register
61   other class objects to be treated like ``dict``.
62
63**Public Classes** [#unsafe]_
64
65   Class **Asizer** may be used to accumulate the results of several
66   **asizeof** or **asizesof** calls.  After creating an **Asizer**
67   instance, use methods **asizeof** and **asizesof** as needed to
68   size any number of additional objects.
69
70   Call methods **exclude_refs** and/or **exclude_types** to exclude
71   references to respectively instances or types of certain objects.
72
73   Use one of the **print\\_...** methods to report the statistics.
74
75   An instance of class **Asized** is returned for each object sized
76   by the **asized** function or method.
77
78**Duplicate Objects**
79
80   Any duplicate, given objects are sized only once and the size
81   is included in the accumulated total only once.  But functions
82   **asizesof** and **asized** will return a size value respectively
83   an **Asized** instance for each given object, including duplicates.
84
85**Definitions** [#arb]_
86
87   The *length* of an objects like ``dict``, ``list``, ``set``,
88   ``str``, ``tuple``, etc. is defined as the number of items held
89   in or allocated by the object.  Held items are *references* to
90   other objects, called the *referents*.
91
92   The *size* of an object is defined as the sum of the *flat size*
93   of the object plus the sizes of any referents [#refs]_.  Referents
94   are visited recursively up to the specified detail level.  However,
95   the size of objects referenced multiple times is included only once
96   in the total *size*.
97
98   The *flat size* of an object is defined as the *basic size* of the
99   object plus the *item size* times the number of allocated *items*,
100   *references* to referents.  The *flat size* does include the size
101   for the *references* to the referents, but not the size of the
102   referents themselves.
103
104   The *flat size* returned by function *flatsize* equals the result
105   of function *asizeof* with options *code=True*, *ignored=False*,
106   *limit=0* and option *align* set to the same value.
107
108   The accurate *flat size* for an object is obtained from function
109   ``sys.getsizeof()`` where available.  Otherwise, the *length* and
110   *size* of sequence objects as ``dicts``, ``lists``, ``sets``, etc.
111   is based on an estimate for the number of allocated items.  As a
112   result, the reported *length* and *size* may differ substantially
113   from the actual *length* and *size*.
114
115   The *basic* and *item size* are obtained from the ``__basicsize__``
116   respectively ``__itemsize__`` attributes of the (type of the)
117   object.  Where necessary (e.g. sequence objects), a zero
118   ``__itemsize__`` is replaced by the size of a corresponding C type.
119
120   The overhead for Python's garbage collector (GC) is included in
121   the *basic size* of (GC managed) objects as well as the space
122   needed for ``refcounts`` (used only in certain Python builds).
123
124   Optionally, size values can be aligned to any power-of-2 multiple.
125
126**Size of (byte)code**
127
128   The *(byte)code size* of objects like classes, functions, methods,
129   modules, etc. can be included by setting option *code=True*.
130
131   Iterators are handled like sequences: iterated object(s) are sized
132   like *referents* [#refs]_, but only up to the specified level or
133   recursion *limit* (and only if function ``gc.get_referents()``
134   returns the referent object of iterators).
135
136   Generators are sized as *(byte)code* only, but the generated
137   objects are never sized.
138
139**Old- and New-style Classes**
140
141   All old- and new-style ``class``, instance and ``type`` objects are
142   handled uniformly such that (a) instance objects are distinguished
143   from class objects and (b) instances of different old-style classes
144   can be dealt with separately.
145
146   Class and type objects are represented as ``<class ....* def>``
147   respectively ``<type ... def>`` where the ``*`` indicates an old-style
148   class and the ``... def`` suffix marks the *definition object*.
149   Instances of  classes are shown as ``<class module.name*>`` without
150   the ``... def`` suffix.  The ``*`` after the name indicates an
151   instance of an old-style class.
152
153**Ignored Objects**
154
155   To avoid excessive sizes, several object types are ignored [#arb]_
156   by default, e.g. built-in functions, built-in types and classes
157   [#bi]_, function globals and module referents.  However, any
158   instances thereof and module objects will be sized when passed as
159   given objects.  Ignored object types are included unless option
160   *ignored* is set accordingly.
161
162   In addition, many ``__...__`` attributes of callable objects are
163   ignored [#arb]_, except crucial ones, e.g. class attributes ``__dict__``,
164   ``__doc__``, ``__name__`` and ``__slots__``.  For more details, see
165   the type-specific ``_..._refs()`` and ``_len_...()`` functions below.
166
167.. rubric:: Footnotes
168.. [#unsafe] The functions and classes in this module are not thread-safe.
169
170.. [#refs] The *referents* of an object are the objects referenced *by*
171     that object.  For example, the *referents* of a ``list`` are the
172     objects held in the ``list``, the *referents* of a ``dict`` are
173     the key and value objects in the ``dict``, etc.
174
175.. [#arb] These definitions and other assumptions are rather arbitrary
176     and may need corrections or adjustments.
177
178.. [#digit] See Python source file ``.../Include/longinterp.h`` for the
179     C ``typedef`` of ``digit`` used in multi-precision int (or long)
180     objects.  The C ``sizeof(digit)`` in bytes can be obtained in
181     Python from the int (or long) ``__itemsize__`` attribute.
182     Function **leng** determines the number of ``digits`` of an int
183     (or long) object.
184
185.. [#bi] ``Type``s and ``class``es are considered built-in if the
186     ``__module__`` of the type or class is listed in the private
187     ``_builtin_modules``.
188'''  # PYCHOK escape
189import sys
190if sys.version_info < (2, 6, 0):
191    raise NotImplementedError('%s requires Python 2.6 or newer' % ('asizeof',))
192
193# all imports listed explicitly to help PyChecker
194from inspect import (isbuiltin, isclass, iscode, isframe, isfunction,
195                     ismethod, ismodule, stack)
196from math import log
197from os import curdir, linesep
198from struct import calcsize  # type/class Struct only in Python 2.5+
199import types as Types
200import warnings
201import weakref as Weakref
202
203__all__ = ['adict', 'asized', 'asizeof', 'asizesof',
204           'Asized', 'Asizer',  # classes
205           'basicsize', 'flatsize', 'itemsize', 'leng', 'refs']
206__version__ = '19.03.29'
207
208# Any classes or types in modules listed in _builtin_modules are
209# considered built-in and ignored by default, as built-in functions
210_builtin_modules = (int.__module__, 'types', Exception.__module__)  # 'weakref'
211if __name__ != '__main__':  # treat this very module as built-in
212    _builtin_modules += (__name__,)
213
214# Sizes of some primitive C types
215# XXX len(pack(T, 0)) == Struct(T).size == calcsize(T)
216_sizeof_Cbyte  = calcsize('c')  # sizeof(unsigned char)
217_sizeof_Clong  = calcsize('l')  # sizeof(long)
218_sizeof_Cvoidp = calcsize('P')  # sizeof(void*)
219
220# sizeof(long) != sizeof(ssize_t) on LLP64
221if _sizeof_Clong < _sizeof_Cvoidp:  # pragma: no coverage
222    _z_P_L = 'P'
223else:
224    _z_P_L = 'L'
225
226
227def _calcsize(fmt):
228    '''Like struct.calcsize() but handling 'z' for Py_ssize_t.
229    '''
230    return calcsize(fmt.replace('z', _z_P_L))
231
232
233# Defaults for some basic sizes with 'z' for C Py_ssize_t
234_sizeof_CPyCodeObject = _calcsize('Pz10P5i0P')  # sizeof(PyCodeObject)
235_sizeof_CPyFrameObject = _calcsize('Pzz13P63i0P')  # sizeof(PyFrameObject)
236_sizeof_CPyModuleObject = _calcsize('PzP0P')  # sizeof(PyModuleObject)
237
238# Defaults for some item sizes with 'z' for C Py_ssize_t
239_sizeof_CPyDictEntry = _calcsize('z2P')  # sizeof(PyDictEntry)
240_sizeof_Csetentry = _calcsize('lP')  # sizeof(setentry)
241
242try:  # C typedef digit for multi-precision int (or long)
243    _sizeof_Cdigit = long.__itemsize__
244except NameError:  # no long in Python 3+
245    _sizeof_Cdigit = int.__itemsize__
246if _sizeof_Cdigit < 2:  # pragma: no coverage
247    raise AssertionError('sizeof(%s) bad: %d' % ('digit', _sizeof_Cdigit))
248
249try:
250    _builtins2 = range, xrange
251except NameError:  # Python 3+
252    _builtins2 = range,
253
254# Get character size for internal unicode representation in Python < 3.3
255try:  # sizeof(unicode_char)
256    u = unicode('\0')
257except NameError:  # no unicode() in Python 3+
258    u = '\0'
259u = u.encode('utf-8')
260_sizeof_Cunicode = len(u)
261del u
262
263try:  # Size of GC header, sizeof(PyGC_Head)
264    import _testcapi as t
265    _sizeof_CPyGC_Head = t.SIZEOF_PYGC_HEAD  # new in Python 2.6
266except (ImportError, AttributeError):  # sizeof(PyGC_Head)
267    # alignment should be to sizeof(long double) but there
268    # is no way to obtain that value, assume twice double
269    t = calcsize('2d') - 1
270    _sizeof_CPyGC_Head = (_calcsize('2Pz') + t) & ~t
271del t
272
273# Size of refcounts (Python debug build only)
274if hasattr(sys, 'gettotalrefcount'):  # pragma: no coverage
275    _sizeof_Crefcounts = _calcsize('2z')
276else:
277    _sizeof_Crefcounts = 0
278
279try:
280    from abc import ABCMeta
281except ImportError:
282    class ABCMeta(type):
283        pass
284
285# Some flags from .../Include/object.h
286_Py_TPFLAGS_HEAPTYPE = 1 << 9  # Py_TPFLAGS_HEAPTYPE
287_Py_TPFLAGS_HAVE_GC = 1 << 14  # Py_TPFLAGS_HAVE_GC
288
289_Type_type = type(type)  # == type and new-style class type
290
291
292# Compatibility functions for more uniform
293# behavior across Python version 2.2 thu 3+
294
295def _items(obj):  # dict only
296    '''Return iter-/generator, preferably.
297    '''
298    o = getattr(obj, 'iteritems', obj.items)
299    if _callable(o):
300        return o()
301    else:
302        return o or ()
303
304
305def _keys(obj):  # dict only
306    '''Return iter-/generator, preferably.
307    '''
308    o = getattr(obj, 'iterkeys', obj.keys)
309    if _callable(o):
310        return o()
311    else:
312        return o or ()
313
314
315def _values(obj):  # dict only
316    '''Return iter-/generator, preferably.
317    '''
318    o = getattr(obj, 'itervalues', obj.values)
319    if _callable(o):
320        return o()
321    else:
322        return o or ()
323
324
325try:  # callable() builtin
326    _callable = callable
327except NameError:  # callable() removed in Python 3+
328    def _callable(obj):
329        '''Substitute for callable().'''
330        return hasattr(obj, '__call__')
331
332# 'cell' is holding data used in closures
333c = (lambda unused: (lambda: unused))(None)
334try:
335    _cell_type = type(c.__closure__[0])
336except AttributeError:  # Python 2.5
337    _cell_type = type(c.func_closure[0])
338del c
339
340try:
341    from gc import get_objects as _getobjects  # containers only?
342except ImportError:
343    def _getobjects():
344        # modules first, globals and stack
345        # objects (may contain duplicates)
346        return tuple(_values(sys.modules)) + (
347               globals(), stack(sys.getrecursionlimit())[2:])
348
349if sys.platform == 'ios':  # Apple iOS
350    _gc_getobjects = _getobjects
351
352    def _getobjects():  # PYCHOK expected
353        # avoid Pythonista3/Python 3+ crash
354        return tuple(o for o in _gc_getobjects() if not _isNULL(o))
355
356try:  # only used to get referents of
357    # iterators, but gc.get_referents()
358    # returns () for dict...-iterators
359    from gc import get_referents as _getreferents
360except ImportError:
361    def _getreferents(unused):
362        return ()  # sorry, no refs
363
364# sys.getsizeof() new in Python 2.6
365_getsizeof = sys.getsizeof  # overridden below
366_getsizeof_excls = ()  # types not sys.getsizeof'd
367
368try:  # str intern()
369    _intern = intern
370except NameError:  # no intern() in Python 3+
371    def _intern(val):
372        return val
373
374
375# Private functions
376
377def _basicsize(t, base=0, heap=False, obj=None):
378    '''Get non-zero basicsize of type,
379       including the header sizes.
380    '''
381    s = max(getattr(t, '__basicsize__', 0), base)
382    # include gc header size
383    if t != _Type_type:
384        h = getattr(t, '__flags__', 0) & _Py_TPFLAGS_HAVE_GC
385    elif heap:  # type, allocated on heap
386        h = True
387    else:  # None has no __flags__ attr
388        h = getattr(obj, '__flags__', 0) & _Py_TPFLAGS_HEAPTYPE
389    if h:
390        s += _sizeof_CPyGC_Head
391    # include reference counters
392    return s + _sizeof_Crefcounts
393
394
395def _classof(obj, dflt=None):
396    '''Return the object's class object.
397    '''
398    return getattr(obj, '__class__', dflt)
399
400
401def _derive_typedef(typ):
402    '''Return single, existing super type typedef or None.
403    '''
404    v = [v for v in _values(_typedefs) if _issubclass(typ, v.type)]
405    if len(v) == 1:
406        return v[0]
407    return None
408
409
410def _dir2(obj, pref='', excl=(), slots=None, itor=''):
411    '''Return an attribute name, object 2-tuple for certain
412       attributes or for the ``__slots__`` attributes of the
413       given object, but not both.  Any iterator referent
414       objects are returned with the given name if the
415       latter is non-empty.
416    '''
417    if slots:  # __slots__ attrs
418        if hasattr(obj, slots):
419            # collect all inherited __slots__ attrs
420            # from list, tuple, or dict __slots__,
421            # while removing any duplicate attrs
422            s = {}
423            for c in type(obj).mro():
424                for a in getattr(c, slots, ()):
425                    if a.startswith('__'):
426                        a = '_' + c.__name__ + a
427                    if hasattr(obj, a):
428                        s.setdefault(a, getattr(obj, a))
429            # assume __slots__ tuple-like is holding the values
430            # yield slots, _Slots(s)  # _keys(s) ... REMOVED,
431            # see _Slots.__doc__ further below
432            for t in _items(s):
433                yield t  # attr name, value
434    elif itor:  # iterator referents
435        for o in obj:  # iter(obj)
436            yield itor, o
437    else:  # regular attrs
438        for a in dir(obj):
439            if a.startswith(pref) and hasattr(obj, a) and a not in excl:
440                yield a, getattr(obj, a)
441
442
443def _getsizeof_excls_add(typ):
444    '''Add another type to the tuple of types to be
445       excluded from sys.getsizeof due to errors.
446    '''
447    global _getsizeof_excls
448    if typ and typ not in _getsizeof_excls:
449        _getsizeof_excls += (typ,)
450
451
452def _infer_dict(obj):
453    '''Return True for likely dict object via duck typing.
454    '''
455    for attrs in (('items', 'keys', 'values'),  # 'update',
456                  ('iteritems', 'iterkeys', 'itervalues')):
457        attrs += '__len__', 'get', 'has_key'
458        if all(_callable(getattr(obj, a, None)) for a in attrs):
459            return True
460    return False
461
462
463def _isbuiltin2(obj):
464    '''Return True for builtins like Python 2.
465    '''
466    # range is no longer a built-in in Python 3+
467    return isbuiltin(obj) or obj in _builtins2
468
469
470def _iscell(obj):
471    '''Return True if obj is a cell as used in a closure.
472    '''
473    return isinstance(obj, _cell_type)
474
475
476def _isdictclass(obj):
477    '''Return True for known dict objects.
478    '''
479    c = _classof(obj)
480    return c and c.__name__ in _dict_classes.get(c.__module__, ())
481
482
483def _isframe(obj):
484    '''Return True for a stack frame object.
485    '''
486    try:  # safe isframe(), see pympler.muppy
487        return isframe(obj)
488    except ReferenceError:
489        return False
490
491
492def _isnamedtuple(obj):
493    '''Named tuples are identified via duck typing:
494       <http://www.gossamer-threads.com/lists/python/dev/1142178>
495    '''
496    return isinstance(obj, tuple) and hasattr(obj, '_fields')
497
498
499def _isNULL(obj):
500    '''Prevent asizeof(all=True, ...) crash.
501
502       Sizing gc.get_objects() crashes in Pythonista3 with
503       Python 3.5.1 on iOS due to 1-tuple (<Null>,) object,
504       see <http://forum.omz-software.com/user/mrjean1>.
505    '''
506    return isinstance(obj, tuple) and len(obj) == 1 \
507                                  and repr(obj) == '(<NULL>,)'
508
509
510def _issubclass(sub, sup):
511    '''Safe issubclass().
512    '''
513    if sup is not object:
514        try:
515            return issubclass(sub, sup)
516        except TypeError:
517            pass
518    return False
519
520
521def _itemsize(t, item=0):
522    '''Get non-zero itemsize of type.
523    '''
524    # replace zero value with default
525    return getattr(t, '__itemsize__', 0) or item
526
527
528def _kwdstr(**kwds):
529    '''Keyword arguments as a string.
530    '''
531    return ', '.join(sorted('%s=%r' % kv for kv in _items(kwds)))
532
533
534def _lengstr(obj):
535    '''Object length as a string.
536    '''
537    n = leng(obj)
538    if n is None:  # no len
539        r = ''
540    elif n > _len(obj):  # extended
541        r = ' leng %d!' % n
542    else:
543        r = ' leng %d' % n
544    return r
545
546
547def _moduleof(obj, dflt=''):
548    '''Return the object's module name.
549    '''
550    return getattr(obj, '__module__', dflt)
551
552
553def _nameof(obj, dflt=''):
554    '''Return the name of an object.
555    '''
556    return getattr(obj, '__name__', dflt)
557
558
559def _objs_opts_x(objs, all=None, **opts):
560    '''Return given or 'all' objects
561       and the remaining options.
562    '''
563    if objs:  # given objects
564        t = objs
565        x = False
566    elif all in (False, None):
567        t = ()
568        x = True
569    elif all is True:  # 'all' objects
570        t = _getobjects()
571        x = True
572    else:
573        raise ValueError('invalid option: %s=%r' % ('all', all))
574    return t, opts, x
575
576
577def _p100(part, total, prec=1):
578    '''Return percentage as string.
579    '''
580    r = float(total)
581    if r:
582        r = part * 100.0 / r
583        return '%.*f%%' % (prec, r)
584    return 'n/a'
585
586
587def _plural(num):
588    '''Return 's' if plural.
589    '''
590    if num == 1:
591        s = ''
592    else:
593        s = 's'
594    return s
595
596
597def _power2(n):
598    '''Find the next power of 2.
599    '''
600    p2 = 16
601    while n > p2:
602        p2 += p2
603    return p2
604
605
606def _prepr(obj, clip=0):
607    '''Prettify and clip long repr() string.
608    '''
609    return _repr(obj, clip=clip).strip('<>').replace("'", '')  # remove <''>
610
611
612def _printf(fmt, *args, **print3options):
613    '''Formatted print to sys.stdout or given stream.
614
615       *print3options* -- some keyword arguments, like Python 3+ print.
616    '''
617    if print3options:  # like Python 3+
618        f = print3options.get('file', None) or sys.stdout
619        if args:
620            f.write(fmt % args)
621        else:
622            f.write(fmt)
623        f.write(print3options.get('end', linesep))
624        if print3options.get('flush', False):
625            f.flush()
626    elif args:
627        print(fmt % args)
628    else:
629        print(fmt)
630
631
632def _refs(obj, named, *attrs, **kwds):
633    '''Return specific attribute objects of an object.
634    '''
635    if named:
636        _N = _NamedRef
637    else:
638        def _N(_, o):
639            return o
640
641    for a in attrs:  # cf. inspect.getmembers()
642        if hasattr(obj, a):
643            yield _N(a, getattr(obj, a))
644    if kwds:  # kwds are _dir2() args
645        for a, o in _dir2(obj, **kwds):
646            yield _N(a, o)
647
648
649def _repr(obj, clip=80):
650    '''Clip long repr() string.
651    '''
652    try:  # safe repr()
653        r = repr(obj).replace(linesep, '\\n')
654    except Exception:
655        r = 'N/A'
656    if len(r) > clip > 0:
657        h = (clip // 2) - 2
658        if h > 0:
659            r = r[:h] + '....' + r[-h:]
660    return r
661
662
663def _SI(size, K=1024, i='i'):
664    '''Return size as SI string.
665    '''
666    if 1 < K <= size:
667        f = float(size)
668        for si in iter('KMGPTE'):
669            f /= K
670            if f < K:
671                return ' or %.1f %s%sB' % (f, si, i)
672    return ''
673
674
675def _SI2(size, **kwds):
676    '''Return size as regular plus SI string.
677    '''
678    return str(size) + _SI(size, **kwds)
679
680
681# Type-specific referents functions
682
683def _cell_refs(obj, named):
684    try:  # handle 'empty' cells
685        o = obj.cell_contents
686        if named:
687            o = _NamedRef('cell_contents', o)
688        yield o
689    except (AttributeError, ValueError):
690        pass
691
692
693def _class_refs(obj, named):
694    '''Return specific referents of a class object.
695    '''
696    return _refs(obj, named, '__class__', '__doc__', '__mro__',
697                             '__name__', '__slots__', '__weakref__',
698                             '__dict__')  # __dict__ last
699
700
701def _co_refs(obj, named):
702    '''Return specific referents of a code object.
703    '''
704    return _refs(obj, named, pref='co_')
705
706
707def _dict_refs(obj, named):
708    '''Return key and value objects of a dict/proxy.
709    '''
710    try:
711        if named:
712            for k, v in _items(obj):
713                s = str(k)
714                yield _NamedRef('[K] ' + s, k)
715                yield _NamedRef('[V] ' + s + ': ' + _repr(v), v)
716        else:
717            for k, v in _items(obj):
718                yield k
719                yield v
720    except (KeyError, ReferenceError, TypeError) as x:
721        warnings.warn("Iterating '%s': %r" % (_classof(obj), x))
722
723
724def _enum_refs(obj, named):
725    '''Return specific referents of an enumerate object.
726    '''
727    return _refs(obj, named, '__doc__')
728
729
730def _exc_refs(obj, named):
731    '''Return specific referents of an Exception object.
732    '''
733    # .message raises DeprecationWarning in Python 2.6
734    return _refs(obj, named, 'args', 'filename', 'lineno', 'msg', 'text')  # , 'message', 'mixed'
735
736
737def _file_refs(obj, named):
738    '''Return specific referents of a file object.
739    '''
740    return _refs(obj, named, 'mode', 'name')
741
742
743def _frame_refs(obj, named):
744    '''Return specific referents of a frame object.
745    '''
746    return _refs(obj, named, pref='f_')
747
748
749def _func_refs(obj, named):
750    '''Return specific referents of a function or lambda object.
751    '''
752    return _refs(obj, named, '__doc__', '__name__', '__code__', '__closure__',
753                 pref='func_', excl=('func_globals',))
754
755
756def _gen_refs(obj, named):
757    '''Return the referent(s) of a generator (expression) object.
758    '''
759    # only some gi_frame attrs
760    f = getattr(obj, 'gi_frame', None)
761    return _refs(f, named, 'f_locals', 'f_code')
762#   do not yield any items to keep generator intact
763#   for r in _refs(f, named, 'f_locals', 'f_code'):
764#       yield r
765#   for r in obj:
766#       yield r
767
768
769def _im_refs(obj, named):
770    '''Return specific referents of a method object.
771    '''
772    return _refs(obj, named, '__doc__', '__name__', '__code__', pref='im_')
773
774
775def _inst_refs(obj, named):
776    '''Return specific referents of a class instance.
777    '''
778    return _refs(obj, named, '__dict__', '__class__', slots='__slots__')
779
780
781def _iter_refs(obj, named):
782    '''Return the referent(s) of an iterator object.
783    '''
784    r = _getreferents(obj)  # special case
785    return _refs(r, named, itor=_nameof(obj) or 'iteref')
786
787
788def _module_refs(obj, named):
789    '''Return specific referents of a module object.
790    '''
791    # ignore this very module
792    if obj.__name__ == __name__:
793        return ()
794    # module is essentially a dict
795    return _dict_refs(obj.__dict__, named)
796
797
798def _namedtuple_refs(obj, named):
799    '''Return specific referents of obj-as-sequence and slots but exclude dict.
800    '''
801    for r in _refs(obj, named, '__class__', slots='__slots__'):
802        yield r
803    for r in obj:
804        yield r
805
806
807def _prop_refs(obj, named):
808    '''Return specific referents of a property object.
809    '''
810    return _refs(obj, named, '__doc__', pref='f')
811
812
813def _seq_refs(obj, unused):  # named unused for PyChecker
814    '''Return specific referents of a frozen/set, list, tuple and xrange object.
815    '''
816    return obj  # XXX for r in obj: yield r
817
818
819def _stat_refs(obj, named):
820    '''Return referents of a os.stat object.
821    '''
822    return _refs(obj, named, pref='st_')
823
824
825def _statvfs_refs(obj, named):
826    '''Return referents of a os.statvfs object.
827    '''
828    return _refs(obj, named, pref='f_')
829
830
831def _tb_refs(obj, named):
832    '''Return specific referents of a traceback object.
833    '''
834    return _refs(obj, named, pref='tb_')
835
836
837def _type_refs(obj, named):
838    '''Return specific referents of a type object.
839    '''
840    return _refs(obj, named, '__doc__', '__mro__', '__name__',
841                             '__slots__', '__weakref__', '__dict__')
842
843
844def _weak_refs(obj, unused):  # named unused for PyChecker
845    '''Return weakly referent object.
846    '''
847    try:  # ignore 'key' of KeyedRef
848        return (obj(),)
849    except Exception:  # XXX ReferenceError
850        return ()
851
852
853_all_refs = (None, _cell_refs, _class_refs, _co_refs, _dict_refs, _enum_refs,
854                   _exc_refs, _file_refs, _frame_refs, _func_refs, _gen_refs,
855                   _im_refs, _inst_refs, _iter_refs, _module_refs, _namedtuple_refs,
856                   _prop_refs, _seq_refs, _stat_refs, _statvfs_refs, _tb_refs,
857                   _type_refs, _weak_refs)
858
859
860# Type-specific length functions
861
862def _len(obj):
863    '''Safe len().
864    '''
865    try:
866        return len(obj)
867    except TypeError:  # no len()
868        return 0
869
870
871def _len_bytearray(obj):
872    '''Bytearray size.
873    '''
874    return obj.__alloc__()
875
876
877def _len_code(obj):  # see .../Lib/test/test_sys.py
878    '''Length of code object (stack and variables only).
879    '''
880    return (obj.co_stacksize + obj.co_nlocals +
881           _len(obj.co_freevars) + _len(obj.co_cellvars) - 1)
882
883
884def _len_dict(obj):
885    '''Dict length in items (estimate).
886    '''
887    n = len(obj)  # active items
888    if n < 6:  # ma_smalltable ...
889        n = 0  # ... in basicsize
890    else:  # at least one unused
891        n = _power2(n + 1)
892    return n
893
894
895def _len_frame(obj):
896    '''Length of a frame object.
897    '''
898    c = getattr(obj, 'f_code', None)
899    if c:
900        n = _len_code(c)
901    else:
902        n = 0
903    return n
904
905
906_digit2p2 = 1 << (_sizeof_Cdigit << 3)
907_digitmax = _digit2p2 - 1  # == (2 * PyLong_MASK + 1)
908_digitlog = 1.0 / log(_digit2p2)
909
910
911def _len_int(obj):
912    '''Length of multi-precision int (aka long) in digits.
913    '''
914    if obj:
915        n, i = 1, abs(obj)
916        if i > _digitmax:
917            # no log(x[, base]) in Python 2.2
918            n += int(log(i) * _digitlog)
919    else:  # zero
920        n = 0
921    return n
922
923
924def _len_iter(obj):
925    '''Length (hint) of an iterator.
926    '''
927    n = getattr(obj, '__length_hint__', None)
928    if n:
929        n = n()
930    else:  # try len()
931        n = _len(obj)
932    return n
933
934
935def _len_list(obj):
936    '''Length of list (estimate).
937    '''
938    n = len(obj)
939    # estimate over-allocation
940    if n > 8:
941        n += 6 + (n >> 3)
942    elif n:
943        n += 4
944    return n
945
946
947def _len_module(obj):
948    '''Module length.
949    '''
950    return _len(obj.__dict__)  # _len(dir(obj))
951
952
953def _len_set(obj):
954    '''Length of frozen/set (estimate).
955    '''
956    n = len(obj)
957    if n > 8:  # assume half filled
958        n = _power2(n + n - 2)
959    elif n:  # at least 8
960        n = 8
961    return n
962
963
964def _len_slice(obj):
965    '''Slice length.
966    '''
967    try:
968        return ((obj.stop - obj.start + 1) // obj.step)
969    except (AttributeError, TypeError):
970        return 0
971
972
973# REMOVED, see _Slots.__doc__
974# def _len_slots(obj):
975#     '''Slots length.
976#     '''
977#     return len(obj) - 1
978
979
980def _len_struct(obj):
981    '''Struct length in bytes.
982    '''
983    try:
984        return obj.size
985    except AttributeError:
986        return 0
987
988
989def _len_unicode(obj):
990    '''Unicode size.
991    '''
992    return len(obj) + 1
993
994
995_all_lens = (None, _len, _len_bytearray, _len_code, _len_dict,
996                   _len_frame, _len_int, _len_iter, _len_list,
997                   _len_module, _len_set, _len_slice, _len_struct,
998                   _len_unicode)  # _len_array, _len_numpy, _len_slots
999
1000
1001# More private functions and classes
1002
1003_old_style = '*'  # marker
1004_new_style = ''   # no marker
1005
1006
1007class _Claskey(object):
1008    '''Wrapper for class objects.
1009    '''
1010    __slots__ = ('_obj', '_sty')
1011
1012    def __init__(self, obj, style):
1013        self._obj = obj  # XXX Weakref.ref(obj)
1014        self._sty = style
1015
1016    def __str__(self):
1017        r = str(self._obj)
1018        if r.endswith('>'):
1019            r = '%s%s def>' % (r[:-1], self._sty)
1020        elif self._sty is _old_style and not r.startswith('class '):
1021            r = 'class %s%s def' % (r, self._sty)
1022        else:
1023            r = '%s%s def' % (r, self._sty)
1024        return r
1025    __repr__ = __str__
1026
1027
1028# For most objects, the object type is used as the key in the
1029# _typedefs dict further below, except class and type objects
1030# and old-style instances.  Those are wrapped with separate
1031# _Claskey or _Instkey instances to be able (1) to distinguish
1032# instances of different old-style classes by class, (2) to
1033# distinguish class (and type) instances from class (and type)
1034# definitions for new-style classes and (3) provide similar
1035# results for repr() and str() of new- and old-style classes
1036# and instances.
1037
1038_claskeys = {}  # [id(obj)] = _Claskey()
1039
1040
1041def _claskey(obj, style):
1042    '''Wrap an old- or new-style class object.
1043    '''
1044    i = id(obj)
1045    k = _claskeys.get(i, None)
1046    if not k:
1047        _claskeys[i] = k = _Claskey(obj, style)
1048    return k
1049
1050
1051try:  # MCCABE 19
1052    # no Class- and InstanceType in Python 3+
1053    _Types_ClassType = Types.ClassType
1054    _Types_InstanceType = Types.InstanceType
1055
1056    class _Instkey(object):
1057        '''Wrapper for old-style class (instances).
1058        '''
1059        __slots__ = ('_obj',)
1060
1061        def __init__(self, obj):
1062            self._obj = obj  # XXX Weakref.ref(obj)
1063
1064        def __str__(self):
1065            t = _moduleof(self._obj), self._obj.__name__, _old_style
1066            return '<class %s.%s%s>' % t
1067        __repr__ = __str__
1068
1069    _instkeys = {}  # [id(obj)] = _Instkey()
1070
1071    def _instkey(obj):
1072        '''Wrap an old-style class (instance).
1073        '''
1074        i = id(obj)
1075        k = _instkeys.get(i, None)
1076        if not k:
1077            _instkeys[i] = k = _Instkey(obj)
1078        return k
1079
1080    def _keytuple(obj):
1081        '''Return class and instance keys for a class.
1082        '''
1083        t = type(obj)
1084        if t is _Types_InstanceType:
1085            t = obj.__class__
1086            return _claskey(t, _old_style), _instkey(t)
1087        elif t is _Types_ClassType:
1088            return _claskey(obj, _old_style), _instkey(obj)
1089        elif t is _Type_type:
1090            return _claskey(obj, _new_style), obj
1091        return None, None  # not a class
1092
1093    def _objkey(obj):
1094        '''Return the key for any object.
1095        '''
1096        k = type(obj)
1097        if k is _Types_InstanceType:
1098            k = _instkey(obj.__class__)
1099        elif k is _Types_ClassType:
1100            k = _claskey(obj, _old_style)
1101        elif k is _Type_type:
1102            k = _claskey(obj, _new_style)
1103        return k
1104
1105except AttributeError:  # Python 3+
1106
1107    def _keytuple(obj):  # PYCHOK expected
1108        '''Return class and instance keys for a class.
1109        '''
1110        if type(obj) is _Type_type:  # isclass(obj):
1111            return _claskey(obj, _new_style), obj
1112        return None, None  # not a class
1113
1114    def _objkey(obj):  # PYCHOK expected
1115        '''Return the key for any object.
1116        '''
1117        k = type(obj)
1118        if k is _Type_type:  # isclass(obj):
1119            k = _claskey(obj, _new_style)
1120        return k
1121
1122
1123class _NamedRef(object):
1124    '''Store referred object along
1125       with the name of the referent.
1126    '''
1127    __slots__ = ('name', 'ref')
1128
1129    def __init__(self, name, ref):
1130        self.name = name
1131        self.ref = ref
1132
1133
1134# class _Slots(tuple):
1135#     '''Wrapper class for __slots__ attribute at class definition.
1136#        The instance-specific __slots__ attributes are stored in
1137#        a "tuple-like" space inside the instance, see Luciano
1138#        Ramalho, "Fluent Python", page 274+, O'Reilly, 2016 or
1139#        at <http://Books.Google.com/books>, then search for
1140#        "Fluent Python" "Space Savings with the __slots__".
1141#     '''
1142#     pass
1143
1144
1145# Kinds of _Typedefs
1146_i = _intern
1147_all_kinds = (_kind_static, _kind_dynamic, _kind_derived, _kind_ignored, _kind_inferred) = (
1148                _i('static'), _i('dynamic'), _i('derived'), _i('ignored'), _i('inferred'))
1149del _i
1150
1151_Not_vari = ''  # non-variable item size
1152
1153
1154class _Typedef(object):
1155    '''Type definition class.
1156    '''
1157    __slots__ = {
1158        'base': 0,     # basic size in bytes
1159        'item': 0,     # item size in bytes
1160        'leng': None,  # or _len_...() function
1161        'refs': None,  # or _..._refs() function
1162        'both': None,  # both data and code if True, code only if False
1163        'kind': None,  # _kind_... value
1164        'type': None,  # original type
1165        'vari': None}  # item size attr name or _Not_vari
1166
1167    def __init__(self, **kwds):
1168        self.reset(**kwds)
1169
1170    def __lt__(self, unused):  # for Python 3+
1171        return True
1172
1173    def __repr__(self):
1174        return repr(self.args())
1175
1176    def __str__(self):
1177        t = [str(self.base), str(self.item)]
1178        for f in (self.leng, self.refs):
1179            if f:
1180                t.append(f.__name__)
1181            else:
1182                t.append('n/a')
1183        if not self.both:
1184            t.append('(code only)')
1185        return ', '.join(t)
1186
1187    def args(self):  # as args tuple
1188        '''Return all attributes as arguments tuple.
1189        '''
1190        return (self.base, self.item, self.leng, self.refs,
1191                self.both, self.kind, self.type)
1192
1193    def dup(self, other=None, **kwds):
1194        '''Duplicate attributes of dict or other typedef.
1195        '''
1196        if other is None:
1197            d = _dict_typedef.kwds()
1198        else:
1199            d = other.kwds()
1200        d.update(kwds)
1201        self.reset(**d)
1202
1203    def flat(self, obj, mask=0):
1204        '''Return the aligned flat size.
1205        '''
1206        s = self.base
1207        if self.leng and self.item > 0:  # include items
1208            s += self.leng(obj) * self.item
1209        # workaround sys.getsizeof (and numpy?) bug ... some
1210        # types are incorrectly sized in some Python versions
1211        # (note, isinstance(obj, ()) == False)
1212        if not isinstance(obj, _getsizeof_excls):
1213            s = _getsizeof(obj, s)
1214        if mask:  # align
1215            s = (s + mask) & ~mask
1216        return s
1217
1218    def format(self):
1219        '''Return format dict.
1220        '''
1221        i = self.item
1222        if self.vari:
1223            i = 'var'
1224        c = n = ''
1225        if not self.both:
1226            c = ' (code only)'
1227        if self.leng:
1228            n = ' (%s)' % _nameof(self.leng)
1229        return dict(base=self.base, item=i, leng=n, code=c,
1230                    kind=self.kind)
1231
1232    def kwds(self):
1233        '''Return all attributes as keywords dict.
1234        '''
1235        return dict(base=self.base, both=self.both,
1236                    item=self.item, kind=self.kind,
1237                    leng=self.leng, refs=self.refs,
1238                    type=self.type, vari=self.vari)
1239
1240    def save(self, t, base=0, heap=False):
1241        '''Save this typedef plus its class typedef.
1242        '''
1243        c, k = _keytuple(t)
1244        if k and k not in _typedefs:  # instance key
1245            _typedefs[k] = self
1246            if c and c not in _typedefs:  # class key
1247                if t.__module__ in _builtin_modules:
1248                    k = _kind_ignored  # default
1249                else:
1250                    k = self.kind
1251                _typedefs[c] = _Typedef(base=_basicsize(type(t), base=base, heap=heap),
1252                                        refs=_type_refs,
1253                                        both=False, kind=k, type=t)
1254        elif t not in _typedefs:
1255            if not _isbuiltin2(t):  # array, range, xrange in Python 2.x
1256                s = ' '.join((self.vari, _moduleof(t), _nameof(t)))
1257                s = '%r %s %s' % ((c, k), self.both, s.strip())
1258                raise KeyError('asizeof typedef %r bad: %s' % (self, s))
1259
1260            _typedefs[t] = _Typedef(base=_basicsize(t, base=base),
1261                                    both=False, kind=_kind_ignored, type=t)
1262
1263    def set(self, safe_len=False, **kwds):
1264        '''Set one or more attributes.
1265        '''
1266        if kwds:  # double check
1267            d = self.kwds()
1268            d.update(kwds)
1269            self.reset(**d)
1270        if safe_len and self.item:
1271            self.leng = _len
1272
1273    def reset(self, base=0, item=0, leng=None, refs=None,
1274                    both=True, kind=None, type=None, vari=_Not_vari):
1275        '''Reset all specified attributes.
1276        '''
1277        if base < 0:
1278            raise ValueError('invalid option: %s=%r' % ('base', base))
1279        else:
1280            self.base = base
1281        if item < 0:
1282            raise ValueError('invalid option: %s=%r' % ('item', item))
1283        else:
1284            self.item = item
1285        if leng in _all_lens:  # XXX or _callable(leng)
1286            self.leng = leng
1287        else:
1288            raise ValueError('invalid option: %s=%r' % ('leng', leng))
1289        if refs in _all_refs:  # XXX or _callable(refs)
1290            self.refs = refs
1291        else:
1292            raise ValueError('invalid option: %s=%r' % ('refs', refs))
1293        if both in (False, True):
1294            self.both = both
1295        else:
1296            raise ValueError('invalid option: %s=%r' % ('both', both))
1297        if kind in _all_kinds:
1298            self.kind = kind
1299        else:
1300            raise ValueError('invalid option: %s=%r' % ('kind', kind))
1301        self.type = type
1302        self.vari = vari or _Not_vari
1303        if str(self.vari) != self.vari:
1304            raise ValueError('invalid option: %s=%r' % ('vari', vari))
1305
1306
1307_typedefs = {}  # [key] = _Typedef()
1308
1309
1310def _typedef_both(t, base=0, item=0, leng=None, refs=None,
1311                     kind=_kind_static, heap=False, vari=_Not_vari):
1312    '''Add new typedef for both data and code.
1313    '''
1314    v = _Typedef(base=_basicsize(t, base=base), item=_itemsize(t, item),
1315                 refs=refs, leng=leng,
1316                 both=True, kind=kind, type=t, vari=vari)
1317    v.save(t, base=base, heap=heap)
1318    return v  # for _dict_typedef
1319
1320
1321def _typedef_code(t, base=0, refs=None, kind=_kind_static, heap=False):
1322    '''Add new typedef for code only.
1323    '''
1324    v = _Typedef(base=_basicsize(t, base=base),
1325                 refs=refs,
1326                 both=False, kind=kind, type=t)
1327    v.save(t, base=base, heap=heap)
1328    return v  # for _dict_typedef
1329
1330
1331# Static typedefs for data and code types
1332_typedef_both(complex)
1333_typedef_both(float)
1334_typedef_both(list, refs=_seq_refs, leng=_len_list, item=_sizeof_Cvoidp)  # sizeof(PyObject*)
1335_typedef_both(tuple, refs=_seq_refs, leng=_len, item=_sizeof_Cvoidp)  # sizeof(PyObject*)
1336_typedef_both(property, refs=_prop_refs)
1337_typedef_both(type(Ellipsis))
1338_typedef_both(type(None))
1339
1340# _Slots are "tuple-like", REMOVED see _Slots.__doc__
1341# _typedef_both(_Slots, item=_sizeof_Cvoidp,
1342#               leng=_len_slots,  # length less one
1343#               refs=None,  # but no referents
1344#               heap=True)  # plus head
1345
1346# dict, dictproxy, dict_proxy and other dict-like types
1347_dict_typedef = _typedef_both(dict, item=_sizeof_CPyDictEntry, leng=_len_dict, refs=_dict_refs)
1348try:  # <type dictproxy> only in Python 2.x
1349    _typedef_both(Types.DictProxyType, item=_sizeof_CPyDictEntry, leng=_len_dict, refs=_dict_refs)
1350except AttributeError:  # XXX any class __dict__ is <type dict_proxy> in Python 3+?
1351    _typedef_both(type(_Typedef.__dict__), item=_sizeof_CPyDictEntry, leng=_len_dict, refs=_dict_refs)
1352# other dict-like classes and types may be derived or inferred,
1353# provided the module and class name is listed here (see functions
1354# adict, _isdictclass and _infer_dict for further details)
1355_dict_classes = {'UserDict': ('IterableUserDict', 'UserDict'),
1356                 'weakref': ('WeakKeyDictionary', 'WeakValueDictionary')}
1357try:  # <type module> is essentially a dict
1358    _typedef_both(Types.ModuleType, base=_dict_typedef.base,
1359                  item=_dict_typedef.item + _sizeof_CPyModuleObject,
1360                  leng=_len_module, refs=_module_refs)
1361except AttributeError:  # missing
1362    pass
1363
1364# Newer or obsolete types
1365try:
1366    from array import array  # array type
1367
1368    def _array_kwds(obj):
1369        if hasattr(obj, 'itemsize'):
1370            v = 'itemsize'
1371        else:
1372            v = _Not_vari
1373        # since item size varies by the array data type, set
1374        # itemsize to 1 byte and use _len_array in bytes; note,
1375        # function itemsize returns the actual size in bytes
1376        # and function leng returns the length in number of items
1377        return dict(leng=_len_array, item=_sizeof_Cbyte, vari=v)
1378
1379    def _len_array(obj):
1380        '''Array length (in bytes!).
1381        '''
1382        return len(obj) * obj.itemsize
1383
1384    _all_lens += (_len_array,)
1385
1386    _typedef_both(array, **_array_kwds(array('d', [])))
1387
1388    v = sys.version_info
1389    _array_excl = (v[0] == 2 and v < (2, 7, 4)) or \
1390                  (v[0] == 3 and v < (3, 2, 4))
1391    if _array_excl:  # see function _typedef below
1392        _getsizeof_excls_add(array)
1393
1394    del v
1395except ImportError:  # missing
1396    _array_excl = array = None  # see function _typedef below
1397
1398try:  # bool has non-zero __itemsize__ in 3.0
1399    _typedef_both(bool)
1400except NameError:  # missing
1401    pass
1402
1403try:  # ignore basestring
1404    _typedef_both(basestring, leng=None)
1405except NameError:  # missing
1406    pass
1407
1408try:
1409    if isbuiltin(buffer):  # Python 2.2
1410        _typedef_both(type(buffer('')), item=_sizeof_Cbyte, leng=_len)  # XXX len in bytes?
1411    else:
1412        _typedef_both(buffer, item=_sizeof_Cbyte, leng=_len)  # XXX len in bytes?
1413except NameError:  # missing
1414    pass
1415
1416try:
1417    _typedef_both(bytearray, item=_sizeof_Cbyte, leng=_len_bytearray)
1418except NameError:  # bytearray new in 2.6, 3.0
1419    pass
1420try:
1421    if type(bytes) is not type(str):  # bytes is str in 2.6, bytes new in 2.6, 3.0
1422        _typedef_both(bytes, item=_sizeof_Cbyte, leng=_len)  # bytes new in 2.6, 3.0
1423except NameError:  # missing
1424    pass
1425# try:  # XXX like bytes
1426#     _typedef_both(str8, item=_sizeof_Cbyte, leng=_len)  # str8 new in 2.6, 3.0
1427# except NameError:  # missing
1428#     pass
1429
1430try:
1431    _typedef_both(enumerate, refs=_enum_refs)
1432except NameError:  # missing
1433    pass
1434
1435try:  # Exception is type in Python 3+
1436    _typedef_both(Exception, refs=_exc_refs)
1437except Exception:  # missing
1438    pass
1439
1440try:
1441    _typedef_both(file, refs=_file_refs)
1442except NameError:  # missing
1443    pass
1444
1445try:
1446    _typedef_both(frozenset, item=_sizeof_Csetentry, leng=_len_set, refs=_seq_refs)
1447except NameError:  # missing
1448    pass
1449try:
1450    _typedef_both(set, item=_sizeof_Csetentry, leng=_len_set, refs=_seq_refs)
1451except NameError:  # missing
1452    pass
1453
1454try:  # not callable()
1455    _typedef_both(Types.GetSetDescriptorType)
1456except AttributeError:  # missing
1457    pass
1458
1459try:  # if long exists, it is multi-precision ...
1460    _typedef_both(long, item=_sizeof_Cdigit, leng=_len_int)
1461    _typedef_both(int)  # ... and int is fixed size
1462except NameError:  # no long, only multi-precision int in Python 3+
1463    _typedef_both(int, item=_sizeof_Cdigit, leng=_len_int)
1464
1465try:  # not callable()
1466    _typedef_both(Types.MemberDescriptorType)
1467except AttributeError:  # missing
1468    pass
1469
1470try:
1471    _typedef_both(type(NotImplemented))  # == Types.NotImplementedType
1472except NameError:  # missing
1473    pass
1474
1475try:  # MCCABE 14
1476    import numpy  # NumPy array, matrix, etc.
1477
1478    def _isnumpy(obj):
1479        '''Return True for a NumPy arange, array, matrix, etc. instance.
1480        '''
1481        try:
1482            return isinstance(obj, _numpy_types) or (hasattr(obj, 'nbytes') and
1483                                          _moduleof(_classof(obj)).startswith('numpy'))
1484        except (AttributeError, OSError, ValueError):  # on iOS/Pythonista
1485            return False
1486
1487    def _len_numpy(obj):
1488        '''NumPy array, matrix, etc. length (in bytes!).
1489        '''
1490        return obj.nbytes  # == obj.size * obj.itemsize
1491
1492    def _numpy_kwds(obj):
1493        b = _getsizeof(obj, 96) - obj.nbytes  # XXX 96..144 typical?
1494        # since item size depends on the nympy data type, set
1495        # itemsize to 1 byte and use _len_numpy in bytes; note,
1496        # function itemsize returns the actual size in bytes,
1497        # function alen returns the length in number of items
1498        return dict(base=b, item=_sizeof_Cbyte,  # not obj.itemsize
1499                            leng=_len_numpy,
1500                            vari='itemsize')
1501
1502    _all_lens += (_len_numpy,)
1503
1504    _numpy_types = ()
1505    for d in (numpy.array(range(0)), numpy.arange(0)):
1506        t = type(d)
1507        if t not in _numpy_types:
1508            _numpy_types += (t,)
1509            if _isnumpy(d):  # double check
1510                _typedef_both(t, **_numpy_kwds(d))
1511            else:
1512                raise AssertionError('not %s: %r' % ('numpy', d))
1513
1514    # sizing numpy 1.13 arrays works fine, but 1.8 and older
1515    # appears to suffer from sys.getsizeof() bug like array
1516    v = tuple(map(int, numpy.__version__.split('.')[:2]))
1517    _numpy_excl = v < (1, 9)
1518    if _numpy_excl:  # see function _typedef below
1519        for t in _numpy_types:
1520            _getsizeof_excls_add(t)
1521
1522    del d, t, v
1523except ImportError:  # no NumPy
1524    _numpy_excl = numpy = None  # see function _typedef below
1525
1526    def _isnumpy(unused):  # PYCHOK expected
1527        '''Not applicable, no NumPy.
1528        '''
1529        return False
1530
1531try:
1532    _typedef_both(range)
1533except NameError:  # missing
1534    pass
1535try:
1536    _typedef_both(xrange)
1537except NameError:  # missing
1538    pass
1539
1540try:
1541    _typedef_both(reversed, refs=_enum_refs)
1542except NameError:  # missing
1543    pass
1544
1545try:
1546    _typedef_both(slice, item=_sizeof_Cvoidp, leng=_len_slice)  # XXX worst-case itemsize?
1547except NameError:  # missing
1548    pass
1549
1550try:
1551    from os import stat
1552    _typedef_both(type(stat(curdir)), refs=_stat_refs)  # stat_result
1553except ImportError:  # missing
1554    pass
1555
1556try:
1557    from os import statvfs
1558    _typedef_both(type(statvfs(curdir)), refs=_statvfs_refs,  # statvfs_result
1559                  item=_sizeof_Cvoidp, leng=_len)
1560except ImportError:  # missing
1561    pass
1562
1563try:
1564    from struct import Struct  # only in Python 2.5 and 3.0
1565    _typedef_both(Struct, item=_sizeof_Cbyte, leng=_len_struct)  # len in bytes
1566except ImportError:  # missing
1567    pass
1568
1569try:
1570    _typedef_both(Types.TracebackType, refs=_tb_refs)
1571except AttributeError:  # missing
1572    pass
1573
1574try:
1575    _typedef_both(unicode, leng=_len_unicode, item=_sizeof_Cunicode)
1576    _typedef_both(str, leng=_len, item=_sizeof_Cbyte)  # 1-byte char
1577except NameError:  # str is unicode
1578    _typedef_both(str, leng=_len_unicode, item=_sizeof_Cunicode)
1579
1580try:  # <type 'KeyedRef'>
1581    _typedef_both(Weakref.KeyedRef, refs=_weak_refs, heap=True)  # plus head
1582except AttributeError:  # missing
1583    pass
1584
1585try:  # <type 'weakproxy'>
1586    _typedef_both(Weakref.ProxyType)
1587except AttributeError:  # missing
1588    pass
1589
1590try:  # <type 'weakref'>
1591    _typedef_both(Weakref.ReferenceType, refs=_weak_refs)
1592except AttributeError:  # missing
1593    pass
1594
1595# some other, callable types
1596_typedef_code(object, kind=_kind_ignored)
1597_typedef_code(super, kind=_kind_ignored)
1598_typedef_code(_Type_type, kind=_kind_ignored)
1599
1600try:
1601    _typedef_code(classmethod, refs=_im_refs)
1602except NameError:
1603    pass
1604try:
1605    _typedef_code(staticmethod, refs=_im_refs)
1606except NameError:
1607    pass
1608try:
1609    _typedef_code(Types.MethodType, refs=_im_refs)
1610except NameError:
1611    pass
1612
1613try:  # generator (expression), no itemsize, no len(), not callable()
1614    _typedef_both(Types.GeneratorType, refs=_gen_refs)
1615except AttributeError:  # missing
1616    pass
1617
1618try:  # <type 'weakcallableproxy'>
1619    _typedef_code(Weakref.CallableProxyType, refs=_weak_refs)
1620except AttributeError:  # missing
1621    pass
1622
1623# any type-specific iterators
1624s = [_items({}), _keys({}), _values({})]
1625try:  # reversed list and tuples iterators
1626    s.extend([reversed([]), reversed(())])
1627except NameError:  # missing
1628    pass
1629
1630try:  # range iterator
1631    s.append(xrange(1))
1632except NameError:  # missing
1633    pass
1634
1635try:  # callable-iterator
1636    from re import finditer
1637    s.append(finditer('', ''))
1638except ImportError:  # missing
1639    pass
1640
1641for t in _values(_typedefs):
1642    if t.type and t.leng:
1643        try:  # create an (empty) instance
1644            s.append(t.type())
1645        except TypeError:
1646            pass
1647for t in s:
1648    try:
1649        i = iter(t)
1650        _typedef_both(type(i), leng=_len_iter, refs=_iter_refs, item=0)  # no itemsize!
1651    except (KeyError, TypeError):  # ignore non-iterables, duplicates, etc.
1652        pass
1653del i, s, t
1654
1655
1656def _typedef(obj, derive=False, frames=False, infer=False):  # MCCABE 25
1657    '''Create a new typedef for an object.
1658    '''
1659    t = type(obj)
1660    v = _Typedef(base=_basicsize(t, obj=obj),
1661                 kind=_kind_dynamic, type=t)
1662#   _printf('new %r %r/%r %s', t, _basicsize(t), _itemsize(t), _repr(dir(obj)))
1663    if ismodule(obj):  # handle module like dict
1664        v.dup(item=_dict_typedef.item + _sizeof_CPyModuleObject,
1665              leng=_len_module,
1666              refs=_module_refs)
1667    elif _isframe(obj):
1668        v.set(base=_basicsize(t, base=_sizeof_CPyFrameObject, obj=obj),
1669              item=_itemsize(t),
1670              leng=_len_frame,
1671              refs=_frame_refs)
1672        if not frames:  # ignore frames
1673            v.set(kind=_kind_ignored)
1674    elif iscode(obj):
1675        v.set(base=_basicsize(t, base=_sizeof_CPyCodeObject, obj=obj),
1676              item=_sizeof_Cvoidp,
1677              leng=_len_code,
1678              refs=_co_refs,
1679              both=False)  # code only
1680    elif _callable(obj):
1681        if isclass(obj):  # class or type
1682            v.set(refs=_class_refs,
1683                  both=False)  # code only
1684            if _moduleof(obj) in _builtin_modules:
1685                v.set(kind=_kind_ignored)
1686        elif isbuiltin(obj):  # function or method
1687            v.set(both=False,  # code only
1688                  kind=_kind_ignored)
1689        elif isfunction(obj):
1690            v.set(refs=_func_refs,
1691                  both=False)  # code only
1692        elif ismethod(obj):
1693            v.set(refs=_im_refs,
1694                  both=False)  # code only
1695        elif isclass(t):  # callable instance, e.g. SCons,
1696            # handle like any other instance further below
1697            v.set(item=_itemsize(t), safe_len=True,
1698                  refs=_inst_refs)  # not code only!
1699        else:
1700            v.set(both=False)  # code only
1701    elif _issubclass(t, dict):
1702        v.dup(kind=_kind_derived)
1703    elif _isdictclass(obj) or (infer and _infer_dict(obj)):
1704        v.dup(kind=_kind_inferred)
1705    elif _iscell(obj):
1706        v.set(item=_itemsize(t), refs=_cell_refs)
1707    elif _isnamedtuple(obj):
1708        v.set(refs=_namedtuple_refs)
1709    elif numpy and _isnumpy(obj):  # NumPy data
1710        v.set(**_numpy_kwds(obj))
1711        if _numpy_excl:
1712            _getsizeof_excls_add(t)
1713    elif array and isinstance(obj, array):
1714        v.set(**_array_kwds(obj))
1715        if _array_excl:
1716            _getsizeof_excls_add(t)
1717    elif _moduleof(obj) in _builtin_modules:
1718        v.set(kind=_kind_ignored)
1719    else:  # assume an instance of some class
1720        if derive:
1721            p = _derive_typedef(t)
1722            if p:  # duplicate parent
1723                v.dup(other=p, kind=_kind_derived)
1724                return v
1725        if _issubclass(t, Exception):
1726            v.set(item=_itemsize(t), safe_len=True,
1727                  refs=_exc_refs,
1728                  kind=_kind_derived)
1729        elif isinstance(obj, Exception):
1730            v.set(item=_itemsize(t), safe_len=True,
1731                  refs=_exc_refs)
1732        else:
1733            v.set(item=_itemsize(t), safe_len=True,
1734                  refs=_inst_refs)
1735    return v
1736
1737
1738class _Prof(object):
1739    '''Internal type profile class.
1740    '''
1741    high   = 0      # largest size
1742    number = 0      # number of (unique) objects
1743    objref = None   # largest obj (weakref)
1744    total  = 0      # total size
1745    weak   = False  # objref is weakref(obj)
1746
1747    def __cmp__(self, other):
1748        if self.total < other.total:
1749            return -1
1750        elif self.total > other.total:
1751            return +1
1752        elif self.number < other.number:
1753            return -1
1754        elif self.number > other.number:
1755            return +1
1756        return 0
1757
1758    def __lt__(self, other):  # for Python 3+
1759        return self.__cmp__(other) < 0
1760
1761    def format(self, clip=0, grand=None):
1762        '''Return format dict.
1763        '''
1764        if self.number > 1:  # avg., plural
1765            a, p = int(self.total / self.number), 's'
1766        else:
1767            a, p = self.total, ''
1768        o = self.objref
1769        if self.weak:
1770            o = o()
1771        t = _SI2(self.total)
1772        if grand:
1773            t += ' (%s)' % _p100(self.total, grand, prec=0)
1774        return dict(avg=_SI2(a), high=_SI2(self.high),
1775                    lengstr=_lengstr(o), obj=_repr(o, clip=clip),
1776                    plural=p, total=t)
1777
1778    def update(self, obj, size):
1779        '''Update this profile.
1780        '''
1781        self.number += 1
1782        self.total += size
1783        if self.high < size:  # largest
1784            self.high = size
1785            try:  # prefer using weak ref
1786                self.objref, self.weak = Weakref.ref(obj), True
1787            except TypeError:
1788                self.objref, self.weak = obj, False
1789
1790
1791class _Rank(object):
1792    '''Internal largest object class.
1793    '''
1794    __slots__ = {
1795        'deep':   0,      # recursion depth
1796        'id':     0,      # obj id
1797        'key':    None,   # Typedef
1798        'objref': None,   # obj or Weakref.ref(obj)
1799        'pid':    0,      # parent obj id
1800        'size':   0,      # size in bytes
1801        'weak':   False}  # objref is Weakref.ref
1802
1803    def __init__(self, key, obj, size, deep, pid):
1804        self.deep = deep
1805        self.id = id(obj)
1806        self.key = key
1807        try:  # prefer using weak ref
1808            self.objref, self.weak = Weakref.ref(obj), True
1809        except TypeError:
1810            self.objref, self.weak = obj, False
1811        self.pid = pid
1812        self.size = size
1813
1814    def format(self, clip=0, id2x={}):
1815        '''Return string.
1816        '''
1817        o = self.objref
1818        if self.weak:
1819            o = o()
1820        if self.deep > 0:
1821            d = ' (at %s)' % (self.deep,)
1822        else:
1823            d = ''
1824        if self.pid:
1825            p = ', pix %s' % (id2x.get(self.pid, '?'),)
1826        else:
1827            p = ''
1828        return '%s: %s%s, ix %d%s%s' % (_prepr(self.key, clip=clip),
1829               _repr(o, clip=clip), _lengstr(o), id2x[self.id], d, p)
1830
1831
1832class _Seen(dict):
1833    '''Internal obj visits counter.
1834    '''
1835    def again(self, key):
1836        s = self[key] + 1
1837        if s > 0:
1838            self[key] = s
1839
1840
1841# Public classes
1842
1843class Asized(object):
1844    '''Stores the results of an **asized** object in the following
1845       4 attributes:
1846
1847        *size* -- total size of the object (including referents)
1848
1849        *flat* -- flat size of the object (in bytes)
1850
1851        *name* -- name or ``repr`` of the object
1852
1853        *refs* -- tuple containing an **Asized** instance for each referent
1854    '''
1855    __slots__ = ('flat', 'name', 'refs', 'size')
1856
1857    def __init__(self, size, flat, refs=(), name=None):
1858        self.size = size  # total size
1859        self.flat = flat  # flat size
1860        self.name = name  # name, repr or None
1861        self.refs = tuple(refs)
1862
1863    def __str__(self):
1864        return 'size %r, flat %r, refs[%d], name %r' % (
1865            self.size, self.flat, len(self.refs), self.name)
1866
1867    def format(self, format='%(name)s size=%(size)d flat=%(flat)d',
1868                     depth=-1, order_by='size', indent=''):
1869        '''Format the size information of the object and of all
1870           sized referents as a string.
1871
1872            *format* -- Specifies the format per instance (with 'name',
1873                        'size' and 'flat' as interpolation parameters)
1874
1875            *depth* -- Recursion level up to which the referents are
1876                       printed (use -1 for unlimited)
1877
1878            *order_by* -- Control sort order of referents, valid choices
1879                          are 'name', 'size' and 'flat'
1880
1881            *indent* -- Optional indentation (default '')
1882        '''
1883        t = indent + (format % dict(size=self.size, flat=self.flat,
1884                                    name=self.name))
1885        if depth and self.refs:
1886            rs = sorted(self.refs, key=lambda x: getattr(x, order_by),
1887                                   reverse=order_by in ('size', 'flat'))
1888            rs = [r.format(format=format, depth=depth-1, order_by=order_by,
1889                           indent=indent+'    ') for r in rs]
1890            t = '\n'.join([t] + rs)
1891        return t
1892
1893    def get(self, name, dflt=None):
1894        '''Return the named referent (or *dflt* if not found).
1895        '''
1896        for ref in self.refs:
1897            if name == ref.name:
1898                return ref
1899        return dflt
1900
1901
1902class Asizer(object):
1903    '''Sizer state and options to accumulate sizes.
1904    '''
1905    _above_  = 1024   # rank only objs of 1K+ size
1906    _align_  = 8
1907    _clip_   = 80
1908    _code_   = False
1909    _cutoff_ = 0  # in percent
1910    _derive_ = False
1911    _detail_ = 0  # for Asized only
1912    _frames_ = False
1913    _infer_  = False
1914    _limit_  = 100
1915    _stats_  = 0
1916
1917    _depth   = 0  # deepest recursion
1918    _excl_d  = None  # {}
1919    _ign_d   = _kind_ignored
1920    _incl    = ''  # or ' (incl. code)'
1921    _mask    = 7   # see _align_
1922    _missed  = 0   # due to errors
1923    _profile = False  # no profiling
1924    _profs   = None   # {}
1925    _ranked  = 0
1926    _ranks   = []     # sorted by decreasing size
1927    _seen    = None   # {}
1928    _stream  = None   # I/O stream for printing
1929    _total   = 0      # total size
1930
1931    def __init__(self, **opts):
1932        '''New **Asizer** accumulator.
1933
1934           See this module documentation for more details.
1935           See method **reset** for all available options and defaults.
1936        '''
1937        self._excl_d = {}
1938        self.reset(**opts)
1939
1940    def _c100(self, stats):
1941        '''Cutoff as percentage (for backward compatibility)
1942        '''
1943        s = int(stats)
1944        c = int((stats - s) * 100.0 + 0.5) or self.cutoff
1945        return s, c
1946
1947    def _clear(self):
1948        '''Clear state.
1949        '''
1950        self._depth = 0   # recursion depth reached
1951        self._incl = ''  # or ' (incl. code)'
1952        self._missed = 0   # due to errors
1953        self._profile = False
1954        self._profs = {}
1955        self._ranked = 0
1956        self._ranks = []
1957        self._seen = _Seen()
1958        self._total = 0   # total size
1959        for k in _keys(self._excl_d):
1960            self._excl_d[k] = 0
1961        # don't size, profile or rank private, possibly large objs
1962        m = sys.modules[__name__]
1963        self.exclude_objs(self, self._excl_d, self._profs, self._ranks,
1964                                self._seen, m, m.__dict__, m.__doc__,
1965                               _typedefs)
1966
1967    def _nameof(self, obj):
1968        '''Return the object's name.
1969        '''
1970        return _nameof(obj, '') or self._repr(obj)
1971
1972    def _prepr(self, obj):
1973        '''Like **prepr()**.
1974        '''
1975        return _prepr(obj, clip=self._clip_)
1976
1977    def _printf(self, fmt, *args, **print3options):
1978        '''Print to sys.stdout or the configured stream if any is
1979           specified and if the file keyword argument is not already
1980           set in the **print3options** for this specific call.
1981        '''
1982        if self._stream and not print3options.get('file', None):
1983            if args:
1984                fmt = fmt % args
1985            _printf(fmt, file=self._stream, **print3options)
1986        else:
1987            _printf(fmt, *args, **print3options)
1988
1989    def _prof(self, key):
1990        '''Get _Prof object.
1991        '''
1992        p = self._profs.get(key, None)
1993        if not p:
1994            self._profs[key] = p = _Prof()
1995            self.exclude_objs(p)  # XXX superfluous?
1996        return p
1997
1998    def _rank(self, key, obj, size, deep, pid):
1999        '''Rank 100 largest objects by size.
2000        '''
2001        rs = self._ranks
2002        # bisect, see <http://GitHub.com/python/cpython/blob/master/Lib/bisect.py>
2003        i, j = 0, len(rs)
2004        while i < j:
2005            m = (i + j) // 2
2006            if size < rs[m].size:
2007                i = m + 1
2008            else:
2009                j = m
2010        if i < 100:
2011            r = _Rank(key, obj, size, deep, pid)
2012            rs.insert(i, r)
2013            self.exclude_objs(r)  # XXX superfluous?
2014            while len(rs) > 100:
2015                rs.pop()
2016            # self._ranks[:] = rs[:100]
2017        self._ranked += 1
2018
2019    def _repr(self, obj):
2020        '''Like ``repr()``.
2021        '''
2022        return _repr(obj, clip=self._clip_)
2023
2024    def _sizer(self, obj, pid, deep, sized):  # MCCABE 19
2025        '''Size an object, recursively.
2026        '''
2027        s, f, i = 0, 0, id(obj)
2028        if i not in self._seen:
2029            self._seen[i] = 1
2030        elif deep or self._seen[i]:
2031            # skip obj if seen before
2032            # or if ref of a given obj
2033            self._seen.again(i)
2034            if sized:
2035                s = sized(s, f, name=self._nameof(obj))
2036                self.exclude_objs(s)
2037            return s  # zero
2038        else:  # deep == seen[i] == 0
2039            self._seen.again(i)
2040        try:
2041            k, rs = _objkey(obj), []
2042            if k in self._excl_d:
2043                self._excl_d[k] += 1
2044            else:
2045                v = _typedefs.get(k, None)
2046                if not v:  # new typedef
2047                    _typedefs[k] = v = _typedef(obj, derive=self._derive_,
2048                                                     frames=self._frames_,
2049                                                      infer=self._infer_)
2050                if (v.both or self._code_) and v.kind is not self._ign_d:
2051                    s = f = v.flat(obj, self._mask)  # flat size
2052                    if self._profile:
2053                        # profile based on *flat* size
2054                        self._prof(k).update(obj, s)
2055                    # recurse, but not for nested modules
2056                    if v.refs and deep < self._limit_ \
2057                              and not (deep and ismodule(obj)):
2058                        # add sizes of referents
2059                        z, d = self._sizer, deep + 1
2060                        if sized and deep < self._detail_:
2061                            # use named referents
2062                            self.exclude_objs(rs)
2063                            for o in v.refs(obj, True):
2064                                if isinstance(o, _NamedRef):
2065                                    r = z(o.ref, i, d, sized)
2066                                    r.name = o.name
2067                                else:
2068                                    r = z(o, i, d, sized)
2069                                    r.name = self._nameof(o)
2070                                rs.append(r)
2071                                s += r.size
2072                        else:  # just size and accumulate
2073                            for o in v.refs(obj, False):
2074                                s += z(o, i, d, None)
2075                        # deepest recursion reached
2076                        if self._depth < d:
2077                            self._depth = d
2078                if self._stats_ and s > self._above_ > 0:
2079                    # rank based on *total* size
2080                    self._rank(k, obj, s, deep, pid)
2081        except RuntimeError:  # XXX RecursionLimitExceeded:
2082            self._missed += 1
2083        if not deep:
2084            self._total += s  # accumulate
2085        if sized:
2086            s = sized(s, f, name=self._nameof(obj), refs=rs)
2087            self.exclude_objs(s)
2088        return s
2089
2090    def _sizes(self, objs, sized=None):
2091        '''Return the size or an **Asized** instance for each
2092           given object plus the total size.  The total includes
2093           the size of duplicates only once.
2094        '''
2095        self.exclude_refs(*objs)  # skip refs to objs
2096        s, t = {}, []
2097        self.exclude_objs(s, t)
2098        for o in objs:
2099            i = id(o)
2100            if i in s:  # duplicate
2101                self._seen.again(i)
2102            else:
2103                s[i] = self._sizer(o, 0, 0, sized)
2104            t.append(s[i])
2105        return tuple(t)
2106
2107    @property
2108    def align(self):
2109        '''Get the size alignment (int).
2110        '''
2111        return self._align_
2112
2113    def asized(self, *objs, **opts):
2114        '''Size each object and return an **Asized** instance with
2115           size information and referents up to the given detail
2116           level (and with modified options, see method **set**).
2117
2118           If only one object is given, the return value is the
2119           **Asized** instance for that object.  The **Asized** size
2120           of duplicate and ignored objects will be zero.
2121        '''
2122        if opts:
2123            self.set(**opts)
2124        t = self._sizes(objs, Asized)
2125        if len(t) == 1:
2126            t = t[0]
2127        return t
2128
2129    def asizeof(self, *objs, **opts):
2130        '''Return the combined size of the given objects
2131           (with modified options, see method **set**).
2132        '''
2133        if opts:
2134            self.set(**opts)
2135        self.exclude_refs(*objs)  # skip refs to objs
2136        return sum(self._sizer(o, 0, 0, None) for o in objs)
2137
2138    def asizesof(self, *objs, **opts):
2139        '''Return the individual sizes of the given objects
2140           (with modified options, see method  **set**).
2141
2142           The size of duplicate and ignored objects will be zero.
2143        '''
2144        if opts:
2145            self.set(**opts)
2146        return self._sizes(objs, None)
2147
2148    @property
2149    def clip(self):
2150        '''Get the clipped string length (int).
2151        '''
2152        return self._clip_
2153
2154    @property
2155    def code(self):
2156        '''Size (byte) code (bool).
2157        '''
2158        return self._code_
2159
2160    @property
2161    def cutoff(self):
2162        '''Stats cutoff (int).
2163        '''
2164        return self._cutoff_
2165
2166    @property
2167    def derive(self):
2168        '''Derive types (bool).
2169        '''
2170        return self._derive_
2171
2172    @property
2173    def detail(self):
2174        '''Get the detail level for **Asized** refs (int).
2175        '''
2176        return self._detail_
2177
2178    @property
2179    def duplicate(self):
2180        '''Get the number of duplicate objects seen so far (int).
2181        '''
2182        return sum(1 for v in _values(self._seen) if v > 1)  # == len
2183
2184    def exclude_objs(self, *objs):
2185        '''Exclude the specified objects from sizing, profiling and ranking.
2186        '''
2187        for o in objs:
2188            self._seen.setdefault(id(o), -1)
2189
2190    def exclude_refs(self, *objs):
2191        '''Exclude any references to the specified objects from sizing.
2192
2193           While any references to the given objects are excluded, the
2194           objects will be sized if specified as positional arguments
2195           in subsequent calls to methods **asizeof** and **asizesof**.
2196        '''
2197        for o in objs:
2198            self._seen.setdefault(id(o), 0)
2199
2200    def exclude_types(self, *objs):
2201        '''Exclude the specified object instances and types from sizing.
2202
2203           All instances and types of the given objects are excluded,
2204           even objects specified as positional arguments in subsequent
2205           calls to methods **asizeof** and **asizesof**.
2206        '''
2207        for o in objs:
2208            for t in _keytuple(o):
2209                if t and t not in self._excl_d:
2210                    self._excl_d[t] = 0
2211
2212    @property
2213    def excluded(self):
2214        '''Get the types being excluded (tuple).
2215        '''
2216        return tuple(_keys(self._excl_d))
2217
2218    @property
2219    def frames(self):
2220        '''Ignore stack frames (bool).
2221        '''
2222        return self._frames_
2223
2224    @property
2225    def ignored(self):
2226        '''Ignore certain types (bool).
2227        '''
2228        return True if self._ign_d else False
2229
2230    @property
2231    def infer(self):
2232        '''Infer types (bool).
2233        '''
2234        return self._infer_
2235
2236    @property
2237    def limit(self):
2238        '''Get the recursion limit (int).
2239        '''
2240        return self._limit_
2241
2242    @property
2243    def missed(self):
2244        '''Get the number of objects missed due to errors (int).
2245        '''
2246        return self._missed
2247
2248    def print_largest(self, w=0, cutoff=0, **print3options):
2249        '''Print the largest objects.
2250
2251           The available options and defaults are:
2252
2253            *w=0*           -- indentation for each line
2254
2255            *cutoff=100*    -- number of largest objects to print
2256
2257            *print3options* -- some keyword arguments, like Python 3+ print
2258        '''
2259        c = int(cutoff) if cutoff else self._cutoff_
2260        n = min(len(self._ranks), max(c, 0))
2261        s = self._above_
2262        if n > 0 and s > 0:
2263            self._printf('%s%*d largest object%s (of %d over %d bytes%s)', linesep,
2264                          w, n, _plural(n), self._ranked, s, _SI(s), **print3options)
2265            id2x = dict((r.id, i) for i, r in enumerate(self._ranks))
2266            for r in self._ranks[:n]:
2267                s, t = r.size, r.format(self._clip_, id2x)
2268                self._printf('%*d bytes%s: %s', w, s, _SI(s), t, **print3options)
2269
2270    def print_profiles(self, w=0, cutoff=0, **print3options):
2271        '''Print the profiles above *cutoff* percentage.
2272
2273           The available options and defaults are:
2274
2275                *w=0*           -- indentation for each line
2276
2277                *cutoff=0*      -- minimum percentage printed
2278
2279                *print3options* -- some keyword arguments, like Python 3+ print
2280        '''
2281        # get the profiles with non-zero size or count
2282        t = [(v, k) for k, v in _items(self._profs) if v.total > 0 or v.number > 1]
2283        if (len(self._profs) - len(t)) < 9:  # just show all
2284            t = [(v, k) for k, v in _items(self._profs)]
2285        if t:
2286            s = ''
2287            if self._total:
2288                s = ' (% of grand total)'
2289                c = int(cutoff) if cutoff else self._cutoff_
2290                C = int(c * 0.01 * self._total)
2291            else:
2292                C = c = 0
2293            self._printf('%s%*d profile%s:  total%s, average, and largest flat size%s:  largest object',
2294                         linesep, w, len(t), _plural(len(t)), s, self._incl, **print3options)
2295            r = len(t)
2296            for v, k in sorted(t, reverse=True):
2297                s = 'object%(plural)s:  %(total)s, %(avg)s, %(high)s:  %(obj)s%(lengstr)s' % v.format(self._clip_, self._total)
2298                self._printf('%*d %s %s', w, v.number, self._prepr(k), s, **print3options)
2299                r -= 1
2300                if r > 1 and v.total < C:
2301                    self._printf('%+*d profiles below cutoff (%.0f%%)', w, r, c)
2302                    break
2303            z = len(self._profs) - len(t)
2304            if z > 0:
2305                self._printf('%+*d %r object%s', w, z, 'zero', _plural(z), **print3options)
2306
2307    def print_stats(self, objs=(), opts={}, sized=(), sizes=(), stats=3, **print3options):
2308        '''Prints the statistics.
2309
2310           The available options and defaults are:
2311
2312                *w=0*           -- indentation for each line
2313
2314                *objs=()*       -- optional, list of objects
2315
2316                *opts={}*       -- optional, dict of options used
2317
2318                *sized=()*      -- optional, tuple of **Asized** instances returned
2319
2320                *sizes=()*      -- optional, tuple of sizes returned
2321
2322                *stats=3*       -- print stats, see function **asizeof**
2323
2324                *print3options* -- some keyword arguments, like Python 3+ print
2325        '''
2326        s = min(opts.get('stats', stats) or 0, self.stats)
2327        if s > 0:  # print stats
2328            w = len(str(self.missed + self.seen + self.total)) + 1
2329            t = c = ''
2330            o = _kwdstr(**opts)
2331            if o and objs:
2332                c = ', '
2333            # print header line(s)
2334            if sized and objs:
2335                n = len(objs)
2336                if n > 1:
2337                    self._printf('%sasized(...%s%s) ...', linesep, c, o, **print3options)
2338                    for i in range(n):  # no enumerate in Python 2.2.3
2339                        self._printf('%*d: %s', w - 1, i, sized[i], **print3options)
2340                else:
2341                    self._printf('%sasized(%s): %s', linesep, o, sized, **print3options)
2342            elif sizes and objs:
2343                self._printf('%sasizesof(...%s%s) ...', linesep, c, o, **print3options)
2344                for z, o in zip(sizes, objs):
2345                    self._printf('%*d bytes%s%s:  %s', w, z, _SI(z), self._incl, self._repr(o), **print3options)
2346            else:
2347                if objs:
2348                    t = self._repr(objs)
2349                self._printf('%sasizeof(%s%s%s) ...', linesep, t, c, o, **print3options)
2350            # print summary
2351            self.print_summary(w=w, objs=objs, **print3options)
2352            # for backward compatibility, cutoff from fractional stats
2353            s, c = self._c100(s)
2354            self.print_largest(w=w, cutoff=c if s < 2 else 10, **print3options)
2355            if s > 1:  # print profile
2356                self.print_profiles(w=w, cutoff=c, **print3options)
2357                if s > 2:  # print typedefs
2358                    self.print_typedefs(w=w, **print3options)  # PYCHOK .print_largest?
2359
2360    def print_summary(self, w=0, objs=(), **print3options):
2361        '''Print the summary statistics.
2362
2363           The available options and defaults are:
2364
2365                *w=0*           -- indentation for each line
2366
2367                *objs=()*       -- optional, list of objects
2368
2369                *print3options* -- some keyword arguments, like Python 3+ print
2370        '''
2371        self._printf('%*d bytes%s%s', w, self._total, _SI(self._total), self._incl, **print3options)
2372        if self._mask:
2373            self._printf('%*d byte aligned', w, self._mask + 1, **print3options)
2374        self._printf('%*d byte sizeof(void*)', w, _sizeof_Cvoidp, **print3options)
2375        n = len(objs or ())
2376        self._printf('%*d object%s %s', w, n, _plural(n), 'given', **print3options)
2377        n = self.sized
2378        self._printf('%*d object%s %s', w, n, _plural(n), 'sized', **print3options)
2379        if self._excl_d:
2380            n = sum(_values(self._excl_d))
2381            self._printf('%*d object%s %s', w, n, _plural(n), 'excluded', **print3options)
2382        n = self.seen
2383        self._printf('%*d object%s %s', w, n, _plural(n), 'seen', **print3options)
2384        n = self.ranked
2385        if n > 0:
2386            self._printf('%*d object%s %s', w, n, _plural(n), 'ranked', **print3options)
2387        n = self.missed
2388        self._printf('%*d object%s %s', w, n, _plural(n), 'missed', **print3options)
2389        n = self.duplicate
2390        self._printf('%*d duplicate%s', w, n, _plural(n), **print3options)
2391        if self._depth > 0:
2392            self._printf('%*d deepest recursion', w, self._depth, **print3options)
2393
2394    def print_typedefs(self, w=0, **print3options):
2395        '''Print the types and dict tables.
2396
2397           The available options and defaults are:
2398
2399                *w=0*           -- indentation for each line
2400
2401                *print3options* -- some keyword arguments, like Python 3+ print
2402        '''
2403        for k in _all_kinds:
2404            # XXX Python 3+ doesn't sort type objects
2405            t = [(self._prepr(a), v) for a, v in _items(_typedefs)
2406                                      if v.kind == k and (v.both or self._code_)]
2407            if t:
2408                self._printf('%s%*d %s type%s:  basicsize, itemsize, _len_(), _refs()',
2409                             linesep, w, len(t), k, _plural(len(t)), **print3options)
2410                for a, v in sorted(t):
2411                    self._printf('%*s %s:  %s', w, '', a, v, **print3options)
2412        # dict and dict-like classes
2413        t = sum(len(v) for v in _values(_dict_classes))
2414        if t:
2415            self._printf('%s%*d dict/-like classes:', linesep, w, t, **print3options)
2416            for m, v in _items(_dict_classes):
2417                self._printf('%*s %s:  %s', w, '', m, self._prepr(v), **print3options)
2418
2419    @property
2420    def ranked(self):
2421        '''Get the number objects ranked by size so far (int).
2422        '''
2423        return self._ranked
2424
2425    def reset(self, above=1024, align=8, clip=80, code=False,  # PYCHOK too many args
2426                    cutoff=10, derive=False, detail=0, frames=False, ignored=True,
2427                    infer=False, limit=100, stats=0, stream=None, **extra):
2428        '''Reset sizing options, state, etc. to defaults.
2429
2430           The available options and default values are:
2431
2432                *above=0*      -- threshold for largest objects stats
2433
2434                *align=8*      -- size alignment
2435
2436                *code=False*   -- incl. (byte)code size
2437
2438                *cutoff=10*    -- limit large objects or profiles stats
2439
2440                *derive=False* -- derive from super type
2441
2442                *detail=0*     -- **Asized** refs level
2443
2444                *frames=False* -- ignore frame objects
2445
2446                *ignored=True* -- ignore certain types
2447
2448                *infer=False*  -- try to infer types
2449
2450                *limit=100*    -- recursion limit
2451
2452                *stats=0*      -- print statistics, see function **asizeof**
2453
2454                *stream=None*  -- output stream for printing
2455
2456           See function **asizeof** for a description of the options.
2457        '''
2458        if extra:
2459            t = _plural(len(extra)), _kwdstr(**extra)
2460            raise KeyError('invalid option%s: %s' % t)
2461        # options
2462        self._above_ = above
2463        self._align_ = align
2464        self._clip_ = clip
2465        self._code_ = code
2466        self._cutoff_ = cutoff
2467        self._derive_ = derive
2468        self._detail_ = detail  # for Asized only
2469        self._frames_ = frames
2470        self._infer_ = infer
2471        self._limit_ = limit
2472        self._stats_ = stats
2473        self._stream = stream
2474        if ignored:
2475            self._ign_d = _kind_ignored
2476        else:
2477            self._ign_d = None
2478        # clear state
2479        self._clear()
2480        self.set(align=align, code=code, cutoff=cutoff, stats=stats)
2481
2482    @property
2483    def seen(self):
2484        '''Get the number objects seen so far (int).
2485        '''
2486        return sum(v for v in _values(self._seen) if v > 0)
2487
2488    def set(self, above=None, align=None, code=None, cutoff=None,
2489                  frames=None, detail=None, limit=None, stats=None):
2490        '''Set some sizing options.  See also **reset**.
2491
2492           The available options are:
2493
2494                *above*  -- threshold for largest objects stats
2495
2496                *align*  -- size alignment
2497
2498                *code*   -- incl. (byte)code size
2499
2500                *cutoff* -- limit large objects or profiles stats
2501
2502                *detail* -- **Asized** refs level
2503
2504                *frames* -- size or ignore frame objects
2505
2506                *limit*  -- recursion limit
2507
2508                *stats*  -- print statistics, see function **asizeof**
2509
2510           Any options not set remain unchanged from the previous setting.
2511        '''
2512        # adjust
2513        if above is not None:
2514            self._above_ = int(above)
2515        if align is not None:
2516            self._align_ = align
2517            if align > 1:
2518                self._mask = align - 1
2519                if (self._mask & align) != 0:
2520                    raise ValueError('invalid option: %s=%r' % ('align', align))
2521            else:
2522                self._mask = 0
2523        if code is not None:
2524            self._code_ = code
2525            if code:  # incl. (byte)code
2526                self._incl = ' (incl. code)'
2527        if detail is not None:
2528            self._detail_ = detail
2529        if frames is not None:
2530            self._frames_ = frames
2531        if limit is not None:
2532            self._limit_ = limit
2533        if stats is not None:
2534            if stats < 0:
2535                raise ValueError('invalid option: %s=%r' % ('stats', stats))
2536            # for backward compatibility, cutoff from fractional stats
2537            s, c = self._c100(stats)
2538            self._cutoff_ = int(cutoff) if cutoff else c
2539            self._stats_ = s
2540            self._profile = s > 1  # profile types
2541
2542    @property
2543    def sized(self):
2544        '''Get the number objects sized so far (int).
2545        '''
2546        return sum(1 for v in _values(self._seen) if v > 0)
2547
2548    @property
2549    def stats(self):
2550        '''Get the stats and cutoff setting (float).
2551        '''
2552        return self._stats_  # + (self._cutoff_ * 0.01)
2553
2554    @property
2555    def total(self):
2556        '''Get the total size (in bytes) accumulated so far.
2557        '''
2558        return self._total
2559
2560
2561# Public functions
2562
2563def adict(*classes):
2564    '''Install one or more classes to be handled as dict.
2565    '''
2566    a = True
2567    for c in classes:
2568        # if class is dict-like, add class
2569        # name to _dict_classes[module]
2570        if isclass(c) and _infer_dict(c):
2571            t = _dict_classes.get(c.__module__, ())
2572            if c.__name__ not in t:  # extend tuple
2573                _dict_classes[c.__module__] = t + (c.__name__,)
2574        else:  # not a dict-like class
2575            a = False
2576    return a  # all installed if True
2577
2578
2579_asizer = Asizer()
2580
2581
2582def asized(*objs, **opts):
2583    '''Return a tuple containing an **Asized** instance for each
2584       object passed as positional argument.
2585
2586       The available options and defaults are:
2587
2588            *above=0*      -- threshold for largest objects stats
2589
2590            *align=8*      -- size alignment
2591
2592            *code=False*   -- incl. (byte)code size
2593
2594            *cutoff=10*    -- limit large objects or profiles stats
2595
2596            *derive=False* -- derive from super type
2597
2598            *detail=0*     -- Asized refs level
2599
2600            *frames=False* -- ignore stack frame objects
2601
2602            *ignored=True* -- ignore certain types
2603
2604            *infer=False*  -- try to infer types
2605
2606            *limit=100*    -- recursion limit
2607
2608            *stats=0*      -- print statistics
2609
2610       If only one object is given, the return value is the **Asized**
2611       instance for that object.  Otherwise, the length of the returned
2612       tuple matches the number of given objects.
2613
2614       The **Asized** size of duplicate and ignored objects will be zero.
2615
2616       Set *detail* to the desired referents level and *limit* to the
2617       maximum recursion depth.
2618
2619       See function **asizeof** for descriptions of the other options.
2620    '''
2621    _asizer.reset(**opts)
2622    if objs:
2623        t = _asizer.asized(*objs)
2624        _asizer.print_stats(objs, opts=opts, sized=t)  # show opts as _kwdstr
2625        _asizer._clear()
2626    else:
2627        t = ()
2628    return t
2629
2630
2631def asizeof(*objs, **opts):
2632    '''Return the combined size (in bytes) of all objects passed
2633       as positional arguments.
2634
2635       The available options and defaults are:
2636
2637            *above=0*      -- threshold for largest objects stats
2638
2639            *align=8*      -- size alignment
2640
2641            *clip=80*      -- clip ``repr()`` strings
2642
2643            *code=False*   -- incl. (byte)code size
2644
2645            *cutoff=10*    -- limit large objects or profiles stats
2646
2647            *derive=False* -- derive from super type
2648
2649            *frames=False* -- ignore stack frame objects
2650
2651            *ignored=True* -- ignore certain types
2652
2653            *infer=False*  -- try to infer types
2654
2655            *limit=100*    -- recursion limit
2656
2657            *stats=0*      -- print statistics
2658
2659       Set *align* to a power of 2 to align sizes.  Any value less
2660       than 2 avoids size alignment.
2661
2662       If *all* is True and if no positional arguments are supplied.
2663       size all current gc objects, including module, global and stack
2664       frame objects.
2665
2666       A positive *clip* value truncates all repr() strings to at
2667       most *clip* characters.
2668
2669       The (byte)code size of callable objects like functions,
2670       methods, classes, etc. is included only if *code* is True.
2671
2672       If *derive* is True, new types are handled like an existing
2673       (super) type provided there is one and only of those.
2674
2675       By default certain base types like object, super, etc. are
2676       ignored.  Set *ignored* to False to include those.
2677
2678       If *infer* is True, new types are inferred from attributes
2679       (only implemented for dict types on callable attributes
2680       as get, has_key, items, keys and values).
2681
2682       Set *limit* to a positive value to accumulate the sizes of
2683       the referents of each object, recursively up to the limit.
2684       Using *limit=0* returns the sum of the flat sizes of the
2685       given objects.  High *limit* values may cause runtime errors
2686       and miss objects for sizing.
2687
2688       A positive value for *stats* prints up to 9 statistics, (1)
2689       a summary of the number of objects sized and seen and a list
2690       of the largests objects with size over *above* bytes, (2) a
2691       simple profile of the sized objects by type and (3+) up to 6
2692       tables showing the static, dynamic, derived, ignored, inferred
2693       and dict types used, found respectively installed.
2694       The fractional part of the *stats* value (x 100) is the number
2695       of largest objects shown for (*stats*1.+) or the cutoff
2696       percentage for simple profiles for (*stats*=2.+).  For example,
2697       *stats=1.10* shows the summary and the 10 largest objects,
2698       also the default.
2699
2700       See this module documentation for the definition of flat size.
2701    '''
2702    t, p, x = _objs_opts_x(objs, **opts)
2703    _asizer.reset(**p)
2704    if t:
2705        if x:  # don't size, profile or rank _getobjects tuple
2706            _asizer.exclude_objs(t)
2707        s = _asizer.asizeof(*t)
2708        _asizer.print_stats(objs=t, opts=opts)  # show opts as _kwdstr
2709        _asizer._clear()
2710    else:
2711        s = 0
2712    return s
2713
2714
2715def asizesof(*objs, **opts):
2716    '''Return a tuple containing the size (in bytes) of all objects
2717       passed as positional arguments.
2718
2719       The available options and defaults are:
2720
2721            *above=1024*   -- threshold for largest objects stats
2722
2723            *align=8*      -- size alignment
2724
2725            *clip=80*      -- clip ``repr()`` strings
2726
2727            *code=False*   -- incl. (byte)code size
2728
2729            *cutoff=10*    -- limit large objects or profiles stats
2730
2731            *derive=False* -- derive from super type
2732
2733            *frames=False* -- ignore stack frame objects
2734
2735            *ignored=True* -- ignore certain types
2736
2737            *infer=False*  -- try to infer types
2738
2739            *limit=100*    -- recursion limit
2740
2741            *stats=0*      -- print statistics
2742
2743       See function **asizeof** for a description of the options.
2744
2745       The length of the returned tuple equals the number of given
2746       objects.
2747
2748       The size of duplicate and ignored objects will be zero.
2749    '''
2750    _asizer.reset(**opts)
2751    if objs:
2752        t = _asizer.asizesof(*objs)
2753        _asizer.print_stats(objs, opts=opts, sizes=t)  # show opts as _kwdstr
2754        _asizer._clear()
2755    else:
2756        t = ()
2757    return t
2758
2759
2760def _typedefof(obj, save=False, **opts):
2761    '''Get the typedef for an object.
2762    '''
2763    k = _objkey(obj)
2764    v = _typedefs.get(k, None)
2765    if not v:  # new typedef
2766        v = _typedef(obj, **opts)
2767        if save:
2768            _typedefs[k] = v
2769    return v
2770
2771
2772def basicsize(obj, **opts):
2773    '''Return the basic size of an object (in bytes).
2774
2775       The available options and defaults are:
2776
2777           *derive=False* -- derive type from super type
2778
2779           *infer=False*  -- try to infer types
2780
2781           *save=False*   -- save the object's type definition if new
2782
2783       See this module documentation for the definition of *basic size*.
2784    '''
2785    b = t = _typedefof(obj, **opts)
2786    if t:
2787        b = t.base
2788    return b
2789
2790
2791def flatsize(obj, align=0, **opts):
2792    '''Return the flat size of an object (in bytes), optionally aligned
2793       to the given power of 2.
2794
2795       See function **basicsize** for a description of other available options.
2796
2797       See this module documentation for the definition of *flat size*.
2798    '''
2799    f = t = _typedefof(obj, **opts)
2800    if t:
2801        if align > 1:
2802            m = align - 1
2803            if (align & m) != 0:
2804                raise ValueError('invalid option: %s=%r' % ('align', align))
2805        else:
2806            m = 0
2807        f = t.flat(obj, mask=m)
2808    return f
2809
2810
2811def itemsize(obj, **opts):
2812    '''Return the item size of an object (in bytes).
2813
2814       See function **basicsize** for a description of the available options.
2815
2816       See this module documentation for the definition of *item size*.
2817    '''
2818    i = t = _typedefof(obj, **opts)
2819    if t:
2820        i, v = t.item, t.vari
2821        if v and i == _sizeof_Cbyte:
2822            i = getattr(obj, v, i)
2823    return i
2824
2825
2826def leng(obj, **opts):
2827    '''Return the length of an object (in items).
2828
2829       See function **basicsize** for a description of the available options.
2830    '''
2831    n = t = _typedefof(obj, **opts)
2832    if t:
2833        n = t.leng
2834        if n and _callable(n):
2835            i, v, n = t.item, t.vari, n(obj)
2836            if v and i == _sizeof_Cbyte:
2837                i = getattr(obj, v, i)
2838                if i > _sizeof_Cbyte:
2839                    n = n // i
2840    return n
2841
2842
2843def named_refs(obj, **opts):
2844    '''Return all named **referents** of an object (re-using
2845       functionality from **asizeof**).
2846
2847       Does not return un-named *referents*, e.g. objects in a list.
2848
2849       See function **basicsize** for a description of the available options.
2850    '''
2851    rs = []
2852    v = _typedefof(obj, **opts)
2853    if v:
2854        v = v.refs
2855        if v and _callable(v):
2856            for r in v(obj, True):
2857                try:
2858                    rs.append((r.name, r.ref))
2859                except AttributeError:
2860                    pass
2861    return rs
2862
2863
2864def refs(obj, **opts):
2865    '''Return (a generator for) specific *referents* of an object.
2866
2867       See function **basicsize** for a description of the available options.
2868    '''
2869    v = _typedefof(obj, **opts)
2870    if v:
2871        v = v.refs
2872        if v and _callable(v):
2873            v = v(obj, False)
2874    return v
2875
2876
2877if __name__ == '__main__':
2878
2879    if '-v' in sys.argv:
2880        import platform
2881        print('%s %s (Python %s %s)' % (__file__, __version__,
2882                                        sys.version.split()[0],
2883                                        platform.architecture()[0]))
2884
2885    elif '-types' in sys.argv:  # print static _typedefs
2886        n = len(_typedefs)
2887        w = len(str(n)) * ' '
2888        _printf('%s%d type definitions: %s and %s, kind ... %s', linesep,
2889                 n, 'basic-', 'itemsize (leng)', '-type[def]s')
2890        for k, v in sorted((_prepr(k), v) for k, v in _items(_typedefs)):
2891            s = '%(base)s and %(item)s%(leng)s, %(kind)s%(code)s' % v.format()
2892            _printf('%s %s: %s', w, k, s)
2893
2894    else:
2895        gc = None
2896        if '-gc' in sys.argv:
2897            try:
2898                import gc  # PYCHOK expected
2899                gc.collect()
2900            except ImportError:
2901                pass
2902
2903        frames = '-frames' in sys.argv
2904
2905        # just an example
2906        asizeof(all=True, frames=frames, stats=1, above=1024)  # print summary + 10 largest
2907
2908        if gc:
2909            print('gc.collect() %d' % (gc.collect(),))
2910
2911# License from the initial version of this source file follows:
2912
2913# --------------------------------------------------------------------
2914#       Copyright (c) 2002-2019 -- ProphICy Semiconductor, Inc.
2915#                        All rights reserved.
2916#
2917# Redistribution and use in source and binary forms, with or without
2918# modification, are permitted provided that the following conditions
2919# are met:
2920#
2921# - Redistributions of source code must retain the above copyright
2922#   notice, this list of conditions and the following disclaimer.
2923#
2924# - Redistributions in binary form must reproduce the above copyright
2925#   notice, this list of conditions and the following disclaimer in
2926#   the documentation and/or other materials provided with the
2927#   distribution.
2928#
2929# - Neither the name of ProphICy Semiconductor, Inc. nor the names
2930#   of its contributors may be used to endorse or promote products
2931#   derived from this software without specific prior written
2932#   permission.
2933#
2934# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
2935# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
2936# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
2937# FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE
2938# COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
2939# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
2940# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
2941# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2942# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
2943# STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
2944# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
2945# OF THE POSSIBILITY OF SUCH DAMAGE.
2946# --------------------------------------------------------------------
2947