File asizeof.patch of Package python-Pympler

From d43776dd72c825382329d2f1a6c0189f3f4eaf66 Mon Sep 17 00:00:00 2001
From: mrJean1 <22154337+mrJean1@users.noreply.github.com>
Date: Sun, 27 Jul 2025 15:51:43 -0400
Subject: [PATCH] Updated asizeof.py for Python 3.13 and __slots__ issue

The size of instances and dicts in Python 3.13+ respectively 3.6+ is much smaller, up to 5 times or more. This asizeof version 25.07.27 has been updated to correctly size those objects. See issue #170 for more details.

Yet another fix for issue #176 with sizing __slots__ attributes.

Other changes are updates to the basic sizes, a check for running with/out the GIL enabled and mostly cosmetic ones.

NOTE, the test_asizeof.py module must be updated as well to 25.07.27.
---
 pympler/asizeof.py | 1539 ++++++++++++++++++++++++++------------------
 1 file changed, 918 insertions(+), 621 deletions(-)

diff --git a/pympler/asizeof.py b/pympler/asizeof.py
index bc3ac35..64ae7ea 100755
--- a/pympler/asizeof.py
+++ b/pympler/asizeof.py
@@ -1,4 +1,5 @@
 #!/usr/bin/env python
+# -*- coding: utf-8 -*-
 
 # Copyright, license and disclaimer are at the very end of this file.
 
@@ -17,7 +18,7 @@
 # has been updated.
 
 '''
-This module exposes 11 functions and 2 classes to obtain lengths and
+This module exposes 10 functions and 2 classes to obtain lengths and
 sizes of Python objects (for Python 3.6 or later).
 
 Earlier versions of this module supported Python versions down to
@@ -101,7 +102,7 @@
    object plus the *item size* times the number of allocated *items*,
    *references* to referents.  The *flat size* does include the size
    for the *references* to the referents, but not the size of the
-   referents themselves.
+   referents themselves [#md].
 
    The *flat size* returned by function *flatsize* equals the result
    of function *asizeof* with options *code=True*, *ignored=False*,
@@ -116,7 +117,7 @@
 
    The *basic* and *item size* are obtained from the ``__basicsize__``
    respectively ``__itemsize__`` attributes of the (type of the)
-   object.  Where necessary (e.g. sequence objects), a zero
+   object.  Where necessary (e.g. for sequence objects), a zero
    ``__itemsize__`` is replaced by the size of a corresponding C type.
 
    The overhead for Python's garbage collector (GC) is included in
@@ -180,100 +181,275 @@
 
 .. [#bi] All ``type``s and ``class``es in modules named in private set
      ``_ignored_modules`` are ignored like other, standard built-ins.
+
+.. [#md] Since Python 3.13, objects may contain *inline values* and a
+     *materialized dict*.  The approximate size of the *inline values*
+     references ("slots") is included in the *flatsize* of the object.
+     The *materialized dict* will be sized only if present in the object,
+     otherwise the *inline values* are sized.  This may need further
+     adjustments for Python 3.14 and beyond.
 '''  # PYCHOK escape
 import sys
-if sys.version_info < (3, 6, 0):
-    raise NotImplementedError('%s requires Python 3.6 or newer' % (__file__,))
+if sys.version_info < (3, 6):
+    raise NotImplementedError('%s requires Python 3.6 or newer: %s' %
+                              (__file__, sys.version.split()[0]))
+import gc
+import types as Types
+import weakref as Weakref
 
 # from abc import ABCMeta
-from typing import Callable, Dict, List, Set, Union  # Optional
-
-# all imports listed explicitly to help PyChecker
-from inspect import (isbuiltin, isclass, iscode, isframe, isfunction,
-                     ismethod, ismodule)  # stack
+from array import array as _array  # array type
+from inspect import (isbuiltin, isclass, iscode, isframe,
+                     isfunction, ismethod, ismodule)  # stack
 from math import log
-from os import curdir, linesep
-from struct import calcsize  # type/class Struct only in Python 2.5+
-import types as Types
-import warnings
-import weakref as Weakref
+from os import curdir, linesep as _NL, environ  # deleted below
+# from typing import Callable, Dict, List, Set, Union
+from warnings import warn
+
+__all__ = ()  # extended below
+__version__ = '25.07.27'  # 24.11.22, 23.11.15, 22.12.07, 22.06.30
+
+_Bools     = (True, False)
+_NN        =  \
+_Non_vari  = ''  # non-variable item size
+_NoneNone  = (None, None)  # not a class
+_typeof    =  type  # function
+_Type_type = _typeof(type)  # == type and (new-style) class type
+_3_Udict   = (3, 9)  # PyDictUnicodeEntry since (3, 6)?
+# 'cell' is holding data used in closures
+t = (lambda unused: (lambda: unused))(None)
+_cell_type = _typeof(t.__closure__[0])  # type: ignore
 
-__all__ = []  # overwritten below
-__version__ = '22.12.07'  # 22.06.30
+# Any classes and types in modules named in set _ignored_modules
+# are ignored by default, like most built-ins classes and types
+_ignored_modules = {Exception.__module__, int.__module__, 'ctypes',
+                    isclass.__module__, warn.__module__, '_ctypes',
+                    sys.__name__, Types.__name__,  # Weakref.__name__,
+                    __name__}  # this very module
+try:
+    _withGIL = sys._is_gil_enabled  # CPy 3.13+
+    if not _withGIL():
+        raise NotImplementedError('%s without GIL enabled' % (__file__,))
+except AttributeError:
+    def _withGIL():
+        return True
 
-_NN       = ''
-_Not_vari = _NN  # non-variable item size
 
-# Any classes and types in modules named in set _ignored_modules
-# are ignored by default, like other built-ins classes and types
-_ignored_modules = {int.__module__, 'types', Exception.__module__,  # 'weakref'
-                    __name__}  # inluding this very module
+def property_RO(method):  # XXX class
+    '''Immutable, *Read-Only* property.
+    '''
+    def fget(inst, *unused):  # PYCHOK 2 vs 3 args
+        return method(inst)
 
-# Sizes of some primitive C types
-# XXX len(pack(T, 0)) == Struct(T).size == calcsize(T)
-_sizeof_Cbyte  = calcsize('c')  # sizeof(unsigned char)
-_sizeof_Clong  = calcsize('l')  # sizeof(long)
-_sizeof_Cvoidp = calcsize('P')  # sizeof(void*)
+    def ferr(inst, *unused):  # PYCHOK no cover
+        t = _typeof(inst).__name__, method.__name__, inst
+        raise TypeError('%s.%s immutable: %r' % t)
 
-# sizeof(long) != sizeof(ssize_t) on LLP64
-_z_P_L = 'P' if _sizeof_Clong < _sizeof_Cvoidp else 'L'
+    return property(fget, ferr, ferr, method.__doc__)
 
 
-def _calcsize(fmt):
-    '''Like struct.calcsize() but with 'z' for Py_ssize_t.
-    '''
-    return calcsize(fmt.replace('z', _z_P_L))
+class _TPFLAGS(object):  # PYCHOK Some Py_TPFLAGS_... in typ.__flags__
+    # <https://GitHub.com/python/cpython/blob/main/Include/internal/object.h>
 
+    def __init__(self):
+        self.INLINE_VALUES = 1 <<  2  # Py_TPFLAGS_INLINE_VALUES
+        self.MANAGED_DICT  = 1 <<  4  # Py_TPFLAGS_MANAGED_DICT
+        self.HEAPTYPE      = 1 <<  9  # Py_TPFLAGS_HEAPTYPE
+        self.HAVE_GC       = 1 << 14  # Py_TPFLAGS_HAVE_GC
 
-# Defaults for some basic sizes with 'z' for C Py_ssize_t
-_sizeof_CPyCodeObject = _calcsize('Pz10P5i0P')  # sizeof(PyCodeObject)
-_sizeof_CPyFrameObject = _calcsize('Pzz13P63i0P')  # sizeof(PyFrameObject)
-_sizeof_CPyModuleObject = _calcsize('PzP0P')  # sizeof(PyModuleObject)
+    def inlined(self, typ):
+        '''Does a type have *inline values*?
+        '''
+        f = getattr(typ, '__flags__', 0)
+        return bool(f & self.INLINE_VALUES)
 
-# Defaults for some item sizes with 'z' for C Py_ssize_t
-_sizeof_CPyDictEntry = _calcsize('z2P')  # sizeof(PyDictEntry)
-_sizeof_Csetentry = _calcsize('lP')  # sizeof(setentry)
+    def managed(self, typ):
+        '''Does a type have a *managed dict*?
+        '''
+        f = getattr(typ, '__flags__', 0)
+        return bool(f & self.MANAGED_DICT) and \
+               bool(f & self.INLINE_VALUES)
 
-# Get character size for internal unicode representation in Python < 3.3
-u = '\0'.encode('utf-8')
-_sizeof_Cunicode = len(u)
-del u
+_TPFLAGS = _TPFLAGS()  # PYCHOK singleton
 
-try:  # Size of GC header, sizeof(PyGC_Head)
-    import _testcapi as t
-    _sizeof_CPyGC_Head = t.SIZEOF_PYGC_HEAD  # new in Python 2.6
-except (ImportError, AttributeError):  # sizeof(PyGC_Head)
-    # alignment should be to sizeof(long double) but there
-    # is no way to obtain that value, assume twice double
-    t = calcsize('2d') - 1
-    _sizeof_CPyGC_Head = (_calcsize('2Pz') + t) & ~t
 
-# Size of refcounts (Python debug build only)
-t = hasattr(sys, 'gettotalrefcount')
-_sizeof_Crefcounts = _calcsize('2z') if t else 0
-del t
+class _Sizeof(object):  # PYCHOK sizes of needed C types
+    from struct import calcsize  # type/class Struct in CPy 2.5+
 
-# Some flags from .../Include/object.h
-_Py_TPFLAGS_HEAPTYPE = 1 << 9  # Py_TPFLAGS_HEAPTYPE
-_Py_TPFLAGS_HAVE_GC = 1 << 14  # Py_TPFLAGS_HAVE_GC
+    def __init__(self):
+        self.Cbyte  = self.calcsize('c')  # sizeof(unsigned char)
+        self.Clong  = self.calcsize('l')  # sizeof(long)
+        self.Cvoidp = self.calcsize('P')  # sizeof(void*), sizeof(PyObject*)
+        try:
+            _ = self.calcsize('n')  # CPy 3.3+
+            self._z2n = 'n'  # Py_ssize_t
+        except Exception:  # struct.error
+            # sizeof(long) != sizeof(Py_ssize_t) on LLP64
+            self._z2n = 'P' if self.Cvoidp > self.Clong else 'L'
+
+        if sys.version_info < _3_Udict:  # Older basic sizes with 'z' for Py_ssize_t
+            self.CPyCodeObject   = self.zsize('Pz10P5i0P')
+            self.CPyDictKeyEntry = \
+            self.CPyDictStrEntry = self.zsize('z2P')  # z for hash
+            self.CPyFrameObject  = self.zsize('Pzz13P63i0P')
+            # sizeof(PyGC_Head) should be aligned to sizeof(long double),
+            # but there's no way to obtain that size, assume twice double
+            self.CPyGC_Head      = self.align(self.zsize('2Pz'),
+                                   self.mask( self.zsize('2d')))
+            self.CPyModuleObject = self.zsize('PzP0P') + self.CPyDictStrEntry
+            self.Csetentry       = self.zsize('lP')
+        else:  # <https://GitHub.com/python/cpython/blob/main/Include/internal/...h>
+            self.CPyCodeObject   = self.zsize('3PiPi5P4iP')  # PyCodeConstructor
+            self.CPyDictKeyEntry = self.zsize('z2P')  # .../internal/pycore_dict.h
+            self.CPyDictStrEntry = self.zsize('2P')   # PyDictUnicodeEntry
+            self.CPyFrameObject  = self.zsize('3Pi2c4P')
+            self.CPyGC_Head      = self.zsize('2P') if _withGIL() else 0  # .../internal/pycore_interp_structs.h
+            self.CPyModuleObject = self.zsize('5P') + self.CPyDictStrEntry
+            self.Csetentry       = self.zsize('Pl')
+
+        # <https://GitHub.com/python/cpython/.../Lib/_pyrepl/simple_interact.py>
+        self.Crefcounts = self.zsize('2z') if hasattr(sys, 'gettotalrefcount') else 0  # _is_debug_build
+
+        # char size for internal unicode in CPy 3.2-
+        t = '\0'.encode('utf-8')
+        self.Cunicode = len(t)
+
+        # <https://GitHub.com/python/cpython/blob/main/Include/internal/pycore_object.h>
+        # <https://GitHub.com/python/cpython/blob/main/Objects/object_layout.md>
+        f = -(3 if _withGIL() else 1)  # NOT tested without GIL!
+        self.CMdDictOffset = self.Cvoidp * f  # MANAGED_DICT_OFFSET, negative!
+        self.CValuesFlags  = self.Cbyte  * 4  # capacity, size, embedded, valid
+
+    def align(self, size, mask):
+        '''Align a size to a mask.
+        '''
+        if mask and size:
+            size +=  mask
+            size &= ~mask
+#           if (mask + 1) & mask:
+#               raise _optionError(self.align, mask=mask)
+        return size
 
