1"""Utility functions and classes used by nose internally.
2"""
3import inspect
4import os
5import sys
6import types
7try:
8    # for python 3
9    from types import ClassType, TypeType
10    class_types = (ClassType, TypeType)
11except:
12    class_types = (type, )
13
14try:
15    #for jython
16    from compiler.consts import CO_GENERATOR
17except:
18    CO_GENERATOR=0x20
19
20PYTHON_VERSION_MAJOR = sys.version_info[0]
21PYTHON_VERSION_MINOR = sys.version_info[1]
22
23def cmp_lineno(a, b):
24    """Compare functions by their line numbers.
25    """
26    return cmp(func_lineno(a), func_lineno(b))
27
28def func_lineno(func):
29    """Get the line number of a function.
30    """
31    try:
32        return func.compat_co_firstlineno
33    except AttributeError:
34        try:
35            if PYTHON_VERSION_MAJOR == 3:
36                return func.__code__.co_firstlineno
37            return func.func_code.co_firstlineno
38        except AttributeError:
39            return -1
40
41def isclass(obj):
42    obj_type = type(obj)
43    return obj_type in class_types or issubclass(obj_type, type)
44
45def isgenerator(func):
46    if PYTHON_VERSION_MAJOR == 3:
47        return inspect.isgeneratorfunction(func)
48    try:
49        return func.func_code.co_flags & CO_GENERATOR != 0
50    except AttributeError:
51        return False
52
53def resolve_name(name, module=None):
54    """Resolve a dotted name to a module and its parts.
55    """
56    parts = name.split('.')
57    parts_copy = parts[:]
58    if module is None:
59        while parts_copy:
60            try:
61                module = __import__('.'.join(parts_copy))
62                break
63            except ImportError:
64                del parts_copy[-1]
65                if not parts_copy:
66                    raise
67        parts = parts[1:]
68    obj = module
69    for part in parts:
70        obj = getattr(obj, part)
71    return obj
72
73def try_run(obj, names):
74    """Given a list of possible method names, try to run them with the
75    provided object.
76    """
77    for name in names:
78        func = getattr(obj, name, None)
79        if func is not None:
80            if type(obj) == types.ModuleType:
81                try:
82                    args, varargs, varkw, defaults = inspect.getargspec(func)
83                except TypeError:
84                    if hasattr(func, '__call__'):
85                        func = func.__call__
86                    try:
87                        args, varargs, varkw, defaults = \
88                            inspect.getargspec(func)
89                        args.pop(0)
90                    except TypeError:
91                        raise TypeError("Attribute %s of %r is not a python "
92                                        "function. Only functions or callables"
93                                        " may be used as fixtures." %
94                                        (name, obj))
95                if len(args):
96                    return func(obj)
97            return func()
98
99def src(filename):
100    """Find the python source file for a .pyc, .pyo
101    or $py.class file on jython
102    """
103    if filename is None:
104        return filename
105    if sys.platform.startswith('java') and filename.endswith('$py.class'):
106        return '.'.join((filename[:-9], 'py'))
107    base, ext = os.path.splitext(filename)
108    if ext in ('.pyc', '.pyo', '.py'):
109        return '.'.join((base, 'py'))
110    return filename
111
112def transplant_class(cls, module):
113    """
114    Make a class appear to reside in `module`, rather than the module in which
115    it is actually defined.
116    """
117    class C(cls):
118        pass
119    C.__module__ = module
120    C.__name__ = cls.__name__
121    return C
122
123def transplant_func(func, module = None):
124    """
125    Make a function imported from module A appear as if it is located
126    in module B.
127    """
128
129    def newfunc(*arg, **kw):
130        return func(*arg, **kw)
131
132    newfunc = make_decorator(func)(newfunc)
133    if module is None:
134        newfunc.__module__ = inspect.getmodule(func)
135    else:
136        newfunc.__module__ = module
137    return newfunc
138
139def make_decorator(func):
140    """
141    Wraps a test decorator so as to properly replicate metadata
142    of the decorated function.
143    """
144    def decorate(newfunc):
145        if hasattr(func, 'compat_func_name'):
146            name = func.compat_func_name
147        else:
148            name = func.__name__
149        newfunc.__dict__ = func.__dict__
150        newfunc.__doc__ = func.__doc__
151        if not hasattr(newfunc, 'compat_co_firstlineno'):
152            if PYTHON_VERSION_MAJOR == 3:
153                newfunc.compat_co_firstlineno = func.__code__.co_firstlineno
154            else:
155                newfunc.compat_co_firstlineno = func.func_code.co_firstlineno
156        try:
157            newfunc.__name__ = name
158        except TypeError:
159            newfunc.compat_func_name = name
160        return newfunc
161    return decorate
162
163# trick for python 3
164# The following emulates the behavior (we need) of an 'unbound method' under
165# Python 3.x (namely, the ability to have a class associated with a function
166# definition so that things can do stuff based on its associated class)
167
168class UnboundMethod:
169    def __init__(self, cls, func):
170        self.func = func
171        self.__self__ = UnboundSelf(cls)
172
173    def address(self):
174        cls = self.__self__.cls
175        module = cls.__module__
176        m = sys.modules[module]
177        file = getattr(m, '__file__', None)
178        if file is not None:
179            file = os.path.abspath(file)
180        return (nose.util.src(file), module, "%s.%s" % (cls.__name__, self.func.__name__))
181
182    def __call__(self, *args, **kwargs):
183        return self.func(*args, **kwargs)
184
185    def __getattr__(self, attr):
186        return getattr(self.func, attr)
187
188class UnboundSelf:
189    def __init__(self, cls):
190        self.cls = cls
191
192    # We have to do this hackery because Python won't let us override the
193    # __class__ attribute...
194    def __getattribute__(self, attr):
195        if attr == '__class__':
196            return self.cls
197        else:
198            return object.__getattribute__(self, attr)
199
200def unbound_method(cls, func):
201    if inspect.ismethod(func):
202        return func
203    if not inspect.isfunction(func):
204        raise TypeError('%s is not a function' % (repr(func),))
205    return UnboundMethod(cls, func)
206
207def ismethod(obj):
208    return inspect.ismethod(obj) or isinstance(obj, UnboundMethod)
209
210def isunboundmethod(obj):
211    return (inspect.ismethod(obj) and obj.im_self is None) or isinstance(obj, UnboundMethod)
212