| """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) |