-_Type_type = type(type)  # == type and (new-style) class type
+    amapped = 0.01  # 0 <= percentage <= 1.0
+    get     = sys.getsizeof  # in CPy 2.6+
 
-from gc import (get_referents as _getreferents,
-                get_objects as _getobjects)  # containers only?
+    def inlined(self, obj):
+        '''Get an object's *inline* size in bytes: flags and "slots" only!
+        '''
+        n = _inlined(obj)
+        return 0 if n is None else (n * self.Cvoidp + self.CValuesFlags)
 
-if sys.platform == 'ios':  # Apple iOS
-    _gc_getobjects = _getobjects
+    def mask(self, align):
+        '''Return the mask for an alignment.
+        '''
+        if align > 1:
+            mask = align - 1
+            if mask & align:
+                raise _optionError(self.mask, align=align)
+        else:
+            mask = 0  # don't align
+        return mask
+
+    def zsize(self, fmt):
+        '''Like struct.calcsize() but with 'z' for Py_ssize_t.
+        '''
+        # len(pack(T, 0)) == Struct(T).size == calcsize(T)
+        return self.calcsize(fmt.replace('z', self._z2n))
 
-    def _getobjects():  # PYCHOK expected
-        # avoid Pythonista3/Python 3+ crash
-        return tuple(o for o in _gc_getobjects() if not _isNULL(o))
+_sizeof = _Sizeof()  # PYCHOK singleton
 
-_getsizeof = sys.getsizeof  # sys.getsizeof() new in Python 2.6
+if sys.version_info < _3_Udict:
+    _Udict = None
+
+    def _udictof(typ, unused):
+        return typ
+
+else:  # CPy _3_Udict+
+    # <https://github.com/python/cpython/blob/main/Objects/dictobject.c>
+    # <https://github.com/python/cpython/blob/main/Include/cpython/dictobject.h>
+    # <https://GitHub.com/python/cpython/blob/main/Objects/dictobject.c>
+
+    class _Udict(dict):  # dict with all (unicode) str keys
+        pass
+
+    def _udictof(typ, obj):
+        '''Return the type or _Udict.
+        '''
+        if typ is dict or isinstance(obj, dict):
+            # it's too messy to detect the DK_KIND_UNICODE item
+            # *PyDictObject.PyDictKeysObject.dk_kind*, instead
+            # check that all dict keys are a (unicode) str
+            if all(map(_ukey, _keys(obj))):
+                typ = _Udict
+        return typ
+
+    def _ukey(key):
+        '''Is key a (unicode) str?
+        '''
+        return _typeof(key) is str  # or isinstance(key, str)
+
+if sys.version_info < (3, 13):  # MCCABE 20
+    _Inline0 = None
+
+    def _inlined(unused):
+        return None  # always
+
+    def _materializedictuple(obj, **unused):
+        try:  # the __dict__, always
+            return obj.__dict__
+        except AttributeError:
+            return None
+
+else:  # CPy 3.13+
+    # courtesy ArieBovenberg <https://GitHub.com/pympler/pympler/issues/170>,
+    # <https://GitHub.com/python/cpython/pull/128763#discussion_r2130003738> and
+    # <https://GitHub.com/ariebovenberg/slotscheck/issues/112#issuecomment-2965388559>
+    import ctypes as _ct
+
+    class _Inline0(tuple):  # 0-size container of *inline* values or attributes
+        pass
+
+    def _inlined(obj):
+        '''Return the number of *inline values* or None.
+        '''
+        r = _materializedictuple(obj, inlinattrs=True)
+        return len(r) if _typeof(r) is _Inline0 else None
+
+    def _materializedictuple(obj, inlinattrs=False):
+        '''Get the *materialized dict* if present, otherwise a tuple
+           with the *inline values* or attributes.
+        '''
+        try:
+            t = _typeof(obj)
+            b =  t.__dictoffset__
+            if b > 0:  # normal __dict__
+                return obj.__dict__
+            elif b and _TPFLAGS.managed(t):  # b == -1
+                # if present, get the materialized __dict__
+                if _void_p_value(id(obj) + _sizeof.CMdDictOffset):
+                    r = obj.__dict__
+                    # assert len(r) >= len(t.__static_attributes__)
+                elif inlinattrs:  # otherwise the inline attrs as tuple
+                    r = _Inline0(t.__static_attributes__)
+                else:  # or the inline values as tuple
+                    def _iv(a):  # doesn't materialize the __dict__
+                        return getattr(obj, a, None)
+                    # the __static_attributes__ tuple doesn't include
+                    # class attrs nor dynamically added instance attrs
+                    # <https://GitHub.com/python/cpython/issues/135428>
+                    # <https://GitHub.com/python/cpython/issues/123860>
+                    r = _Inline0(map(_iv, t.__static_attributes__))
+                return r
+        except (AttributeError, ValueError):
+            pass  # no __dictoffset__, __flags__, __dict__, etc.
+        return None
+
+    def _void_p_value(addr):  # deref a c_void_p addr
+        return _ct.cast(addr, _ct.POINTER(_ct.c_void_p)).contents.value
 
 
 # Compatibility functions for more uniform
-# behavior across Python version 2.2 thu 3+
+# behavior across CPy 2.2 thru 3+
 
 def _items(obj):  # dict only
     '''Return iter-/generator, preferably.
@@ -296,30 +472,24 @@ def _values(obj):  # dict only
     return o() if callable(o) else (o or ())
 
 
-# 'cell' is holding data used in closures
-c = (lambda unused: (lambda: unused))(None)
-_cell_type = type(c.__closure__[0])  # type: ignore
-del c
-
-
 # Private functions
 
-def _basicsize(t, base=0, heap=False, obj=None):
+def _basicsize(typ, base=0, heap=False, obj=None):
     '''Get non-zero basicsize of type,
        including the header sizes.
     '''
-    s = max(getattr(t, '__basicsize__', 0), base)
+    s = max(getattr(typ, '__basicsize__', 0), base)
     # include gc header size
-    if t != _Type_type:
-        h = getattr(t, '__flags__', 0) & _Py_TPFLAGS_HAVE_GC
+    if typ != _Type_type:
+        f = getattr(typ, '__flags__', 0) & _TPFLAGS.HAVE_GC
     elif heap:  # type, allocated on heap
-        h = True
+        f = True
     else:  # None has no __flags__ attr
-        h = getattr(obj, '__flags__', 0) & _Py_TPFLAGS_HEAPTYPE
-    if h:
-        s += _sizeof_CPyGC_Head
-    # include reference counters
-    return s + _sizeof_Crefcounts
+        f = getattr(obj, '__flags__', 0) & _TPFLAGS.HEAPTYPE
+    if f:
+        s += _sizeof.CPyGC_Head
+    # include reference counter
+    return s + _sizeof.Crefcounts
 
 
 def _classof(obj, dflt=None):
@@ -336,36 +506,31 @@ def _derive_typedef(typ):
 
 
 def _dir2(obj, pref=_NN, excl=(), slots=None, itor=_NN):
-    '''Return an attribute name, object 2-tuple for certain
-       attributes or for the ``__slots__`` attributes of the
-       given object, but not both.  Any iterator referent
-       objects are returned with the given name if the
-       latter is non-empty.
-    '''
-    if slots:  # __slots__ attrs
-        if hasattr(obj, slots):
-            # collect all inherited __slots__ attrs
-            # from list, tuple, or dict __slots__,
-            # while removing any duplicate attrs
-            s = {}
-            for c in type(obj).mro():
-                n = _nameof(c)
-                for a in getattr(c, slots, ()):
-                    if a.startswith('__'):
-                        a = '_' + n + a
-                    if hasattr(obj, a):
-                        s.setdefault(a, getattr(obj, a))
-            # assume __slots__ tuple-like is holding the values
-            # yield slots, _Slots(s)  # _keys(s) ... REMOVED,
-            # see _Slots.__doc__ further below
-            for t in _items(s):
-                yield t  # attr name, value
-    elif itor:  # iterator referents
+    '''Return an attribute (name, object) 2-tuple for certain
+       attributes or for the ``__slots__`` of the given object,
+       **but not both**.  Or, any iterator referent objects are
+       returned with the given, non-empty *itor* name.
+    '''
+    if slots:  # either __slots__ or ...
+        # yield the closest parent __slots__ attributes
+        # (from a list, tuple, or dict) with the values
+        for a in getattr(obj, '__slots__', ()):
+            try:
+                yield a, getattr(obj, a)
+            except AttributeError:  # DUNDER?
+                if a.startswith('__'):  # and a.endswith('__')
+                    for c in _typeof(obj).mro()[:-1]:
+                        d = '_' + _nameof(c) + a
+                        if hasattr(obj, d):
+                            yield a, getattr(obj, d)
+                            break
+    elif itor:  # ... iterator referents or ...
         for o in obj:  # iter(obj)
             yield itor, o
-    else:  # regular attrs
+    else:  # ... attrs with prefix *pref*, except *excl*uded
         for a in dir(obj):
-            if a.startswith(pref) and hasattr(obj, a) and a not in excl:
+            if a.startswith(pref) and a not in excl \
+                                  and hasattr(obj, a):  # last!
                 yield a, getattr(obj, a)
 
 
@@ -374,16 +539,19 @@ def _infer_dict(obj):
     '''
     for attrs in (('items', 'keys', 'values'),
                   ('iteritems', 'iterkeys', 'itervalues')):
-        attrs += '__len__', 'get', 'has_key'  # 'update'
+        attrs += '__len__', 'get'  # 'has_key', 'popitem', 'setdefault', 'update'
         if all(callable(getattr(obj, a, None)) for a in attrs):
             return True
     return False
 
 
+# def _inlined() ... see above
+
+
 def _isbuiltin2(typ):
-    '''Return True for built-in types as in Python 2.
+    '''Return True for built-in types as in CPy 2.
     '''
-    # range is no longer a built-in in Python 3+
+    # range is no longer a built-in in CPy 3+
     return isbuiltin(typ) or (typ is range)
 
 
@@ -423,17 +591,6 @@ def _isnamedtuple(obj):
     return isinstance(obj, tuple) and hasattr(obj, '_fields')
 
 
-def _isNULL(obj):
-    '''Prevent asizeof(all=True, ...) crash.
-
-       Sizing gc.get_objects() crashes in Pythonista3 with
-       Python 3.5.1 on iOS due to 1-tuple (<Null>,) object,
-       see <http://forum.omz-software.com/user/mrjean1>.
-    '''
-    return isinstance(obj, tuple) and len(obj) == 1 \
-                                  and repr(obj) == '(<NULL>,)'
-
-
 def _issubclass(obj, Super):
     '''Safe inspect.issubclass() returning None if Super is
        *object* or if obj and Super are not a class or type.
@@ -446,17 +603,24 @@ def _issubclass(obj, Super):
     return None
 
 
-def _itemsize(t, item=0):
+def _itemsize(typ, item=0):
     '''Get non-zero itemsize of type.
     '''
     # replace zero value with default
-    return getattr(t, '__itemsize__', 0) or item
+    return getattr(typ, '__itemsize__', 0) or item
+
+
+def _itemstr(item):
+    '''Return a (key, value) pair as str.
+    '''
+    return '%s=%r' % item
 
 
 def _kwdstr(**kwds):
     '''Keyword arguments as a string.
     '''
-    return ', '.join(sorted('%s=%r' % kv for kv in _items(kwds)))
+    t = sorted(map(_itemstr, _items(kwds)), key=str.lower)
+    return ', '.join(t)
 
 
 def _lengstr(obj):
@@ -471,6 +635,9 @@ def _lengstr(obj):
     return r
 
 
+# def _materializedictuple() ... see above
+
+
 def _moduleof(obj, dflt=_NN):
     '''Return the object's module name.
     '''
@@ -483,6 +650,29 @@ def _nameof(obj, dflt=_NN):
     return getattr(obj, '__name__', dflt)
 
 
+if sys.platform == 'ios':  # Apple iOS
+    _ignored_modules |= {'objc_util',}  # 'runtime'
+
+    def _None(obj):  # prevent asizeof(all=True, ...) crash:
+        # sizing gc.get_objects() crashes in Pythonista3 with
+        # CPy 3.5.1 on iOS due to 1-tuple (<Null>,) object, see
+        # <http://forum.omz-software.com/user/mrjean1>.
+        if isinstance(obj, tuple) and len(obj) == 1 \
+                                  and repr(obj) == '(<NULL>,)':
+            obj = None  # (None,)
+        return obj
+
+    def _noNULLs(lt):
+        if isinstance(lt, list):
+            lt[:] = map(_None, lt)
+        else:
+            lt = tuple(map(_None, lt))
+        return lt
+else:
+    def _noNULLs(lt):
+        return lt
+
+
 def _objs_opts_x(where, objs, all=None, **opts):
     '''Return the given or 'all' objects plus
        the remaining options and exclude flag
@@ -492,13 +682,13 @@ def _objs_opts_x(where, objs, all=None, **opts):
     elif all in (False, None):
         t, x = (), True
     elif all is True:  # 'all' objects
-        t, x = _getobjects(), True
+        t, x = _noNULLs(gc.get_objects()), True
     else:
-        raise _OptionError(where, all=all)
+        raise _optionError(where, all=all)
     return t, opts, x
 
 
-def _OptionError(where, Error=ValueError, **options):
+def _optionError(where, Error=ValueError, **options):
     '''Format an *Error* instance for invalid *option* or *options*.
     '''
     t = _plural(len(options)), _nameof(where), _kwdstr(**options)
@@ -518,7 +708,7 @@ def _p100(part, total, prec=1):
 
 
 def _plural(num):
-    '''Return 's' if *num* is not one.
+    '''Return 's' if *num* is not 1.
     '''
     return 's' if num != 1 else _NN
 
