import xinspect
get_func_kwargs = xinspect.get_func_kwargs
[docs]
def get_stack_frame(N=0, strict=True):
"""
Args:
N (int): N=0 means the frame you called this function in.
N=1 is the parent frame.
strict (bool): (default = True)
"""
import inspect
frame_cur = inspect.currentframe()
for idx in range(N + 1):
# always skip the frame of this function
frame_next = frame_cur.f_back
if frame_next is None:
if strict:
raise AssertionError('Frame level {:!r} is root'.format(idx))
else:
break
frame_cur = frame_next
return frame_cur
[docs]
def distext(obj):
"""
Like dis.dis, but returns the text
Args:
obj : a compiled object (e.g. a function, class, generator, etc...)
Returns:
str: text of the python byte code
Example:
>>> from xdev.introspect import * # NOQA
>>> import ubelt as ub
>>> code = ub.codeblock(
'''
def foo(a, b):
print('hello')
if __debug__:
if a is None or b is None:
raise ValueError
print('world')
# This is not entirely optimized away
if __debug__ and (a is None or b is None):
raise ValueError
return a + b
''')
>>> obj = compile(code, filename='<memory>', mode='exec', dont_inherit=True, optimize=0)
>>> print(' ** UNOPTIMIZED')
>>> text = distext(obj)
>>> print(text)
>>> print(' ** OPTIMIZED')
>>> obj = compile(code, filename='<memory>', mode='exec', dont_inherit=True, optimize=1)
>>> text = distext(obj)
>>> print(text)
"""
import dis
import io
file = io.StringIO()
dis.dis(obj, file=file)
file.seek(0)
text = file.read()
return text
# def import_star(module):
# """
# This is the unholy grail
# Import all contents of the module into the global scope
# Args:
# module (str | module): a path, module name, or module itself
# Example:
# >>> # xdoctest: +DISABLE_DOCTEST
# >>> from xdev.misc import import_star
# >>> module = 'ubelt'
# >>> print(import_star(module).keys())
# """
# import ubelt as ub
# if isinstance(module, str):
# if exists(module):
# module = ub.import_module_from_path(module)
# else:
# module = ub.import_module_from_name(module)
# mod_attrs = {
# key: val for key, val in module.__dict__.items()
# if not key.startswith('_')
# }
# parent_frame = get_stack_frame(N=2)
# print('parent_frame.f_code.co_names = {!r}'.format(parent_frame.f_code.co_names))
# print('parent_frame.f_code.co_name = {!r}'.format(parent_frame.f_code.co_name))
# # Note: locals is usually read only
# # parent_frame.f_locals.update(mod_attrs)
# parent_frame.f_globals.update(mod_attrs)
# return mod_attrs
[docs]
def iter_object_tree(obj):
# ub.IndexableWalker
data = obj.__dict__
prefix = []
list_cls = (list, tuple)
dict_cls = (dict,)
indexable_cls = dict_cls + list_cls
seen_ = set()
stack = [(data, prefix)]
while stack:
_data, _prefix = stack.pop()
# Create an items iterable of depending on the indexable data type
if hasattr(_data, '__dict__'):
items = _data.__dict__.items()
elif isinstance(_data, list_cls):
items = enumerate(_data)
elif isinstance(_data, dict_cls):
items = _data.items()
else:
raise TypeError(type(_data))
for key, value in items:
# Yield the full path to this position and its value
path = _prefix + [key]
message = yield path, value
# If the value at this path is also indexable, then continue
# the traversal, unless the False message was explicitly sent
# by the caller.
if message is False:
# Because the `send` method will return the next value,
# we yield a dummy value so we don't clobber the next
# item in the traversal.
yield None
else:
if isinstance(value, indexable_cls) or hasattr(value, '__dict__'):
objid = id(value)
if objid not in seen_:
seen_.add(objid)
stack.append((value, path))
[docs]
def test_object_pickleability(obj):
# ub.IndexableWalker
import pickle
serialized = pickle.dumps(obj)
recon = pickle.loads(serialized) # NOQA
data = obj.__dict__
prefix = []
list_cls = (list, tuple)
dict_cls = (dict,)
indexable_cls = dict_cls + list_cls
def condition(path, value):
import pickle
dumped = pickle.dumps(value)
try:
recon = pickle.loads(dumped) # NOQA
except Exception:
return True
else:
return False
ignore_types = (float, int, bool, type(None))
return not isinstance(value, ignore_types)
walker = iter_object_tree(obj)
for path, value in walker:
flag = condition(path, value)
if flag:
print('BAD FLAG path = {!r}'.format(path))
walker.send(False)
found = []
keyfn = type
seen_ = set()
stack = [(data, prefix)]
while stack:
_data, _prefix = stack.pop()
# Create an items iterable of depending on the indexable data type
if isinstance(_data, list_cls):
items = enumerate(_data)
elif isinstance(_data, dict_cls):
items = _data.items()
elif hasattr(_data, '__dict__'):
items = _data.__dict__.items()
else:
raise TypeError(type(_data))
for key, value in items:
# Yield the full path to this position and its value
path = _prefix + [key]
flag = condition(path, value)
if flag:
store = keyfn(value)
found.append((path, store))
if isinstance(value, indexable_cls) or hasattr(value, '__dict__'):
objid = id(value)
if objid not in seen_:
seen_.add(objid)
stack.append((value, path))