Source code for monty.inspect

# coding: utf-8
Useful additional functions to help get information about live objects
from __future__ import absolute_import, division, print_function, unicode_literals

import inspect
from inspect import currentframe, getframeinfo, stack, getouterframes

[docs]def all_subclasses(cls): """ Given a class `cls`, this recursive function returns a list with all subclasses, subclasses of subclasses, and so on. """ subclasses = cls.__subclasses__() return subclasses + [g for s in subclasses for g in all_subclasses(s)]
[docs]def find_top_pyfile(): """ This function inspects the Cpython frame to find the path of the script. """ import os frame = currentframe() while True: if frame.f_back is None: finfo = getframeinfo(frame) #print(getframeinfo(frame)) return os.path.abspath(finfo.filename) frame = frame.f_back
[docs]def caller_name(skip=2): """ Get a name of a caller in the format module.class.method `skip` specifies how many levels of stack to skip while getting caller name. skip=1 means "who calls me", skip=2 "who calls my caller" etc. An empty string is returned if skipped levels exceed stack height Taken from: Public Domain, i.e. feel free to copy/paste """ stack = inspect.stack() start = 0 + skip if len(stack) < start + 1: return '' parentframe = stack[start][0] name = [] module = inspect.getmodule(parentframe) # `modname` can be None when frame is executed directly in console # TODO(techtonik): consider using __main__ if module: name.append(module.__name__) # detect classname if 'self' in parentframe.f_locals: # I don't know any way to detect call from the object method # XXX: there seems to be no way to detect static method call - it will # be just a function call name.append(parentframe.f_locals['self'].__class__.__name__) codename = parentframe.f_code.co_name if codename != '<module>': # top level usually name.append( codename ) # function or a method del parentframe return ".".join(name)
[docs]def initializer(func): """ Automatically assigns the parameters. >>> class process: ... @initializer ... def __init__(self, cmd, reachable=False, user='root'): ... pass >>> p = process('halt', True) >>> p.cmd, p.reachable, p.user ('halt', True, 'root') """ names, varargs, keywords, defaults = inspect.getargspec(func) from functools import wraps @wraps(func) def wrapper(self, *args, **kargs): #print("names", names, "defaults", defaults) for name, arg in list(zip(names[1:], args)) + list(kargs.items()): setattr(self, name, arg) # Avoid TypeError: argument to reversed() must be a sequence if defaults is not None: for name, default in zip(reversed(names), reversed(defaults)): if not hasattr(self, name): setattr(self, name, default) return func(self, *args, **kargs) return wrapper