@@ -538,22 +728,23 @@ def _prepr(obj, clip=0):
     return _repr(obj, clip=clip).strip('<>').replace("'", _NN)  # remove <''>
 
 
-def _printf(fmt, *args, **print3options):
-    '''Formatted print to sys.stdout or given stream.
+def _printf(fmt, *args, **end_file_flush):
+    '''Formatted print to *sys.stdout* or given stream.
 
-       *print3options* -- some keyword arguments, like Python 3+ print.
+       *end_file_flush* -- some keyword arguments, like Python 3+ print.
     '''
-    if print3options:  # like Python 3+
-        f = print3options.get('file', None) or sys.stdout
-        if args:
-            f.write(fmt % args)
-        else:
-            f.write(fmt)
-        f.write(print3options.get('end', linesep))
-        if print3options.get('flush', False):
-            f.flush()
-    elif args:
-        print(fmt % args)
+    if args:
+        fmt %= args
+    if end_file_flush:  # like CPy 3+
+
+        def _opt3(end=_NL, file=None, flush=False, **unused):
+            return end, (file or sys.stdout), flush
+
+        e, s, f = _opt3(**end_file_flush)
+        s.write(fmt)
+        s.write(e)
+        if f:
+            s.flush()
     else:
         print(fmt)
 
@@ -570,16 +761,28 @@ def _N(unused, o):
     for a in attrs:  # cf. inspect.getmembers()
         if hasattr(obj, a):
             yield _N(a, getattr(obj, a))
+    mdict, kwds = _refs2(obj, **kwds)
+    if mdict:  # __dict__ last attr
+        # '__dict__' if isinstance(mdict, dict) else '__inlined__'
+        yield _N('__dict__', mdict)
     if kwds:  # kwds are _dir2() args
         for a, o in _dir2(obj, **kwds):
             yield _N(a, o)
 
 
+def _refs2(obj, mdict=False, **kwds):
+    '''Get the *materialized dict* or *inline values* tuple.
+    '''
+    if mdict:
+        mdict = _materializedictuple(obj)
+    return mdict, kwds
+
+
 def _repr(obj, clip=80):
     '''Clip long repr() string.
     '''
     try:  # safe repr()
-        r = repr(obj).replace(linesep, '\\n')
+        r = repr(obj).replace(_NL, '\\n')
     except Exception:
         r = 'N/A'
     if len(r) > clip > 0:
@@ -607,6 +810,12 @@ def _SI2(size, **kwds):
     return str(size) + _SI(size, **kwds)
 
 
+def _SI3(w, size):
+    '''Return size as formatted SI bytes string.
+    '''
+    return '%*d bytes%s' % (w, size, _SI(size))
+
+
 # Type-specific referents functions
 
 def _cell_refs(obj, named):
@@ -622,15 +831,15 @@ def _cell_refs(obj, named):
 def _class_refs(obj, named):
     '''Return specific referents of a class object.
     '''
-    return _refs(obj, named, '__class__', '__doc__', '__mro__',
-                             '__name__', '__slots__', '__weakref__',
-                             '__dict__')  # __dict__ last
+    return _refs(obj, named, '__class__', '__doc__', '__mro__', '__name__',
+                             '__slots__', '__static_attributes__',
+                             '__weakref__', mdict=True)  # __dict__ last
 
 
 def _co_refs(obj, named):
     '''Return specific referents of a code object.
     '''
-    return _refs(obj, named, pref='co_')
+    return _refs(obj, named, pref='co_', excl=('co_lnotab',))  # DeprecationWarning
 
 
 def _dict_refs(obj, named):
@@ -648,7 +857,7 @@ def _dict_refs(obj, named):
                 yield k
                 yield v
     except (KeyError, ReferenceError, TypeError) as x:
-        warnings.warn("Iterating '%s': %r" % (_classof(obj), x))
+        warn("Iterating '%s': %r" % (_classof(obj), x))
 
 
 def _enum_refs(obj, named):
@@ -660,7 +869,7 @@ def _enum_refs(obj, named):
 def _exc_refs(obj, named):
     '''Return specific referents of an Exception object.
     '''
-    # .message raises DeprecationWarning in Python 2.6
+    # .message raises DeprecationWarning in CPy 2.6
     return _refs(obj, named, 'args', 'filename', 'lineno', 'msg', 'text')  # , 'message', 'mixed'
 
 
@@ -680,7 +889,7 @@ def _func_refs(obj, named):
     '''Return specific referents of a function or lambda object.
     '''
     return _refs(obj, named, '__doc__', '__name__', '__code__', '__closure__',
-                 pref='func_', excl=('func_globals',))
+                             pref='func_', excl=('func_globals',))
 
 
 def _gen_refs(obj, named):
@@ -701,13 +910,13 @@ def _im_refs(obj, named):
 def _inst_refs(obj, named):
     '''Return specific referents of a class instance.
     '''
-    return _refs(obj, named, '__dict__', '__class__', slots='__slots__')
+    return _refs(obj, named, '__class__', mdict=True, slots=True)
 
 
 def _iter_refs(obj, named):
     '''Return the referent(s) of an iterator object.
     '''
-    r = _getreferents(obj)  # special case
+    r = gc.get_referents(obj)  # special case
     return _refs(r, named, itor=_nameof(obj) or 'iteref')
 
 
@@ -722,7 +931,7 @@ def _module_refs(obj, named):
 def _namedtuple_refs(obj, named):
     '''Return specific referents of obj-as-sequence and slots but exclude dict.
     '''
-    for r in _refs(obj, named, '__class__', slots='__slots__'):
+    for r in _refs(obj, named, '__class__', slots=True):
         yield r
     for r in obj:
         yield r
@@ -762,16 +971,16 @@ def _type_refs(obj, named):
     '''Return specific referents of a type object.
     '''
     return _refs(obj, named, '__doc__', '__mro__', '__name__',
-                             '__slots__', '__weakref__', '__dict__')
+                             '__slots__', '__weakref__', '__dict__')  # mdict=True
 
 
 def _weak_refs(obj, unused):  # unused for named
     '''Return weakly referent object.
     '''
     try:  # ignore 'key' of KeyedRef
-        return (obj(),)
+        yield obj()
     except Exception:  # XXX ReferenceError
-        return ()
+        pass
 
 
 _all_refs = {None, _cell_refs, _class_refs, _co_refs, _dict_refs, _enum_refs,
@@ -792,6 +1001,12 @@ def _len(obj):
         return 0
 
 
+def _len_array(obj):
+    '''Array length (in bytes!).
+    '''
+    return len(obj) * obj.itemsize
+
+
 def _len_bytearray(obj):
     '''Bytearray size.
     '''
@@ -823,15 +1038,16 @@ def _len_frame(obj):
     return _len_code(c) if c else 0
 
 
-# _sizeof_Cdigit = sys.int_info.sizeof_digit  # sys.int_info in Python 3.1+
-# _bitsof_Cdigit = sys.int_info.bits_per_digit  # (_sizeof_Cdigit * 15) // 2
-# _Typedef(int).base = int.__basicsize__  # == _getsizeof(0)
-# _Typedef(int).item = int.__itemsize__  # == _sizeof_Cdigit
+# _sizeof.Cdigit = sys.int_info.sizeof_digit    # sys.int_info in CPy 3.1+
+# _bitsof.Cdigit = sys.int_info.bits_per_digit  # (_sizeof.Cdigit * 15) // 2
+
+# _Typedef(int).base = int.__basicsize__  # == _sizeof.get(0)
+# _Typedef(int).item = int.__itemsize__   # == _sizeof.Cdigit
 
 def _len_int(obj):
     '''Length of *int* (multi-precision, formerly long) in Cdigits.
     '''
-    n = _getsizeof(obj, 0) - int.__basicsize__
+    n = _sizeof.get(obj, 0) - int.__basicsize__
     return (n // int.__itemsize__) if n > 0 else 0
 
 
@@ -875,8 +1091,8 @@ def _len_slice(obj):
     '''Slice length.
     '''
     try:
-        return ((obj.stop - obj.start + 1) // obj.step)
-    except (AttributeError, TypeError):
+        return abs(abs(obj.stop - obj.start) // obj.step)
+    except (AttributeError, TypeError, ZeroDivisionError):
         return 0
 
 
@@ -902,10 +1118,9 @@ def _len_unicode(obj):
     return len(obj) + 1
 
 
-_all_lens = {None, _len, _len_bytearray, _len_code, _len_dict,
-                   _len_frame, _len_int, _len_iter, _len_list,
-                   _len_module, _len_set, _len_slice, _len_struct,
-                   _len_unicode}  # type: Set[Union[None, Callable], ...]
+_all_lens = {None, _len, _len_array, _len_bytearray, _len_code, _len_dict,
+                   _len_frame, _len_int, _len_iter, _len_list, _len_module,
+                   _len_set, _len_slice, _len_struct, _len_unicode}  # type: Set[Union[None, Callable], ...]
 
 
 # More private functions and classes
@@ -913,10 +1128,10 @@ def _len_unicode(obj):
 # _old_style = '*'  # marker, OBSOLETE
 # _new_style = _NN  # no marker
 
-class _Claskey(object):
+class _Claskey(object):  # never sized
     '''Wrapper for class objects.
     '''
-    __slots__ = ('_obj',)  # '_sty'
+    # __slots__ = ('_obj',)  # '_sty'
 
     def __init__(self, obj):
         self._obj = obj  # XXX Weakref.ref(obj)
@@ -938,7 +1153,6 @@ def __str__(self):
 # and instances.
 
 _claskeys = {}  # type: Dict[int, _Claskey]
-_NoneNone = None, None  # not a class
 
 
 def _claskey(obj):
@@ -955,66 +1169,63 @@ def _claskey(obj):
 def _key2tuple(obj):  # PYCHOK expected
     '''Return class and instance keys for a class.
     '''
-    t = type(obj) is _Type_type  # isclass(obj):
+    t = _typeof(obj) is _Type_type  # isclass(obj):
     return (_claskey(obj), obj) if t else _NoneNone
 
 
-def _objkey(obj):  # PYCHOK expected
+def _objkey(obj):  # PYCHOK expected, in cached_asizeof.py
     '''Return the key for any object.
     '''
-    k = type(obj)
+    k = _typeof(obj)
     if k is _Type_type:  # isclass(obj):
         k = _claskey(obj)
+    elif _Udict:
+        k = _udictof(k, obj)
     return k
 
 
-class _NamedRef(object):
+class _NamedRef(object):  # never sized
     '''Store referred object along
        with the name of the referent.
     '''
-    __slots__ = ('name', 'ref')
+    # __slots__ = ('name', 'ref')
 
     def __init__(self, name, ref):
         self.name = name
         self.ref  = ref
 
 
-# class _Slots(tuple):
-#     '''Wrapper class for __slots__ attribute at class definition.
-#        The instance-specific __slots__ attributes are stored in
-#        a "tuple-like" space inside the instance, see Luciano
-#        Ramalho, "Fluent Python", page 274+, O'Reilly, 2016 or
-#        at <http://Books.Google.com/books>, then search for
-#        "Fluent Python" "Space Savings with the __slots__".
-#     '''
-#     pass
+class _NamedRefs(list):  # never sized
+    '''Container of _NamedRef instances.
+    '''
+    pass
 
 
 # all kinds of _Typedefs
-i = sys.intern  # Python 3+
-t = (_kind_static, _kind_dynamic, _kind_derived, _kind_ignored, _kind_inferred) = (
-        i('static'),  i('dynamic'),  i('derived'),  i('ignored'),  i('inferred'))
-_all_kinds = set(t)
-del i, t
+t =  'static        dynamic        derived        ignored        inferred'
+_kind_static, _kind_dynamic, _kind_derived, _kind_ignored, _kind_inferred = \
+t = tuple(map(sys.intern, t.split()))  # sys.intern CPy 3+
+_all_kinds = set(t)  # type: ignore
 
 
-class _Typedef(object):
+class _Typedef(object):  # never sized, in cached_asizeof.py
     '''Type definition class.
     '''
     base = 0     # basic size in bytes
     both = None  # both data and code if True, code only if False
+    inli = None  # has *inline values*
     item = 0     # item size in bytes
     kind = None  # _kind_... value
     leng = None  # _len_...() function or None
     refs = None  # _..._refs() function or None
     type = None  # original type
-    vari = None  # item size attr name or _Not_vari
-    xtyp = None  # if True, not _getsizeof'd
+    vari = None  # item size attr name or _Non_vari
+    _get = True  # if False, do NOT use _sizeof.get
 
     def __init__(self, **kwds):
         self.reset(**kwds)
 
