Source code for wheezy.core.introspection

""" ``introspection`` module.
"""

import warnings

from inspect import getargspec
from inspect import isfunction

from wheezy.core.comp import __import__


def import_name(fullname):
[docs] """ Dynamically imports object by its full name. >>> from datetime import timedelta >>> import_name('datetime.timedelta') is timedelta True """ namespace, name = fullname.rsplit('.', 1) obj = __import__(namespace, None, None, [name]) return getattr(obj, name) class looks(object):
[docs] """ Performs duck typing checks for two classes. Typical use:: assert looks(IFoo, ignore_argspec=['pex']).like(Foo) """ def __init__(self, cls): """ *cls* - a class to be checked """ self.cls = cls def like(self, cls, notice=None, ignore_funcs=None, ignore_argspec=None):
[docs] """ Check if `self.cls` can be used as duck typing for `cls`. *cls* - class to be checked for duck typing. *ignore_funcs* - a list of functions to ignore *ignore_argspec* - a list of functions to ignore arguments spec. """ notice = notice or [] ignore_funcs = ignore_funcs or [] ignore_argspec = ignore_argspec or [] basis = declarations(cls, notice=notice) contestee = declarations(self.cls, notice=notice) for name in ignore_funcs: if name not in basis: warn("'%s': redundant ignore." % name) return False for name, t in basis.items(): if name in ignore_funcs: continue if name not in contestee: warn("'%s': is missing." % name) return False else: t2 = contestee[name] if isfunction(t) and isfunction(t2): if name in ignore_argspec: continue if getargspec(t) != getargspec(t2): warn("'%s': argument names or defaults " "have no match." % name) return False elif t2.__class__ is not t.__class__: warn("'%s': is not %s." % (name, t.__class__.__name__)) return False return True # region: internal details def declarations(cls, notice):
return dict((name, t) for name, t in cls.__dict__.items() if name in notice or not name.startswith('_')) def warn(message): warnings.warn(message, stacklevel=3)