"""Utility functions and classes used by nose internally. """ import inspect import os import sys import types try: # for python 3 from types import ClassType, TypeType class_types = (ClassType, TypeType) except: class_types = (type, ) try: #for jython from compiler.consts import CO_GENERATOR except: CO_GENERATOR=0x20 PYTHON_VERSION_MAJOR = sys.version_info[0] PYTHON_VERSION_MINOR = sys.version_info[1] def cmp_lineno(a, b): """Compare functions by their line numbers. """ return cmp(func_lineno(a), func_lineno(b)) def func_lineno(func): """Get the line number of a function. """ try: return func.compat_co_firstlineno except AttributeError: try: if PYTHON_VERSION_MAJOR == 3: return func.__code__.co_firstlineno return func.func_code.co_firstlineno except AttributeError: return -1 def isclass(obj): obj_type = type(obj) return obj_type in class_types or issubclass(obj_type, type) def isgenerator(func): if PYTHON_VERSION_MAJOR == 3: return inspect.isgeneratorfunction(func) try: return func.func_code.co_flags & CO_GENERATOR != 0 except AttributeError: return False def resolve_name(name, module=None): """Resolve a dotted name to a module and its parts. """ parts = name.split('.') parts_copy = parts[:] if module is None: while parts_copy: try: module = __import__('.'.join(parts_copy)) break except ImportError: del parts_copy[-1] if not parts_copy: raise parts = parts[1:] obj = module for part in parts: obj = getattr(obj, part) return obj def try_run(obj, names): """Given a list of possible method names, try to run them with the provided object. """ for name in names: func = getattr(obj, name, None) if func is not None: if type(obj) == types.ModuleType: try: args, varargs, varkw, defaults = inspect.getargspec(func) except TypeError: if hasattr(func, '__call__'): func = func.__call__ try: args, varargs, varkw, defaults = \ inspect.getargspec(func) args.pop(0) except TypeError: raise TypeError("Attribute %s of %r is not a python " "function. Only functions or callables" " may be used as fixtures." % (name, obj)) if len(args): return func(obj) return func() def src(filename): """Find the python source file for a .pyc, .pyo or $py.class file on jython """ if filename is None: return filename if sys.platform.startswith('java') and filename.endswith('$py.class'): return '.'.join((filename[:-9], 'py')) base, ext = os.path.splitext(filename) if ext in ('.pyc', '.pyo', '.py'): return '.'.join((base, 'py')) return filename def transplant_class(cls, module): """ Make a class appear to reside in `module`, rather than the module in which it is actually defined. """ class C(cls): pass C.__module__ = module C.__name__ = cls.__name__ return C def transplant_func(func, module = None): """ Make a function imported from module A appear as if it is located in module B. """ def newfunc(*arg, **kw): return func(*arg, **kw) newfunc = make_decorator(func)(newfunc) if module is None: newfunc.__module__ = inspect.getmodule(func) else: newfunc.__module__ = module return newfunc def make_decorator(func): """ Wraps a test decorator so as to properly replicate metadata of the decorated function. """ def decorate(newfunc): if hasattr(func, 'compat_func_name'): name = func.compat_func_name else: name = func.__name__ newfunc.__dict__ = func.__dict__ newfunc.__doc__ = func.__doc__ if not hasattr(newfunc, 'compat_co_firstlineno'): if PYTHON_VERSION_MAJOR == 3: newfunc.compat_co_firstlineno = func.__code__.co_firstlineno else: newfunc.compat_co_firstlineno = func.func_code.co_firstlineno try: newfunc.__name__ = name except TypeError: newfunc.compat_func_name = name return newfunc return decorate # trick for python 3 # The following emulates the behavior (we need) of an 'unbound method' under # Python 3.x (namely, the ability to have a class associated with a function # definition so that things can do stuff based on its associated class) class UnboundMethod: def __init__(self, cls, func): self.func = func self.__self__ = UnboundSelf(cls) def address(self): cls = self.__self__.cls module = cls.__module__ m = sys.modules[module] file = getattr(m, '__file__', None) if file is not None: file = os.path.abspath(file) return (nose.util.src(file), module, "%s.%s" % (cls.__name__, self.func.__name__)) def __call__(self, *args, **kwargs): return self.func(*args, **kwargs) def __getattr__(self, attr): return getattr(self.func, attr) class UnboundSelf: def __init__(self, cls): self.cls = cls # We have to do this hackery because Python won't let us override the # __class__ attribute... def __getattribute__(self, attr): if attr == '__class__': return self.cls else: return object.__getattribute__(self, attr) def unbound_method(cls, func): if inspect.ismethod(func): return func if not inspect.isfunction(func): raise TypeError('%s is not a function' % (repr(func),)) return UnboundMethod(cls, func) def ismethod(obj): return inspect.ismethod(obj) or isinstance(obj, UnboundMethod) def isunboundmethod(obj): return (inspect.ismethod(obj) and obj.im_self is None) or isinstance(obj, UnboundMethod)