-    def __lt__(self, unused):  # for Python 3+
+    def __lt__(self, unused):  # for CPy 3+
         return True
 
     def __repr__(self):
@@ -1032,31 +1243,36 @@ def args(self):  # as args tuple
         '''Return all attributes as arguments tuple.
         '''
         return (self.base, self.item, self.leng, self.refs,
-                self.both, self.kind, self.type, self.xtyp)
+                self.both, self.inli, self.kind, self.type,
+                self._get)
 
-    def dup(self, other=None, **kwds):
-        '''Duplicate attributes of dict or other typedef.
+    def dup(self, other=None, force=False, **updates):
+        '''Duplicate attributes from dict or an other typedef.
         '''
         t = other or _dict_typedef
         d = t.kwds()
-        d.update(kwds)
-        self.reset(**d)
+        d.update(updates)
+        if force:  # set kwds as-is
+            self.__dict__.update(d)
+        else:
+            self.reset(**d)
+        return self
 
     def flat(self, obj, mask=0):
         '''Return the aligned flat size.
         '''
         s = self.base
+        if self.inli:
+            s += _sizeof.inlined(obj)
         if self.leng and self.item > 0:  # include items
             s += self.leng(obj) * self.item
         # workaround sys.getsizeof bug for _array types
         # (in some Python versions) and for other types
         # with variable .itemsize like numpy.arrays, etc.
-        if not self.xtyp:
-            s = _getsizeof(obj, s)
-        if mask:  # alignment mask
-            s = (s + mask) & ~mask
-#           if (mask + 1) & mask:
-#               raise _OptionError(self.flat, mask=mask)
+        if self._get:
+            s = _sizeof.get(obj, s)
+        if mask:
+            s = _sizeof.align(s, mask)
         return s
 
     def format(self):
@@ -1072,21 +1288,36 @@ def kwds(self):
         '''Return all attributes as keywords dict.
         '''
         return dict(base=self.base, both=self.both, item=self.item,
-                    kind=self.kind, leng=self.leng, refs=self.refs,
-                    type=self.type, vari=self.vari, xtyp=self.xtyp)
+                    inli=self.inli, kind=self.kind, leng=self.leng,
+                    refs=self.refs, type=self.type, vari=self.vari,
+                    _get=self._get)
+
+    def kwds4(self, obj, **kwds):
+        '''Return all attributes for an object as keywords dict.
+        '''
+        d = self.kwds()  # copy
+        if self.leng:
+            d.update(len=self.leng(obj))
+        if kwds:
+            d.update(kwds)
+        return d
 
     def reset(self, base=0,    item=0,    leng=None, refs=None,
-                    both=True, kind=None, type=None, vari=_Not_vari,
-                    xtyp=False, **extra):
+                    both=True, inli=None, kind=None, type=None,
+                    vari=_Non_vari,       _get=True, **extra):
         '''Reset all specified typedef attributes.
         '''
-        v = vari or _Not_vari
+        if inli is None:
+            inli = _TPFLAGS.inlined(type)
+        v = vari or _Non_vari
         if v != str(v):  # attr name
             e = dict(vari=v)
         elif base < 0:
             e = dict(base=base)
-        elif both not in (False, True):
+        elif both not in _Bools:
             e = dict(both=both)
+        elif inli not in _Bools:
+            e = dict(inli=inli)
         elif item < 0:
             e = dict(item=item)
         elif kind not in _all_kinds:
@@ -1095,23 +1326,24 @@ def reset(self, base=0,    item=0,    leng=None, refs=None,
             e = dict(leng=leng)
         elif refs not in _all_refs:  # XXX or not callable(refs)
             e = dict(refs=refs)
-        elif xtyp not in (False, True):
-            e = dict(xtyp=xtyp)
+        elif _get not in _Bools:
+            e = dict(_get=_get)
         elif extra:
             e = {}
         else:
             self.base = base
             self.both = both
+            self.inli = inli
             self.item = item
             self.kind = kind
             self.leng = leng
             self.refs = refs
             self.type = type  # unchecked, as-is
             self.vari = v
-            self.xtyp = xtyp
-            return
+            self._get = _get
+            return self
         e.update(extra)
-        raise _OptionError(self.reset, **e)
+        raise _optionError(self.reset, **e)
 
     def save(self, t, base=0, heap=False):
         '''Save this typedef plus its class typedef.
@@ -1120,51 +1352,65 @@ def save(self, t, base=0, heap=False):
         if k and k not in _typedefs:  # instance key
             _typedefs[k] = self
             if c and c not in _typedefs:  # class key
-                b = _basicsize(type(t), base=base, heap=heap)
+                b = _basicsize(_typeof(t), base=base, heap=heap)
                 k = _kind_ignored if _isignored(t) else self.kind
                 _typedefs[c] = _Typedef(base=b, both=False,
                                         kind=k, type=t, refs=_type_refs)
         elif t not in _typedefs:
-            if not _isbuiltin2(t):  # array, range, xrange in Python 2.x
-                s = ' '.join((self.vari, _moduleof(t), _nameof(t)))
+            if not _isbuiltin2(t):  # array, range, xrange in CPy 2
+                s = '%s %s %s' % (self.vari, _moduleof(t), _nameof(t))
                 s = '%r %s %s' % ((c, k), self.both, s.strip())
                 raise KeyError('typedef %r bad: %s' % (self, s))
 
             _typedefs[t] = _Typedef(base=_basicsize(t, base=base), both=False,
                                     kind=_kind_ignored, type=t)
+        return self
 
-    def set(self, safe_len=False, **kwds):
+    def set(self, safe_len=False, force=False, **updates):
         '''Set one or more attributes.
         '''
-        if kwds:  # double check
+        if force:  # set kwds as-is
+            self.__dict__.update(updates)
+        elif updates:  # double check
             d = self.kwds()
-            d.update(kwds)
+            d.update(updates)
             self.reset(**d)
         if safe_len and self.item:
             self.leng = _len
+        return self
+
 
+_typedefs = {}  # type: Dict[type, _Typedef]  # in cached_asizeof.py
 
-_typedefs = {}  # type: Dict[type, _Typedef]
+
+def _array_kwds(obj):
+    # since item size varies by the array data type, set
+    # itemsize to 1 byte and use _len_array in bytes;
+    # _sizeof.get(array) returns array plus base size
+    b = max(56, _sizeof.get(obj, 0) - _len_array(obj))
+    return dict(base=b, leng=_len_array, item=_sizeof.Cbyte,
+                vari='itemsize',  # array.itemsize
+                _get= False)  # no _sizeof.get
 
 
-def _typedef_both(t, base=0, item=0, leng=None, refs=None,
-                     kind=_kind_static, heap=False, vari=_Not_vari):
+def _typedef_both(typ, base=0, item=0, leng=None, refs=None,
+                       kind=_kind_static, heap=False, vari=_Non_vari):
     '''Add new typedef for both data and code.
     '''
-    v = _Typedef(base=_basicsize(t, base=base), item=_itemsize(t, item),
+    v = _Typedef(base=_basicsize(typ, base=base), item=_itemsize(typ, item),
                  refs=refs, leng=leng,
-                 both=True, kind=kind, type=t, vari=vari)
-    v.save(t, base=base, heap=heap)
+                 both=True, kind=kind, type=typ, vari=vari)
+    v.save(typ, base=base, heap=heap)
     return v  # for _dict_typedef
 
 
-def _typedef_code(t, base=0, refs=None, kind=_kind_static, heap=False):
+def _typedef_code(typ, base=0, refs=None, kind=_kind_static, heap=False):
     '''Add new typedef for code only.
     '''
-    v = _Typedef(base=_basicsize(t, base=base),
+    v = _Typedef(base=_basicsize(typ, base=base),  # item=0,
                  refs=refs,
-                 both=False, kind=kind, type=t)
-    v.save(t, base=base, heap=heap)
+                 both=False, kind=kind, type=typ)
+    v.save(typ, base=base, heap=heap)
     return v  # for _dict_typedef
 
 
@@ -1172,73 +1418,57 @@ def _typedef_code(t, base=0, refs=None, kind=_kind_static, heap=False):
 _typedef_both(complex)
 _typedef_both(float)
 _typedef_both(int, leng=_len_int)  # see _len_int
-_typedef_both(list, refs=_seq_refs, leng=_len_list, item=_sizeof_Cvoidp)  # sizeof(PyObject*)
-_typedef_both(tuple, refs=_seq_refs, leng=_len, item=_sizeof_Cvoidp)  # sizeof(PyObject*)
+_typedef_both(list, refs=_seq_refs, leng=_len_list, item=_sizeof.Cvoidp)  # sizeof(PyObject*)
 _typedef_both(property, refs=_prop_refs)
-_typedef_both(type(Ellipsis))
-_typedef_both(type(None))
+_typedef_both(str, leng=_len_unicode, item=_sizeof.Cunicode)
+_typedef_both(_typeof(Ellipsis))
+_typedef_both(_typeof(None))
 
+t = _typedef_both(tuple, refs=_seq_refs, leng=_len, item=_sizeof.Cvoidp)  # sizeof(PyObject*)
+if _Inline0:
+    _typedef_both(_Inline0).dup(t, force=True, base=0, item=0, type=_Inline0, _get=False)  # no _sizeof.get
 # _Slots are "tuple-like", REMOVED see _Slots.__doc__
-# _typedef_both(_Slots, item=_sizeof_Cvoidp,
+# _typedef_both(_Slots, item=_sizeof.Cvoidp,
 #               leng=_len_slots,  # length less one
 #               refs=None,  # but no referents
 #               heap=True)  # plus head
 
-# dict, dictproxy, dict_proxy and other dict-like types
-_dict_typedef = _typedef_both(dict, item=_sizeof_CPyDictEntry, leng=_len_dict, refs=_dict_refs)
-# XXX any class __dict__ is <type dict_proxy> in Python 3+?
-_typedef_both(type(_Typedef.__dict__), item=_sizeof_CPyDictEntry, leng=_len_dict, refs=_dict_refs)
+# dict, mappingproxy, dict_proxy and other dict-like types
+_dict_typedef = _typedef_both(dict, item=_sizeof.CPyDictKeyEntry,
+                                    leng=_len_dict, refs=_dict_refs)
+if _Udict:
+    _typedef_both(_Udict).dup(item=_sizeof.CPyDictStrEntry, force=True)
+t = _typeof(_Typedef.__dict__)
+if t is not dict:  # <class mappingproxy> in CPy 3+?
+    _typedef_both(t).dup(force=True)
+t = getattr(Types, 'ModuleType', None)
+if t:  # <type module> is essentially a dict
+    _typedef_both(t).dup(item=_sizeof.CPyModuleObject,
+                         leng=_len_module, refs=_module_refs)
 # other dict-like classes and types may be derived or inferred,
 # provided the module and class name is listed here (see functions
 # adict, _isdictype and _infer_dict for further details)
 _dict_types = dict(UserDict=('IterableUserDict', 'UserDict'),
                    weakref =('WeakKeyDictionary', 'WeakValueDictionary'))
-try:  # <type module> is essentially a dict
-    _typedef_both(Types.ModuleType, base=_dict_typedef.base,
-                  item=_dict_typedef.item + _sizeof_CPyModuleObject,
-                  leng=_len_module, refs=_module_refs)
-except AttributeError:  # missing
-    pass
 
 
 # Newer or obsolete types
-from array import array as _array  # array type
-
-
-def _len_array(obj):
-    '''Array length (in bytes!).
-    '''
-    return len(obj) * obj.itemsize
-
-
-def _array_kwds(obj):
-    # since item size varies by the array data type, set
-    # itemsize to 1 byte and use _len_array in bytes;
-    # _getsizeof(array) returns array plus base size
-    b = max(56, _getsizeof(obj, 0) - _len_array(obj))
-    return dict(base=b, leng=_len_array, item=_sizeof_Cbyte,
-                vari='itemsize',  # array.itemsize
-                xtyp= True)  # never _getsizeof'd
-
-
-_all_lens.add(_len_array)  # type: ignore
-
 try:  # bool has non-zero __itemsize__ in 3.0
     _typedef_both(bool)
 except NameError:  # missing
     pass
 
 try:
-    _typedef_both(bytearray, item=_sizeof_Cbyte, leng=_len_bytearray)
+    _typedef_both(bytearray, item=_sizeof.Cbyte, leng=_len_bytearray)
 except NameError:  # bytearray new in 2.6, 3.0
     pass
 try:
-    if type(bytes) is not type(str):  # bytes is str in 2.6, bytes new in 2.6, 3.0
-        _typedef_both(bytes, item=_sizeof_Cbyte, leng=_len)  # bytes new in 2.6, 3.0
+    if _typeof(bytes) is not _typeof(str):  # bytes is str in 2.6, bytes new in 2.6, 3.0
+        _typedef_both(bytes, item=_sizeof.Cbyte, leng=_len)  # bytes new in 2.6, 3.0
 except NameError:  # missing
     pass
 # try:  # XXX like bytes
-#     _typedef_both(str8, item=_sizeof_Cbyte, leng=_len)  # str8 new in 2.6, 3.0
+#     _typedef_both(str8, item=_sizeof.Cbyte, leng=_len)  # str8 new in 2.6, 3.0
 # except NameError:  # missing
 #     pass
 
