blob: 7419ea1d5d6f4724bff0b6734408e07197b62423 [file] [log] [blame]
"""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)