#
# MIT License
#
# Copyright (c) 2023 nbiotcloud
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in all
# copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
#
"""
Inspector Gadget - Extended Python Inspection.
This module extends the python built-in :any:`inspect` module
* :any:`get_signature` - Get Signature as a string
* :any:`getsize` - Sum Size of `obj` and Members
"""
import inspect as _inspect
import sys
from gc import get_referents
from types import FunctionType, ModuleType
[docs]def get_signature(func, vars_=None, exclude=None):
"""
Return signature of `func` as string.
>>> def func(a, b=2):
... return a + b
>>> get_signature(func)
'func(a, b=2)'
In case of given variables `vars_` the caller equivalent is returned.
>>> get_signature(func, {'a': 4, 'b': 2})
'func(4)'
>>> get_signature(func, {'a': 4, 'b': 9})
'func(4, b=9)'
This is useful in combination with `vars()`.
>>> def func(a, b=2):
... print(get_signature(func, vars()))
>>> func(10, b=8)
func(10, b=8)
Positional arguments:
>>> def func(a, *b):
... print(get_signature(func, vars()))
>>> func("a", "b0", "b1")
func('a', 'b0', 'b1')
Keyword arguments (Please note, that the kwargs are sorted by name):
>>> def func(a, **kwargs):
... print(get_signature(func, vars()))
>>> func("a", foo=4, bar=8)
func('a', bar=8, foo=4)
Parameters can be hidden via `exclude` by name or index.
>>> def func(a, b, c, d=None, e=None):
... print(get_signature(func, vars(), exclude=(0, "d", "g")))
>>> func('a', 'b1', 'c2', 'd3', 'e4')
func('b1', 'c2', e='e4')
"""
# pylint: disable=too-many-nested-blocks,too-many-branches
sig = _inspect.signature(func)
name = func.__name__
if vars_ is None:
return f"{name}{str(sig)}"
args = []
if exclude is None:
exclude = tuple()
for idx, param in enumerate(sig.parameters.values()):
if param.name in exclude or idx in exclude:
continue
if param.default is _inspect.Parameter.empty:
if param.kind is _inspect.Parameter.VAR_POSITIONAL:
args += [f"'{v}'" for v in vars_[param.name]]
elif param.kind is _inspect.Parameter.VAR_KEYWORD:
for k, value in sorted(vars_[param.name].items()):
if k in exclude: # pragma: no cover
continue
args.append(f"{str(k)}={value!r}")
else:
args.append(repr(vars_[param.name]))
else:
try:
value = vars_[param.name]
except KeyError: # pragma: no cover
continue
if param.default != value:
args.append(f"{param.name}={value!r}")
jargs = ", ".join(args)
return f"{name}({jargs})"
[docs]def getsize(obj, blacklist=(type, ModuleType, FunctionType)):
"""
Sum Size of `obj` and Members.
>>> import sys
>>> sys.getsizeof('a') # doctest: +SKIP
50
>>> sys.getsizeof('b') # doctest: +SKIP
50
>>> sys.getsizeof(('a', 'b')) # doctest: +SKIP
56
>>> getsize(('a', 'b')) # doctest: +SKIP
156
>>> def myfunc(a):
... return a
>>> getsize(myfunc)
Traceback (most recent call last):
...
TypeError: getsize() does not take argument of type: <class 'function'>
"""
if isinstance(obj, blacklist):
raise TypeError(f"getsize() does not take argument of type: {type(obj)}")
seen_ids = set()
size = 0
objs = [obj]
while objs:
need_referents = []
for item in objs:
if not isinstance(item, blacklist) and id(item) not in seen_ids:
seen_ids.add(id(item))
size += sys.getsizeof(item)
need_referents.append(item)
objs = get_referents(*need_referents)
return size