@@ -1247,17 +1477,17 @@ def _array_kwds(obj):
 except NameError:  # missing
     pass
 
-try:  # Exception is type in Python 3+
+try:  # Exception is type in CPy 3+
     _typedef_both(Exception, refs=_exc_refs)
 except Exception:  # missing
     pass
 
 try:
-    _typedef_both(frozenset, item=_sizeof_Csetentry, leng=_len_set, refs=_seq_refs)
+    _typedef_both(frozenset, item=_sizeof.Csetentry, leng=_len_set, refs=_seq_refs)
 except NameError:  # missing
     pass
 try:
-    _typedef_both(set, item=_sizeof_Csetentry, leng=_len_set, refs=_seq_refs)
+    _typedef_both(set, item=_sizeof.Csetentry, leng=_len_set, refs=_seq_refs)
 except NameError:  # missing
     pass
 
@@ -1272,7 +1502,7 @@ def _array_kwds(obj):
     pass
 
 try:
-    _typedef_both(type(NotImplemented))  # == Types.NotImplementedType
+    _typedef_both(_typeof(NotImplemented))  # == Types.NotImplementedType
 except NameError:  # missing
     pass
 
@@ -1292,12 +1522,11 @@ def _array_kwds(obj):
     def _isnumpy(obj):
         '''Return True for a NumPy arange, array, matrix, memmap, ndarray, etc. instance.
         '''
-        # not every numpy obj  hasattr(obj, 'base')
-        try:
+        try:  # not every numpy obj  hasattr(obj, 'base')
             if hasattr(obj, 'dtype') and hasattr(obj, 'itemsize') \
-                    and hasattr(obj, 'nbytes'):
+                                     and hasattr(obj, 'nbytes'):
                 return (_moduleof(_classof(obj)).startswith('numpy') or
-                        _moduleof(type(obj)).startswith('numpy'))
+                        _moduleof(_typeof( obj)).startswith('numpy'))
         except (AttributeError, OSError, ValueError):  # on iOS/Pythonista
             pass
         return False
@@ -1310,17 +1539,17 @@ def _len_numpy(obj):
     def _len_numpy_memmap(obj):
         '''Approximate NumPy memmap in-memory size (in bytes!).
         '''
-        nb = int(obj.nbytes * _amapped)
+        nb = int(obj.nbytes * _sizeof.amapped)
         # round up to multiple of virtual memory page size
         return ((nb + _PAGESIZE - 1) // _PAGESIZE) * _PAGESIZE
 
     def _numpy_kwds(obj):
-        t = type(obj)
+        t = _typeof(obj)
         # .nbytes is included in sys.sizeof size for most numpy
         # objects except for numpy.memmap (and for the latter it
         # is the length of the file to be memory-mapped which by
         # default is the file size less the offset specified)
-        _i, _v = _sizeof_Cbyte, 'itemsize'
+        _i, _v = _sizeof.Cbyte, 'itemsize'
         if t is _numpy_memmap:  # isinstance(obj, _numpy_memmap)
             b, _l, nb = 144, _len_numpy_memmap, 0
         elif t.__name__ in ('str', 'str_'):  # numpy.str Deprecated!
@@ -1329,22 +1558,22 @@ def _numpy_kwds(obj):
             _l = _len
             nb = _l(obj)
             _i =  obj.nbytes // nb
-            _v = _Not_vari
+            _v = _Non_vari
         else:  # XXX 96, 128, 144 typical?
-            b, _l, nb =  96, _len_numpy, obj.nbytes
+            b, _l, nb = 96, _len_numpy, obj.nbytes
         # since item size depends on the nympy data type, set
         # itemsize to 1 byte and use _len_numpy in bytes; note,
         # function itemsize returns the actual size in bytes,
         # function leng returns the length in number of items
-        return dict(base=_getsizeof(obj, b + nb) - nb,
+        return dict(base=max(_sizeof.get(obj, b + nb) - nb, b),
                     item=_i,  # not obj.itemsize!
                     leng=_l,
                     refs=_numpy_refs,
                     vari=_v,  # numpy.itemsize
-                    xtyp= True)  # never _getsizeof'd
+                    _get= False)  # no _sizeof.get
 
     def _numpy_refs(obj, named):
-        '''Return the .base object for NumPy slices, views, etc.
+        '''Return the .base object for NumPy slices, tile, views, etc.
         '''
         return _refs(obj, named, 'base')
 
@@ -1371,26 +1600,26 @@ def _isnumpy(unused):  # PYCHOK expected
     pass
 
 try:
-    _typedef_both(slice, item=_sizeof_Cvoidp, leng=_len_slice)  # XXX worst-case itemsize?
+    _typedef_both(slice, item=_sizeof.Cvoidp, leng=_len_slice)  # XXX worst-case itemsize?
 except NameError:  # missing
     pass
 
 try:
     from os import stat
-    _typedef_both(type(stat(curdir)), refs=_stat_refs)  # stat_result
+    _typedef_both(_typeof(stat(curdir)), refs=_stat_refs)  # stat_result
 except ImportError:  # missing
     pass
 
 try:
     from os import statvfs
-    _typedef_both(type(statvfs(curdir)), refs=_statvfs_refs,  # statvfs_result
-                  item=_sizeof_Cvoidp, leng=_len)
+    _typedef_both(_typeof(statvfs(curdir)), refs=_statvfs_refs,  # statvfs_result
+                   item=_sizeof.Cvoidp, leng=_len)
 except ImportError:  # missing
     pass
 
 try:
-    from struct import Struct  # only in Python 2.5 and 3.0
-    _typedef_both(Struct, item=_sizeof_Cbyte, leng=_len_struct)  # len in bytes
+    from struct import Struct  # only in CPy 2.5 and 3.0
+    _typedef_both(Struct, item=_sizeof.Cbyte, leng=_len_struct)  # len in bytes
 except ImportError:  # missing
     pass
 
@@ -1399,8 +1628,6 @@ def _isnumpy(unused):  # PYCHOK expected
 except AttributeError:  # missing
     pass
 
-_typedef_both(str, leng=_len_unicode, item=_sizeof_Cunicode)
-
 try:  # <type 'KeyedRef'>
     _typedef_both(Weakref.KeyedRef, refs=_weak_refs, heap=True)  # plus head
 except AttributeError:  # missing
@@ -1444,56 +1671,61 @@ def _isnumpy(unused):  # PYCHOK expected
 except AttributeError:  # missing
     pass
 
-# any type-specific iterators
-s = [_items({}), _keys({}), _values({})]
-try:  # reversed list and tuples iterators
-    s.extend([reversed([]), reversed(())])
-except NameError:  # missing
-    pass
 
-try:  # callable-iterator
-    from re import finditer
-    s.append(finditer(_NN, _NN))
-    del finditer
-except ImportError:  # missing
-    pass
+def _iter_types(**kwds):  # type-specific iterators
+    s = [_items({}), _keys({}), _values({})]
+    try:  # reversed list and tuple iterators
+        s.extend([reversed([]), reversed(())])
+    except NameError:  # missing
+        pass
 
-for t in _values(_typedefs):
-    if t.type and t.leng:
-        try:  # create an (empty) instance
-            s.append(t.type())
-        except TypeError:
-            pass
-for t in s:
-    try:
-        i = iter(t)
-        _typedef_both(type(i), leng=_len_iter, refs=_iter_refs, item=0)  # no itemsize!
-    except (KeyError, TypeError):  # ignore non-iterables, duplicates, etc.
+    try:  # callable-iterator
+        from re import finditer
+        s.append(finditer(_NN, _NN))
+        del finditer
+    except ImportError:  # missing
         pass
-del i, s, t
 
+    for t in _values(_typedefs):
+        if t.type and t.leng:
+            try:  # create an (empty) instance
+                s.append(t.type())
+            except TypeError:
+                pass
 
-def _typedef(obj, derive=False, frames=False, infer=False):  # MCCABE 25
+    for t in s:
+        try:
+            t = _typeof(iter(t))
+            yield _typedef_both(t, **kwds)
+        except (KeyError, TypeError):  # ignore non-iterables, duplicates, etc.
+            pass
+
+for t in _iter_types(leng=_len_iter, refs=_iter_refs, item=0):  # PYCHOK no itemsize!
+    pass  # _printf('%s: %s', t.type, str(t))
+del t, _iter_types
+
+
+def _typedef(obj, derive=False, frames=False, infer=False):  # MCCABE 22  # in cached_asizeof.py
     '''Create a new typedef for an object.
     '''
-    t =  type(obj)
+    t = _typeof(obj)
     v = _Typedef(base=_basicsize(t, obj=obj),
                  kind=_kind_dynamic, type=t)
 #   _printf('new %r %r/%r %s', t, _basicsize(t), _itemsize(t), _repr(dir(obj)))
     if ismodule(obj):  # handle module like dict
-        v.dup(item=_dict_typedef.item + _sizeof_CPyModuleObject,
+        v.dup(item=_sizeof.CPyModuleObject,
               leng=_len_module,
               refs=_module_refs)
     elif _isframe(obj):
-        v.set(base=_basicsize(t, base=_sizeof_CPyFrameObject, obj=obj),
+        v.set(base=_basicsize(t, base=_sizeof.CPyFrameObject, obj=obj),
               item=_itemsize(t),
               leng=_len_frame,
               refs=_frame_refs)
         if not frames:  # ignore frames
             v.set(kind=_kind_ignored)
     elif iscode(obj):
-        v.set(base=_basicsize(t, base=_sizeof_CPyCodeObject, obj=obj),
-              item=_sizeof_Cvoidp,
+        v.set(base=_basicsize(t, base=_sizeof.CPyCodeObject, obj=obj),
+              item=_sizeof.Cvoidp,
               leng=_len_code,
               refs=_co_refs,
               both=False)  # code only
@@ -1518,8 +1750,6 @@ def _typedef(obj, derive=False, frames=False, infer=False):  # MCCABE 25
                   refs=_inst_refs)  # not code only!
         else:
             v.set(both=False)  # code only
-    elif _issubclass(t, dict):
-        v.dup(kind=_kind_derived)
     elif _isdictype(obj) or (infer and _infer_dict(obj)):
         v.dup(kind=_kind_inferred)
     elif _iscell(obj):
@@ -1530,7 +1760,7 @@ def _typedef(obj, derive=False, frames=False, infer=False):  # MCCABE 25
         v.set(**_numpy_kwds(obj))
     elif isinstance(obj, _array):
         v.set(**_array_kwds(obj))
-    elif _isignored(obj):
+    elif _isignored(obj) or _isignored(t):
         v.set(kind=_kind_ignored)
     else:  # assume an instance of some class
         if derive:
@@ -1551,7 +1781,7 @@ def _typedef(obj, derive=False, frames=False, infer=False):  # MCCABE 25
     return v
 
 
-class _Prof(object):
+class _Prof(object):  # never sized
     '''Internal type profile class.
     '''
     high   = 0      # largest size
@@ -1571,7 +1801,7 @@ def __cmp__(self, other):
             return +1
         return 0
 
-    def __lt__(self, other):  # for Python 3+
+    def __lt__(self, other):  # for CPy 3+
         return self.__cmp__(other) < 0
 
     def format(self, clip=0, grand=None):
@@ -1587,9 +1817,9 @@ def format(self, clip=0, grand=None):
         t = _SI2(self.total)
         if grand:
             t += ' (%s)' % _p100(self.total, grand, prec=0)
-        return dict(avg=_SI2(a), high=_SI2(self.high),
-                    lengstr=_lengstr(o), obj=_repr(o, clip=clip),
-                    plural=p, total=t)
+        s = _lengstr(o)
+        return dict(avg=_SI2(a), high=_SI2(self.high), lengstr=s,
+                    obj=_repr(o, clip=clip), plural=p, total=t)
 
     def update(self, obj, size):
         '''Update this profile.
@@ -1604,7 +1834,13 @@ def update(self, obj, size):
                 self.objref, self.weak = obj, False
 
 
-class _Rank(object):
+class _Profs(dict):  # never sized
+    '''Container of _Prof instances.
+    '''
+    pass
+
+
+class _Rank(object):  # never sized
     '''Internal largest object class.
     '''
     deep   = 0      # recursion depth
@@ -1639,23 +1875,30 @@ def _ix(_id):  # id or parent_id
                _repr(o, clip=clip), _lengstr(o), _ix(self.id), d, p)
 
 
-class _Seen(dict):
+class _Ranks(list):  # never sized
+    '''Container of _Rank instances.
+    '''
+    pass
+
+
+class _Seen(dict):  # never sized, in cached_asizeof.py
     '''Internal obj visits counter.
     '''
     def again(self, key):
         try:
-            s = self[key] + 1
+            n = self[key] + 1
         except KeyError:
-            s = 1
-        if s > 0:
-            self[key] = s
+            n = 1
+        if n > 0:
+            self[key] = n
+        return n
 
 
 # Public classes
 
-class Asized(object):
+class Asized(object):  # never sized
     '''Stores the results of an **asized** object in the following
-       4 attributes:
+       5 attributes:
 
         *size* -- total size of the object (including referents)
 
@@ -1664,18 +1907,26 @@ class Asized(object):
         *name* -- name or ``repr`` of the object
 
         *refs* -- tuple containing an **Asized** instance for each referent
+
+        *tobj* -- the object or its type.
     '''
-    __slots__ = ('flat', 'name', 'refs', 'size')
+    # __slots__ = ('flat', 'name', 'refs', 'size', 'tobj')
+    refs = ()  # empty, _NamedRefs()
 
-    def __init__(self, size, flat, refs=(), name=None):
+    def __init__(self, size, flat, refs=None, name=None, tobj=None):
         self.size = size  # total size
         self.flat = flat  # flat size
+        self.refs = tuple(refs) if refs else Asized.refs  # and isinstance(refs, _NamedRefs)
         self.name = name  # name, repr or None
-        self.refs = tuple(refs)
+        self.tobj = tobj if tobj is None else _typeof(tobj)
 
     def __str__(self):
-        return 'size %r, flat %r, refs[%d], name %r' % (
-            self.size, self.flat, len(self.refs), self.name)
+        s = 'size %r, flat %r, refs[%d]' % (self.size, self.flat, len(self.refs))
+        if self.name:
+            s += ', name %r' % (self.name,)
+        if self.tobj:
+            s += ', type %s' % (_repr(self.tobj),)
+        return s
 
     def format(self, format='%(name)s size=%(size)d flat=%(flat)d',
                      depth=-1, order_by='size', indent=_NN):
@@ -1696,11 +1947,11 @@ def format(self, format='%(name)s size=%(size)d flat=%(flat)d',
         t = indent + (format % dict(size=self.size, flat=self.flat,
                                     name=self.name))
         if depth and self.refs:
-            rs = sorted(self.refs, key=lambda x: getattr(x, order_by),
+            rs = sorted(self.refs, key=lambda r: getattr(r, order_by),
                                    reverse=order_by in ('size', 'flat'))
             rs = [r.format(format=format, depth=depth-1, order_by=order_by,
                            indent=indent+'    ') for r in rs]
-            t = '\n'.join([t] + rs)
+            t = _NL.join([t] + rs)
         return t
 
     def get(self, name, dflt=None):
@@ -1712,11 +1963,11 @@ def get(self, name, dflt=None):
         return dflt
 
 
-class Asizer(object):
+class Asizer(object):  # never sized, in cached_asizeof.py
     '''Sizer state and options to accumulate sizes.
     '''
-    _above_  = 1024   # rank only objs of size 1K+
-    _align_  = 8  # alignment, power-of-2
+    _above_  = 1024  # rank only objs of size 1K+
+    _align_  = 8     # alignment, power-of-2
     _clip_   = 80
     _code_   = False
     _cutoff_ = 0  # in percent
@@ -1727,25 +1978,26 @@ class Asizer(object):
     _limit_  = 100
     _stats_  = 0
 
+    _debugs  = 0  # see ._clear, _reset, ._sizer
     _depth   = 0  # deepest recursion
     _excl_d  = None  # {}
     _ign_d   = _kind_ignored
-    _incl    = _NN  # or ' (incl. code)'
     _mask    = 7   # see _align_
     _missed  = 0   # due to errors
     _profile = False  # no profiling
-    _profs   = None   # {}
+    _profs   = None   # _Profs()
     _ranked  = 0
-    _ranks   = []     # type: List[_Rank] # sorted by decreasing size
-    _seen    = None   # {}
+    _ranks   = []     # type: List[_Ranks] # sorted by decreasing size
+    _seen    = None   # _Seen()
     _stream  = None   # I/O stream for printing
     _total   = 0      # total size
 
     def __init__(self, **opts):
         '''New **Asizer** accumulator.
 
-           See this module documentation for more details.
            See method **reset** for all available options and defaults.
+
+           See this module documentation for more details.
         '''
         self._excl_d = {}
         self.reset(**opts)
@@ -1761,51 +2013,70 @@ def _clear(self):
         '''Clear state.
         '''
         self._depth = 0   # recursion depth reached
-        self._incl = _NN  # or ' (incl. code)'
         self._missed = 0   # due to errors
         self._profile = False
-        self._profs = {}
+        self._profs = _Profs()
         self._ranked = 0
-        self._ranks = []
+        self._ranks = _Ranks()
         self._seen = _Seen()
         self._total = 0   # total size
         for k in _keys(self._excl_d):
             self._excl_d[k] = 0
         # don't size, profile or rank private, possibly large objs
         m = sys.modules[__name__]
-        self.exclude_objs(self, self._excl_d, self._profs, self._ranks,
-                                self._seen, m, m.__dict__, m.__doc__,
+        self.exclude_objs(self, self._excl_d, m, m.__dict__, m.__doc__,
+                                self._profs, self._ranks, self._seen,
                                _typedefs)
 
-    def _nameof(self, obj):
+    @property_RO
+    def _incl_d(self):
+        '''Return "incl. (byte)code" or "".
+        '''
+        return ' (incl. code)' if self.code else _NN
+
+    def _nameof(self, obj, _attr=_NN):
         '''Return the object's name.
         '''
-        return _nameof(obj, _NN) or self._repr(obj)
+        return _nameof(obj, _NN) or _attr or self._repr(obj)
 
     def _prepr(self, obj):
         '''Like **prepr()**.
         '''
         return _prepr(obj, clip=self._clip_)
 
+    def _printd(self, d, i, **kwds):
+        '''Print a *_debugsizer* step.
+        '''
+        d  =  max(0, d)
+        n  =  self._seen.get(i, 0)
+        t  = _kwdstr(deep=d, seen=n, **kwds).replace('function ', _NN)
+        d *= ' '  # indentation
+        self._printf(' %s%+d id=%#x, %s', d, self._debugs, i, t)
+        self._debugs += 1
+
     def _printf(self, fmt, *args, **print3options):
         '''Print to sys.stdout or the configured stream if any is
            specified and if the file keyword argument is not already
            set in the **print3options** for this specific call.
         '''
         if self._stream and not print3options.get('file', None):
-            if args:
-                fmt = fmt % args
-            _printf(fmt, file=self._stream, **print3options)
-        else:
-            _printf(fmt, *args, **print3options)
+            print3options = dict(file=self._stream, **print3options)
+        _printf(fmt, *args, **print3options)
+
+    def _printo(self, w, num, adj, **print3options):
+        '''Print an *objects* summary stat.
+        '''
+        s = _plural(num)
+        self._printf('%*d object%s %s', w, num, s, adj, **print3options)
 
     def _prof(self, key):
         '''Get _Prof object.
         '''
-        p = self._profs.get(key, None)
-        if not p:
+        try:
+            p = self._profs[key]
+        except KeyError:
             self._profs[key] = p = _Prof()
-            self.exclude_objs(p)  # XXX superfluous?
+            # self.exclude_objs(p)  # superfluous
         return p
 
     def _rank(self, key, obj, size, deep, pid):
@@ -1823,7 +2094,7 @@ def _rank(self, key, obj, size, deep, pid):
         if i < 100:
             r = _Rank(key, obj, size, deep, pid)
             rs.insert(i, r)
-            self.exclude_objs(r)  # XXX superfluous?
+            # self.exclude_objs(r)  # superfluous
             while len(rs) > 100:
                 rs.pop()
             # self._ranks[:] = rs[:100]
@@ -1834,25 +2105,31 @@ def _repr(self, obj):
         '''
         return _repr(obj, clip=self._clip_)
 
-    def _sizer(self, obj, pid, deep, sized):  # MCCABE 19
+    def _sizer(self, obj, pid, deep, sized, **_attr):  # MCCABE 18
         '''Size an object, recursively.
         '''
         s, f, i = 0, 0, id(obj)
-        if i not in self._seen:
+        # _Inline0 instances are re-used and the same id()
+        # may be seen again containing different items
+        if _typeof(obj) is _Inline0:  # skip self._seen
+            deep -= 1  # ignore the _Inline0 depth
+#           sized = None  # accumulate sizes only
+        elif i not in self._seen:
             self._seen[i] = 1
         elif deep or self._seen[i]:
-            # skip obj if seen before
-            # or if ref of a given obj
-            if self._seen[i]:
-                self._seen.again(i)
+            # skip obj seen before or
+            # if ref of a given obj
+            self._seen.again(i)  # type: int
+            if self._debugs:
+                self._printd(deep, i, type=_typeof(obj), **_attr)
             if sized:
-                s = sized(s, f, name=self._nameof(obj))
+                s = sized(s, f, name=self._nameof(obj), tobj=obj)
                 self.exclude_objs(s)
             return s  # zero
         else:  # deep == seen[i] == 0
-            self._seen.again(i)
+            self._seen.again(i)  # type: int
         try:
-            k, rs = _objkey(obj), []
+            k, rs = _objkey(obj), None
             if k in self._excl_d:
                 self._excl_d[k] += 1
             else:
@@ -1863,86 +2140,103 @@ def _sizer(self, obj, pid, deep, sized):  # MCCABE 19
                                                       infer=self._infer_)
                 if (v.both or self._code_) and v.kind is not self._ign_d:
                     s = f = v.flat(obj, self._mask)  # flat size
-                    if self._profile:
+                    if self._debugs:
+                        self._printd(deep, i, flat=s, **v.kwds4(obj, **_attr))
+                    if self._profile and v.type is not _Inline0:
                         # profile based on *flat* size
                         self._prof(k).update(obj, s)
-                    # recurse, but not for nested modules
-                    if v.refs and deep < self._limit_ \
-                              and not (deep and ismodule(obj)):
-                        # add sizes of referents
-                        z, d = self._sizer, deep + 1
-                        if sized and deep < self._detail_:
-                            # use named referents
-                            self.exclude_objs(rs)
-                            for o in v.refs(obj, True):
-                                if isinstance(o, _NamedRef):
-                                    r = z(o.ref, i, d, sized)
-                                    r.name = o.name
-                                else:
-                                    r = z(o, i, d, sized)
-                                    r.name = self._nameof(o)
-                                rs.append(r)
-                                s += r.size
-                        else:  # just size and accumulate
-                            for o in v.refs(obj, False):
-                                s += z(o, i, d, None)
-                        # deepest recursion reached
-                        if self._depth < d:
-                            self._depth = d
+                    # recurse to refs, but not for nested modules
+                    if v.refs and  deep < self._limit_ and \
+                              not (deep and ismodule(obj)):
+                        s, rs = self._sizerefs2(obj, i, deep, sized, s, rs, v)
                 if self._stats_ and s > self._above_ > 0:
                     # rank based on *total* size
                     self._rank(k, obj, s, deep, pid)
         except RuntimeError:  # XXX RecursionLimitExceeded:
             self._missed += 1
+        if self._debugs and s != f != 0:
+            self._printd(deep, i, size=s, **v.kwds4(obj, **_attr))
         if not deep:
             self._total += s  # accumulate
         if sized:
-            s = sized(s, f, name=self._nameof(obj), refs=rs)
+            s = sized(s, f, name=self._nameof(obj, **_attr), refs=rs, tobj=obj)
             self.exclude_objs(s)
         return s
 
-    def _sizes(self, objs, sized=None):
-        '''Return the size or an **Asized** instance for each
-           given object plus the total size.  The total includes
-           the size of duplicates only once.
+    def _sizerefs2(self, obj, pid, deep, sized, s, rs, v):
+        '''Size the referents of an object.
         '''
-        self.exclude_refs(*objs)  # skip refs to objs
-        s, t = {}, []
-        self.exclude_objs(s, t)
-        for o in objs:
-            i = id(o)
-            if i in s:  # duplicate
-                self._seen.again(i)
-            else:
-                s[i] = self._sizer(o, 0, 0, sized)
-            t.append(s[i])
-        return tuple(t)
+        z, d = self._sizer, (deep + 1)
+        if sized and deep < self._detail_:
+            # use named referents
+            rs = _NamedRefs()
+            for o in v.refs(obj, True):
+                if isinstance(o, _NamedRef):
+                    r = z(o.ref, pid, d, sized, _attr=o.name)
+                    r.name = o.name
+                else:
+                    r = z(o, pid, d, sized)
+                    r.name = self._nameof(o)
+                rs.append(r)
+                s += r.size
+        elif self._debugs:
+            for o in v.refs(obj, True):
+                if isinstance(o, _NamedRef):
+                    s += z(o.ref, pid, d, None, _attr=o.name)
+                else:
+                    s += z(o, pid, d, None)
+        else:  # just size and accumulate
+            for o in v.refs(obj, False):
+                s += z(o, pid, d, None)  # , sized?
+        # deepest recursion reached
+        if self._depth < d:
+            self._depth = d
+        return s, rs
+
+    def _sizes(self, objs, sized):
+        '''Yield the size or **Asized** for each given object.
+        '''
+        if objs:
+            s = {}
+            self.exclude_objs(s)
+            self.exclude_refs(*objs)  # skip refs to objs
+
+            for o in objs:
+                i = id(o)
+                if i in s:
+                    self._seen.again(i)
+                else:
+                    s[i] = self._sizer(o, 0, 0, sized)
+                yield s[i]
 
-    @property
+    @property_RO
     def above(self):
         '''Get the large object size threshold (int).
         '''
         return self._above_
 
-    @property
+    @property_RO
     def align(self):
         '''Get the size alignment (int).
         '''
         return self._align_
 
     def asized(self, *objs, **opts):
-        '''Size each object and return an **Asized** instance with
-           size information and referents up to the given detail
-           level (and with modified options, see method **set**).
+        '''Return a tuple with an **Asized** instance for each given
+           object with size information and referents up to the given
+           detaillevel (and with modified options, see method **set**).
 
-           If only one object is given, the return value is the
-           **Asized** instance for that object.  The **Asized** size
-           of duplicate and ignored objects will be zero.
+           If only one *objs* is given, only **Asized** instance for
+           that object is returned.
+
+           Duplicate *objs* will have the same **Asized** instance,
+           ignored *objs* will have an **Asized** instance with
+           size zero.
         '''
         if opts:
             self.set(**opts)
-        t = self._sizes(objs, Asized)
-        return t[0] if len(t) == 1 else t
+        t = tuple(self._sizes(objs, Asized))
+        return t if len(t) != 1 else t[0]
 
     def asizeof(self, *objs, **opts):
         '''Return the combined size of the given objects
@@ -1954,46 +2248,47 @@ def asizeof(self, *objs, **opts):
         return sum(self._sizer(o, 0, 0, None) for o in objs)
 
     def asizesof(self, *objs, **opts):
-        '''Return the individual sizes of the given objects
-           (with modified options, see method  **set**).
+        '''Return the size of each given object (with modified
+           options, see method  **set**).
 
-           The size of duplicate and ignored objects will be zero.
+           Duplicate *objs* will have the same size (instance),
+           ignored *objs* will have size zero.
         '''
         if opts:
             self.set(**opts)
-        return self._sizes(objs, None)
+        return tuple(self._sizes(objs, None))
 
-    @property
+    @property_RO
     def clip(self):
         '''Get the clipped string length (int).
         '''
         return self._clip_
 
-    @property
+    @property_RO
     def code(self):
-        '''Size (byte) code (bool).
+        '''Size the (byte) code (bool).
         '''
         return self._code_
 
-    @property
+    @property_RO
     def cutoff(self):
         '''Stats cutoff (int).
         '''
         return self._cutoff_
 
-    @property
+    @property_RO
     def derive(self):
         '''Derive types (bool).
         '''
         return self._derive_
 
-    @property
+    @property_RO
     def detail(self):
         '''Get the detail level for **Asized** refs (int).
         '''
         return self._detail_
 
-    @property
+    @property_RO
     def duplicate(self):
         '''Get the number of duplicate objects seen so far (int).
         '''
@@ -2003,6 +2298,7 @@ def exclude_objs(self, *objs):
         '''Exclude the specified objects from sizing, profiling and ranking.
         '''
         for o in objs:
+            # if _typeof(o).__module__ is not __name__:
             self._seen.setdefault(id(o), -1)
 
     def exclude_refs(self, *objs):
@@ -2027,37 +2323,37 @@ def exclude_types(self, *objs):
                 if t and t not in self._excl_d:
                     self._excl_d[t] = 0
 
-    @property
+    @property_RO
     def excluded(self):
         '''Get the types being excluded (tuple).
         '''
         return tuple(_keys(self._excl_d))
 
-    @property
+    @property_RO
     def frames(self):
         '''Ignore stack frames (bool).
         '''
         return self._frames_
 
-    @property
+    @property_RO
     def ignored(self):
         '''Ignore certain types (bool).
         '''
-        return True if self._ign_d else False
+        return bool(self._ign_d)
 
-    @property
+    @property_RO
     def infer(self):
         '''Infer types (bool).
         '''
         return self._infer_
 
-    @property
+    @property_RO
     def limit(self):
         '''Get the recursion limit (int).
         '''
         return self._limit_
 
-    @property
+    @property_RO
     def missed(self):
         '''Get the number of objects missed due to errors (int).
         '''
@@ -2078,12 +2374,12 @@ def print_largest(self, w=0, cutoff=0, **print3options):
         n = min(len(self._ranks), max(c, 0))
         s = self._above_
         if n > 0 and s > 0:
-            self._printf('%s%*d largest object%s (of %d over %d bytes%s)', linesep,
-                          w, n, _plural(n), self._ranked, s, _SI(s), **print3options)
+            self._printf('%s%*d largest object%s (of %d over %s)', _NL, w, n,
+                        _plural(n), self._ranked, _SI3(0, s), **print3options)
             id2x = dict((r.id, i) for i, r in enumerate(self._ranks))
             for r in self._ranks[:n]:
-                s, t = r.size, r.format(self._clip_, id2x)
-                self._printf('%*d bytes%s: %s', w, s, _SI(s), t, **print3options)
+                t = r.format(self._clip_, id2x)
+                self._printf('%s: %s', _SI3(w, r.size), t, **print3options)
 
     def print_profiles(self, w=0, cutoff=0, **print3options):
         '''Print the profiles above *cutoff* percentage.
@@ -2096,30 +2392,32 @@ def print_profiles(self, w=0, cutoff=0, **print3options):
 
                 *print3options* -- some keyword arguments, like Python 3+ print
         '''
+        z = len(self._profs)
         # get the profiles with non-zero size or count
         t = [(v, k) for k, v in _items(self._profs) if v.total > 0 or v.number > 1]
-        if (len(self._profs) - len(t)) < 9:  # just show all
+        if (z - len(t)) < 9:  # just show all
             t = [(v, k) for k, v in _items(self._profs)]
         if t:
-            s = _NN
             if self._total:
-                s = ' (% of grand total)'
                 c = int(cutoff) if cutoff else self._cutoff_
                 C = int(c * 0.01 * self._total)
+                s = ' (% of grand total)'
             else:
                 C = c = 0
-            self._printf('%s%*d profile%s:  total%s, average, and largest flat size%s:  largest object',
-                         linesep, w, len(t), _plural(len(t)), s, self._incl, **print3options)
+                s = _NN
             r = len(t)
-            t = [(v, self._prepr(k)) for v, k in t]  # replace types with str for Python 3.11+
+            self._printf('%s%*d profile%s:  total%s, average, and largest flat size%s:  largest object',
+                         _NL, w, r, _plural(r), s, self._incl_d, **print3options)
+            f = 'object%(plural)s:  %(total)s, %(avg)s, %(high)s:  %(obj)s%(lengstr)s'
+            t = [(v, self._prepr(k)) for v, k in t]  # replace types with str for CPy 3.11+
             for v, k in sorted(t, reverse=True):
-                s = 'object%(plural)s:  %(total)s, %(avg)s, %(high)s:  %(obj)s%(lengstr)s' % v.format(self._clip_, self._total)
+                s = f % v.format(self._clip_, self._total)
                 self._printf('%*d %s %s', w, v.number, k, s, **print3options)
                 r -= 1
                 if r > 1 and v.total < C:
                     self._printf('%+*d profiles below cutoff (%.0f%%)', w, r, c)
                     break
-            z = len(self._profs) - len(t)
+            z -= len(t)
             if z > 0:
                 self._printf('%+*d %r object%s', w, z, 'zero', _plural(z), **print3options)
 
@@ -2153,19 +2451,19 @@ def print_stats(self, objs=(), opts={}, sized=(), sizes=(), stats=3, **print3opt
             if sized and objs:
                 n = len(objs)
                 if n > 1:
-                    self._printf('%sasized(...%s%s) ...', linesep, c, o, **print3options)
-                    for i in range(n):  # no enumerate in Python 2.2.3
+                    self._printf('%sasized(...%s%s) ...', _NL, c, o, **print3options)
+                    for i in range(n):  # no enumerate in CPy 2.2.3
                         self._printf('%*d: %s', w - 1, i, sized[i], **print3options)
                 else:
-                    self._printf('%sasized(%s): %s', linesep, o, sized, **print3options)
+                    self._printf('%sasized(%s): %s', _NL, o, sized, **print3options)
             elif sizes and objs:
-                self._printf('%sasizesof(...%s%s) ...', linesep, c, o, **print3options)
+                self._printf('%sasizesof(...%s%s) ...', _NL, c, o, **print3options)
                 for z, o in zip(sizes, objs):
-                    self._printf('%*d bytes%s%s:  %s', w, z, _SI(z), self._incl, self._repr(o), **print3options)
+                    self._printf('%s%s:  %s', _SI3(w, z), self._incl_d, self._repr(o), **print3options)
             else:
                 if objs:
                     t = self._repr(objs)
-                self._printf('%sasizeof(%s%s%s) ...', linesep, t, c, o, **print3options)
+                self._printf('%sasizeof(%s%s%s) ...', _NL, t, c, o, **print3options)
             # print summary
             self.print_summary(w=w, objs=objs, **print3options)
             # for backward compatibility, cutoff from fractional stats
@@ -2187,24 +2485,18 @@ def print_summary(self, w=0, objs=(), **print3options):
 
                 *print3options* -- some keyword arguments, like Python 3+ print
         '''
-        self._printf('%*d bytes%s%s', w, self._total, _SI(self._total), self._incl, **print3options)
+        self._printf('%s%s', _SI3(w, self._total), self._incl_d, **print3options)
         if self._mask:
             self._printf('%*d byte aligned', w, self._mask + 1, **print3options)
-        self._printf('%*d byte sizeof(void*)', w, _sizeof_Cvoidp, **print3options)
-        n = len(objs or ())
-        self._printf('%*d object%s %s', w, n, _plural(n), 'given', **print3options)
-        n = self.sized
-        self._printf('%*d object%s %s', w, n, _plural(n), 'sized', **print3options)
+        self._printf('%*d byte sizeof(void*)', w, _sizeof.Cvoidp, **print3options)
+        self._printo(w, len(objs or ()), 'given', **print3options)
+        self._printo(w, self.sized, 'sized', **print3options)
         if self._excl_d:
             n = sum(_values(self._excl_d))
-            self._printf('%*d object%s %s', w, n, _plural(n), 'excluded', **print3options)
-        n = self.seen
-        self._printf('%*d object%s %s', w, n, _plural(n), 'seen', **print3options)
-        n = self.ranked
-        if n > 0:
-            self._printf('%*d object%s %s', w, n, _plural(n), 'ranked', **print3options)
-        n = self.missed
-        self._printf('%*d object%s %s', w, n, _plural(n), 'missed', **print3options)
+            self._printo(w, n, 'excluded', **print3options)
+        self._printo(w, self.seen, 'seen', **print3options)
+        self._printo(w, self.ranked, 'ranked', **print3options)
+        self._printo(w, self.missed, 'missed', **print3options)
         n = self.duplicate
         self._printf('%*d duplicate%s', w, n, _plural(n), **print3options)
         if self._depth > 0:
@@ -2219,31 +2511,32 @@ def print_typedefs(self, w=0, **print3options):
 
                 *print3options* -- some keyword arguments, like Python 3+ print
         '''
-        for k in _all_kinds:
-            # XXX Python 3+ doesn't sort type objects
+        for k in sorted(_all_kinds):
+            # XXX CPy 3+ doesn't sort type objects
+            c = self._code_
             t = [(self._prepr(a), v) for a, v in _items(_typedefs)
-                                      if v.kind == k and (v.both or self._code_)]
+                                              if v.kind == k and (v.both or c)]
             if t:
                 self._printf('%s%*d %s type%s:  basicsize, itemsize, _len_(), _refs()',
-                             linesep, w, len(t), k, _plural(len(t)), **print3options)
+                             _NL, w, len(t), k, _plural(len(t)), **print3options)
                 for a, v in sorted(t):
                     self._printf('%*s %s:  %s', w, _NN, a, v, **print3options)
         # dict and dict-like classes
         t = sum(len(v) for v in _values(_dict_types))
         if t:
-            self._printf('%s%*d dict/-like classes:', linesep, w, t, **print3options)
+            self._printf('%s%*d dict/-like classes:', _NL, w, t, **print3options)
             for m, v in _items(_dict_types):
                 self._printf('%*s %s:  %s', w, _NN, m, self._prepr(v), **print3options)
 
-    @property
+    @property_RO
     def ranked(self):
-        '''Get the number objects ranked by size so far (int).
+        '''Get the number of objects ranked by size so far (int).
         '''
         return self._ranked
 
     def reset(self, above=1024, align=8, clip=80, code=False,  # PYCHOK too many args
                     cutoff=10, derive=False, detail=0, frames=False, ignored=True,
-                    infer=False, limit=100, stats=0, stream=None, **extra):
+                    infer=False, limit=100, stats=0, stream=None, **extras):
         '''Reset sizing options, state, etc. to defaults.
 
            The available options and default values are:
@@ -2274,9 +2567,7 @@ def reset(self, above=1024, align=8, clip=80, code=False,  # PYCHOK too many arg
 
            See function **asizeof** for a description of the options.
         '''
-        if extra:
-            raise _OptionError(self.reset, Error=KeyError, **extra)
-        # options
+        self._xtras(extras, self.reset)
         self._above_ = above
         self._align_ = align
         self._clip_ = clip
@@ -2289,22 +2580,20 @@ def reset(self, above=1024, align=8, clip=80, code=False,  # PYCHOK too many arg
         self._limit_ = limit
         self._stats_ = stats
         self._stream = stream
-        if ignored:
-            self._ign_d = _kind_ignored
-        else:
-            self._ign_d = None
+        self._ign_d = _kind_ignored if ignored else None
         # clear state
         self._clear()
         self.set(align=align, code=code, cutoff=cutoff, stats=stats)
+        return self
 
-    @property
+    @property_RO
     def seen(self):
         '''Get the number objects seen so far (int).
         '''
         return sum(v for v in _values(self._seen) if v > 0)
 
     def set(self, above=None, align=None, code=None, cutoff=None,
-                  frames=None, detail=None, limit=None, stats=None):
+                  frames=None, detail=None, limit=None, stats=None, **extras):
         '''Set some sizing options.  See also **reset**.
 
            The available options are:
@@ -2327,22 +2616,15 @@ def set(self, above=None, align=None, code=None, cutoff=None,
 
            Any options not set remain unchanged from the previous setting.
         '''
+        self._xtras(extras, self.set)
         # adjust
         if above is not None:
             self._above_ = int(above)
         if align is not None:
-            if align > 1:
-                m = align - 1
-                if m & align:
-                    raise _OptionError(self.set, align=align)
-            else:
-                m = 0
-            self._align_ = align
-            self._mask   = m
+            self._align_ =  align
+            self._mask   = _sizeof.mask(align)
         if code is not None:
             self._code_ = code
-            if code:  # incl. (byte)code
-                self._incl = ' (incl. code)'
         if detail is not None:
             self._detail_ = detail
         if frames is not None:
@@ -2351,36 +2633,54 @@ def set(self, above=None, align=None, code=None, cutoff=None,
             self._limit_ = limit
         if stats is not None:
             if stats < 0:
-                raise _OptionError(self.set, stats=stats)
+                raise _optionError(self.set, stats=stats)
             # for backward compatibility, cutoff from fractional stats
             s, c = self._c100(stats)
             self._cutoff_ = int(cutoff) if cutoff else c
             self._stats_ = s
             self._profile = s > 1  # profile types
+        return self
 
-    @property
+    @property_RO
     def sized(self):
         '''Get the number objects sized so far (int).
         '''
         return sum(1 for v in _values(self._seen) if v > 0)
 
-    @property
+    @property_RO
     def stats(self):
         '''Get the stats and cutoff setting (float).
         '''
         return self._stats_  # + (self._cutoff_ * 0.01)
 
-    @property
+    @property_RO
     def total(self):
         '''Get the total size (in bytes) accumulated so far.
         '''
         return self._total
 
+    def _xtras(self, extras, where):
+        '''(INTERNAL) Re/set *._debugs[izer]*.
+        '''
+        d = self._debugs
+        if extras:
+            try:  # *_debugsizer=False* -- print ._sizer steps
+                d = extras.pop('_debugsizer')
+            except KeyError:
+                pass
+            if extras:
+                raise _optionError(where, Error=KeyError, **extras)
+        self._debugs = 1 if d else 0
+
+_asizer = Asizer()  # PYCHOK singleton
+
 
 # Public functions
 
 def adict(*classes):
-    '''Install one or more classes to be handled as dict.
+    '''Install one or more classes to be handled as *dict*.
+
+       By default, *type(os.environ)* is installed as *adict*.
     '''
     a = True
     for c in classes:
@@ -2397,6 +2697,10 @@ def adict(*classes):
     return a  # all installed if True
 
 
+assert adict(_typeof(environ))  # _Environ
+del environ
+
+
 def amapped(percentage=None):
     '''Set/get approximate mapped memory usage as a percentage
        of the mapped file size.
@@ -2406,17 +2710,12 @@ def amapped(percentage=None):
 
        Applies only to *numpy.memmap* objects.
     '''
-    global _amapped
-    p = _amapped * 100.0
+    p = _sizeof.amapped * 100.0
     if percentage is not None:
-        _amapped = max(0, min(1, percentage * 0.01))
+        _sizeof.amapped = max(0, min(1, percentage / 100.0))
     return p
 
 
-_amapped = 0.01  # 0 <= percentage <= 1.0
-_asizer  = Asizer()
-
-
 def asized(*objs, **opts):
     '''Return a tuple containing an **Asized** instance for each
        object passed as positional argument.
@@ -2540,7 +2839,7 @@ def asizeof(*objs, **opts):
     t, p, x = _objs_opts_x(asizeof, objs, **opts)
     _asizer.reset(**p)
     if t:
-        if x:  # don't size, profile or rank _getobjects tuple
+        if x:  # don't size, profile or rank gc.get_objects container
             _asizer.exclude_objs(t)
         s = _asizer.asizeof(*t)
         _asizer.print_stats(objs=t, opts=opts)  # show opts as _kwdstr
@@ -2636,13 +2935,8 @@ def flatsize(obj, align=0, **opts):
     '''
     f = t = _typedefof(obj, **opts)
     if t:
-        if align > 1:
-            m = align - 1
-            if m & align:
-                raise _OptionError(flatsize, align=align)
-        else:
-            m = 0
-        f = t.flat(obj, mask=m)
+        m = _sizeof.mask(align) if align else 0
+        f =  t.flat(obj, mask=m)
     return f
 
 
@@ -2656,7 +2950,7 @@ def itemsize(obj, **opts):
     i = t = _typedefof(obj, **opts)
     if t:
         i, v = t.item, t.vari
-        if v and i == _sizeof_Cbyte:
+        if v and i == _sizeof.Cbyte:
             i = getattr(obj, v, i)
     return i
 
@@ -2671,112 +2965,115 @@ def leng(obj, **opts):
         n = t.leng
         if n and callable(n):
             i, v, n = t.item, t.vari, n(obj)
-            if v and i == _sizeof_Cbyte:
+            if v and i == _sizeof.Cbyte:
                 i = getattr(obj, v, i)
-                if i > _sizeof_Cbyte:
+                if i > _sizeof.Cbyte:
                     n = n // i
     return n
 
 
 def named_refs(obj, **opts):
-    '''Return all named **referents** of an object (re-using
-       functionality from **asizeof**).
+    '''DEPRECATED on 2025.07.11, use function **refs(obj, named=True, ...)**.'''
+    return list(refs(obj, named=True, **opts))
+
 
-       Does not return un-named *referents*, e.g. objects in a list.
+def refs(obj, named=False, **opts):
+    '''Yield the *referents* of an object, each a single ref or a 2-tuple
+       (name, ref) if **named=True*.
 
        See function **basicsize** for a description of the available options.
     '''
-    rs = []
     v = _typedefof(obj, **opts)
     if v:
         v = v.refs
         if v and callable(v):
-            for r in v(obj, True):
-                try:
-                    rs.append((r.name, r.ref))
+            for r in v(obj, named):
+                if named:
+                    try:
+                        r = (r.name, r.ref)
+                    except AttributeError:
+                        continue
+                yield r
+
+
+__all__ += tuple(map(_nameof, (Asized, Asizer,  # classes
+                               adict, amapped, asized, asizeof, asizesof,
+                               basicsize, flatsize, itemsize, leng, refs)))
+
+if __name__ == '__main__':  # MCCABE 15
+
+    def _main(args):
+
+        def _gc_collect():
+            if '-gc' in args:
+                _printf(' gc.collect(): %d', gc.collect())
+
+        def _examples(**kwds):
+            '''*_Typedef* and size some examples.
+            '''
+            t = 2**99, _array('B', range(127)), _array('d', range(100))
+            if _numpy:
+                t += (_numpy.arange(0),
+                      _numpy.array(range(0)),
+                      _numpy.ma.masked_array([]),
+                      _numpy.memmap(sys.executable, mode='r'),  # dtype=_numpy.uint8
+                      _numpy.float64(0),
+                      _numpy.ndarray(0),
+                      _numpy.uint64(2**63)),
+                try:  # .matrix deprecated in numpy 1.19.3
+                    t += _numpy.matrix(range(0)),
                 except AttributeError:
                     pass
-    return rs
-
-
-def refs(obj, **opts):
-    '''Return (a generator for) specific *referents* of an object.
-
-       See function **basicsize** for a description of the available options.
-    '''
-    v = _typedefof(obj, **opts)
-    if v:
-        v = v.refs
-        if v and callable(v):
-            v = v(obj, False)
-    return v
-
-
-__all__ = [_nameof(_) for _ in (Asized, Asizer,  # classes
-                                adict, amapped, asized, asizeof, asizesof,
-                                basicsize, flatsize, itemsize, leng, refs)]
-
-if __name__ == '__main__':
-
-    def _examples(**kwds):
-        '''*_Typedef* and size some examples.
-        '''
-        t = 2**99, _array('B', range(127)), _array('d', range(100))
-        if _numpy:
-            t += (_numpy.arange(0),
-                  _numpy.array(range(0)),
-                  _numpy.ma.masked_array([]),
-                  _numpy.memmap(sys.executable, mode='r'),  # dtype=_numpy.uint8
-                  _numpy.float64(0),
-                  _numpy.ndarray(0),
-                  _numpy.uint64(2**63)),
-            try:  # .matrix deprecated in numpy 1.19.3
-                t += _numpy.matrix(range(0)),
-            except AttributeError:
-                pass
-        asizesof(*t, **kwds)  # sizing creates _Typedefs dynamically
-        return t
-
-    if '-examples' in sys.argv or '-x' in sys.argv:
-        # show some asizeof examples
-        import gc
-        collect = False
-        if '-gc' in sys.argv:
-            collect = True
-            gc.collect()
-
-        t = _examples(above=0, cutoff=0, stats=2)
-        amapped(100)  # numpy.memmap'd file size
-        # print summary + 10 largest
-        asizeof(all=True, stats=9, above=1024, frames='-frames' in sys.argv)
-
-        if collect:
-            print('gc.collect() %d' % (gc.collect(),))
-
-    elif '-types' in sys.argv or '-t' in sys.argv:
-        # show static and some dynamic _typedefs
-        t = _examples(stats=0)
-        n =  len(_typedefs)
-        w =  len(str(n)) * ' '
-        _printf('%s%d type definitions: %s and %s, kind ... %s', linesep,
-                 n, 'basic-', 'itemsize (leng)', '-type[def]s')
-        for k, td in sorted((_prepr(k), td) for k, td in _items(_typedefs)):
-            t = '%(base)s and %(item)s%(leng)s, %(kind)s%(code)s' % td.format()
-            _printf('%s %s: %s', w, k, t)
-
-    else:  # if '-version' in sys.argv or '-v' in sys.argv
-        import platform
-        t = (',', _numpy.__name__, _numpy.__version__) if _numpy else ()
-        _printf('%s %s (Python %s %s %s%s)', __file__, __version__,
-                                             sys.version.split()[0],
-                                             platform.architecture()[0],
-                                             platform.machine(), ' '.join(t))
+            if _Inline0:
+                t += _Inline0(),
+            if _Udict:
+                t += _Udict(),
+            asizesof(*t, **kwds)  # sizing creates _Typedefs dynamically
+            return t
+
+        if '-examples' in args or '-x' in args:
+            # show some asizeof examples
+            _gc_collect()
+
+            _ = _examples(above=0, cutoff=0, stats=2)
+            amapped(100)  # numpy.memmap'd file size
+            # print summary + 10 largest
+            asizeof(all=True, stats=9, above=1024, frames='-frames' in sys.argv)
+
+            _gc_collect()
+
+        elif '-types' in args or '-t' in args:
+            # show static and some dynamic _typedefs
+            if '-debugsizer' in args:
+                _asizer._debugs = 1
+            _ = _examples(stats=0)
+            n =  len(_typedefs)
+            w =  len(str(n)) * ' '
+            _printf('%s%d type definitions: %s and %s, kind ... %s', _NL,
+                     n, 'basic-', 'itemsize (leng)', '-type[def]s')
+            for k, td in sorted((_prepr(k), td) for k, td in _items(_typedefs)):
+                t = '%(base)s and %(item)s%(leng)s, %(kind)s%(code)s' % td.format()
+                _printf('%s %s: %s', w, k, t)
+
+        else:  # if '-version' in sys.argv or '-v' in sys.argv
+            import platform as p
+            a = p.architecture()[0]
+            m = p.machine()
+            v = sys.version.split()[0]
+            p = 'Python %s %s %s %s' % (v, a, m, sys.platform)  # p.system()
+            if _numpy:
+                p = '%s, %s %s' % (p, _nameof(_numpy), _numpy.__version__)
+            t = dict(inlined=_inlined(_sizeof))
+            t.update(_sizeof.__dict__)  # materialized
+            t = _kwdstr(**t).replace(', C', ', .C')
+            _printf('%s %s (%s) _sizeof.%s', __file__, __version__, p, t)
+
+    _main(sys.argv[1:])
 
 # License from the initial version of this source file follows:
 
 # --------------------------------------------------------------------
-#       Copyright (c) 2002-2022 -- ProphICy Semiconductor, Inc.
-#                        All rights reserved.
+# Copyright 2002-2025 ProphICy Semiconductor, Inc. All rights reserved.
 #
 # Redistribution and use in source and binary forms, with or without
 # modification, are permitted provided that the following conditions
openSUSE Build Service is sponsored by