1823b8294SYaroslav Brustinov"""
2823b8294SYaroslav BrustinovThis module contains fixups for using nose under different versions of Python.
3823b8294SYaroslav Brustinov"""
4823b8294SYaroslav Brustinovimport sys
5823b8294SYaroslav Brustinovimport os
6823b8294SYaroslav Brustinovimport traceback
7823b8294SYaroslav Brustinovimport types
8823b8294SYaroslav Brustinovimport inspect
9823b8294SYaroslav Brustinovimport nose.util
10823b8294SYaroslav Brustinov
11823b8294SYaroslav Brustinov__all__ = ['make_instancemethod', 'cmp_to_key', 'sort_list', 'ClassType',
12823b8294SYaroslav Brustinov           'TypeType', 'UNICODE_STRINGS', 'unbound_method', 'ismethod',
13823b8294SYaroslav Brustinov           'bytes_', 'is_base_exception', 'force_unicode', 'exc_to_unicode',
14823b8294SYaroslav Brustinov           'format_exception']
15823b8294SYaroslav Brustinov
16823b8294SYaroslav Brustinov# In Python 3.x, all strings are unicode (the call to 'unicode()' in the 2.x
17823b8294SYaroslav Brustinov# source will be replaced with 'str()' when running 2to3, so this test will
18823b8294SYaroslav Brustinov# then become true)
19823b8294SYaroslav BrustinovUNICODE_STRINGS = (type(unicode()) == type(str()))
20823b8294SYaroslav Brustinov
21823b8294SYaroslav Brustinovif sys.version_info[:2] < (3, 0):
22823b8294SYaroslav Brustinov    def force_unicode(s, encoding='UTF-8'):
23823b8294SYaroslav Brustinov        try:
24823b8294SYaroslav Brustinov            s = unicode(s)
25823b8294SYaroslav Brustinov        except UnicodeDecodeError:
26823b8294SYaroslav Brustinov            s = str(s).decode(encoding, 'replace')
27823b8294SYaroslav Brustinov
28823b8294SYaroslav Brustinov        return s
29823b8294SYaroslav Brustinovelse:
30823b8294SYaroslav Brustinov    def force_unicode(s, encoding='UTF-8'):
31823b8294SYaroslav Brustinov        return str(s)
32823b8294SYaroslav Brustinov
33823b8294SYaroslav Brustinov# new.instancemethod() is obsolete for new-style classes (Python 3.x)
34823b8294SYaroslav Brustinov# We need to use descriptor methods instead.
35823b8294SYaroslav Brustinovtry:
36823b8294SYaroslav Brustinov    import new
37823b8294SYaroslav Brustinov    def make_instancemethod(function, instance):
38823b8294SYaroslav Brustinov        return new.instancemethod(function.im_func, instance,
39823b8294SYaroslav Brustinov                                  instance.__class__)
40823b8294SYaroslav Brustinovexcept ImportError:
41823b8294SYaroslav Brustinov    def make_instancemethod(function, instance):
42823b8294SYaroslav Brustinov        return function.__get__(instance, instance.__class__)
43823b8294SYaroslav Brustinov
44823b8294SYaroslav Brustinov# To be forward-compatible, we do all list sorts using keys instead of cmp
45823b8294SYaroslav Brustinov# functions.  However, part of the unittest.TestLoader API involves a
46823b8294SYaroslav Brustinov# user-provideable cmp function, so we need some way to convert that.
47823b8294SYaroslav Brustinovdef cmp_to_key(mycmp):
48823b8294SYaroslav Brustinov    'Convert a cmp= function into a key= function'
49823b8294SYaroslav Brustinov    class Key(object):
50823b8294SYaroslav Brustinov        def __init__(self, obj):
51823b8294SYaroslav Brustinov            self.obj = obj
52823b8294SYaroslav Brustinov        def __lt__(self, other):
53823b8294SYaroslav Brustinov            return mycmp(self.obj, other.obj) < 0
54823b8294SYaroslav Brustinov        def __gt__(self, other):
55823b8294SYaroslav Brustinov            return mycmp(self.obj, other.obj) > 0
56823b8294SYaroslav Brustinov        def __eq__(self, other):
57823b8294SYaroslav Brustinov            return mycmp(self.obj, other.obj) == 0
58823b8294SYaroslav Brustinov    return Key
59823b8294SYaroslav Brustinov
60823b8294SYaroslav Brustinov# Python 2.3 also does not support list-sorting by key, so we need to convert
61823b8294SYaroslav Brustinov# keys to cmp functions if we're running on old Python..
62823b8294SYaroslav Brustinovif sys.version_info < (2, 4):
63823b8294SYaroslav Brustinov    def sort_list(l, key, reverse=False):
64823b8294SYaroslav Brustinov        if reverse:
65823b8294SYaroslav Brustinov            return l.sort(lambda a, b: cmp(key(b), key(a)))
66823b8294SYaroslav Brustinov        else:
67823b8294SYaroslav Brustinov            return l.sort(lambda a, b: cmp(key(a), key(b)))
68823b8294SYaroslav Brustinovelse:
69823b8294SYaroslav Brustinov    def sort_list(l, key, reverse=False):
70823b8294SYaroslav Brustinov        return l.sort(key=key, reverse=reverse)
71823b8294SYaroslav Brustinov
72823b8294SYaroslav Brustinov# In Python 3.x, all objects are "new style" objects descended from 'type', and
73823b8294SYaroslav Brustinov# thus types.ClassType and types.TypeType don't exist anymore.  For
74823b8294SYaroslav Brustinov# compatibility, we make sure they still work.
75823b8294SYaroslav Brustinovif hasattr(types, 'ClassType'):
76823b8294SYaroslav Brustinov    ClassType = types.ClassType
77823b8294SYaroslav Brustinov    TypeType = types.TypeType
78823b8294SYaroslav Brustinovelse:
79823b8294SYaroslav Brustinov    ClassType = type
80823b8294SYaroslav Brustinov    TypeType = type
81823b8294SYaroslav Brustinov
82823b8294SYaroslav Brustinov# The following emulates the behavior (we need) of an 'unbound method' under
83823b8294SYaroslav Brustinov# Python 3.x (namely, the ability to have a class associated with a function
84823b8294SYaroslav Brustinov# definition so that things can do stuff based on its associated class)
85823b8294SYaroslav Brustinovclass UnboundMethod:
86823b8294SYaroslav Brustinov    def __init__(self, cls, func):
87823b8294SYaroslav Brustinov        # Make sure we have all the same attributes as the original function,
88823b8294SYaroslav Brustinov        # so that the AttributeSelector plugin will work correctly...
89823b8294SYaroslav Brustinov        self.__dict__ = func.__dict__.copy()
90823b8294SYaroslav Brustinov        self._func = func
91823b8294SYaroslav Brustinov        self.__self__ = UnboundSelf(cls)
92823b8294SYaroslav Brustinov        if sys.version_info < (3, 0):
93823b8294SYaroslav Brustinov            self.im_class = cls
94823b8294SYaroslav Brustinov
95823b8294SYaroslav Brustinov    def address(self):
96823b8294SYaroslav Brustinov        cls = self.__self__.cls
97823b8294SYaroslav Brustinov        modname = cls.__module__
98823b8294SYaroslav Brustinov        module = sys.modules[modname]
99823b8294SYaroslav Brustinov        filename = getattr(module, '__file__', None)
100823b8294SYaroslav Brustinov        if filename is not None:
101823b8294SYaroslav Brustinov            filename = os.path.abspath(filename)
102823b8294SYaroslav Brustinov        return (nose.util.src(filename), modname, "%s.%s" % (cls.__name__,
103823b8294SYaroslav Brustinov                                                        self._func.__name__))
104823b8294SYaroslav Brustinov
105823b8294SYaroslav Brustinov    def __call__(self, *args, **kwargs):
106823b8294SYaroslav Brustinov        return self._func(*args, **kwargs)
107823b8294SYaroslav Brustinov
108823b8294SYaroslav Brustinov    def __getattr__(self, attr):
109823b8294SYaroslav Brustinov        return getattr(self._func, attr)
110823b8294SYaroslav Brustinov
111823b8294SYaroslav Brustinov    def __repr__(self):
112823b8294SYaroslav Brustinov        return '<unbound method %s.%s>' % (self.__self__.cls.__name__,
113823b8294SYaroslav Brustinov                                           self._func.__name__)
114823b8294SYaroslav Brustinov
115823b8294SYaroslav Brustinovclass UnboundSelf:
116823b8294SYaroslav Brustinov    def __init__(self, cls):
117823b8294SYaroslav Brustinov        self.cls = cls
118823b8294SYaroslav Brustinov
119823b8294SYaroslav Brustinov    # We have to do this hackery because Python won't let us override the
120823b8294SYaroslav Brustinov    # __class__ attribute...
121823b8294SYaroslav Brustinov    def __getattribute__(self, attr):
122823b8294SYaroslav Brustinov        if attr == '__class__':
123823b8294SYaroslav Brustinov            return self.cls
124823b8294SYaroslav Brustinov        else:
125823b8294SYaroslav Brustinov            return object.__getattribute__(self, attr)
126823b8294SYaroslav Brustinov
127823b8294SYaroslav Brustinovdef unbound_method(cls, func):
128823b8294SYaroslav Brustinov    if inspect.ismethod(func):
129823b8294SYaroslav Brustinov        return func
130823b8294SYaroslav Brustinov    if not inspect.isfunction(func):
131823b8294SYaroslav Brustinov        raise TypeError('%s is not a function' % (repr(func),))
132823b8294SYaroslav Brustinov    return UnboundMethod(cls, func)
133823b8294SYaroslav Brustinov
134823b8294SYaroslav Brustinovdef ismethod(obj):
135823b8294SYaroslav Brustinov    return inspect.ismethod(obj) or isinstance(obj, UnboundMethod)
136823b8294SYaroslav Brustinov
137823b8294SYaroslav Brustinov
138823b8294SYaroslav Brustinov# Make a pseudo-bytes function that can be called without the encoding arg:
139823b8294SYaroslav Brustinovif sys.version_info >= (3, 0):
140823b8294SYaroslav Brustinov    def bytes_(s, encoding='utf8'):
141823b8294SYaroslav Brustinov        if isinstance(s, bytes):
142823b8294SYaroslav Brustinov            return s
143823b8294SYaroslav Brustinov        return bytes(s, encoding)
144823b8294SYaroslav Brustinovelse:
145823b8294SYaroslav Brustinov    def bytes_(s, encoding=None):
146823b8294SYaroslav Brustinov        return str(s)
147823b8294SYaroslav Brustinov
148823b8294SYaroslav Brustinov
149823b8294SYaroslav Brustinovif sys.version_info[:2] >= (2, 6):
150823b8294SYaroslav Brustinov    def isgenerator(o):
151823b8294SYaroslav Brustinov        if isinstance(o, UnboundMethod):
152823b8294SYaroslav Brustinov            o = o._func
153823b8294SYaroslav Brustinov        return inspect.isgeneratorfunction(o) or inspect.isgenerator(o)
154823b8294SYaroslav Brustinovelse:
155823b8294SYaroslav Brustinov    try:
156823b8294SYaroslav Brustinov        from compiler.consts import CO_GENERATOR
157823b8294SYaroslav Brustinov    except ImportError:
158823b8294SYaroslav Brustinov        # IronPython doesn't have a complier module
159823b8294SYaroslav Brustinov        CO_GENERATOR=0x20
160823b8294SYaroslav Brustinov
161823b8294SYaroslav Brustinov    def isgenerator(func):
162823b8294SYaroslav Brustinov        try:
163823b8294SYaroslav Brustinov            return func.func_code.co_flags & CO_GENERATOR != 0
164823b8294SYaroslav Brustinov        except AttributeError:
165823b8294SYaroslav Brustinov            return False
166823b8294SYaroslav Brustinov
167823b8294SYaroslav Brustinov# Make a function to help check if an exception is derived from BaseException.
168823b8294SYaroslav Brustinov# In Python 2.4, we just use Exception instead.
169823b8294SYaroslav Brustinovif sys.version_info[:2] < (2, 5):
170823b8294SYaroslav Brustinov    def is_base_exception(exc):
171823b8294SYaroslav Brustinov        return isinstance(exc, Exception)
172823b8294SYaroslav Brustinovelse:
173823b8294SYaroslav Brustinov    def is_base_exception(exc):
174823b8294SYaroslav Brustinov        return isinstance(exc, BaseException)
175823b8294SYaroslav Brustinov
176823b8294SYaroslav Brustinovif sys.version_info[:2] < (3, 0):
177823b8294SYaroslav Brustinov    def exc_to_unicode(ev, encoding='utf-8'):
178823b8294SYaroslav Brustinov        if is_base_exception(ev):
179823b8294SYaroslav Brustinov            if not hasattr(ev, '__unicode__'):
180823b8294SYaroslav Brustinov                # 2.5-
181823b8294SYaroslav Brustinov                if not hasattr(ev, 'message'):
182823b8294SYaroslav Brustinov                    # 2.4
183823b8294SYaroslav Brustinov                    msg = len(ev.args) and ev.args[0] or ''
184823b8294SYaroslav Brustinov                else:
185823b8294SYaroslav Brustinov                    msg = ev.message
186823b8294SYaroslav Brustinov                msg = force_unicode(msg, encoding=encoding)
187823b8294SYaroslav Brustinov                clsname = force_unicode(ev.__class__.__name__,
188823b8294SYaroslav Brustinov                        encoding=encoding)
189823b8294SYaroslav Brustinov                ev = u'%s: %s' % (clsname, msg)
190823b8294SYaroslav Brustinov        elif not isinstance(ev, unicode):
191823b8294SYaroslav Brustinov            ev = repr(ev)
192823b8294SYaroslav Brustinov
193823b8294SYaroslav Brustinov        return force_unicode(ev, encoding=encoding)
194823b8294SYaroslav Brustinovelse:
195823b8294SYaroslav Brustinov    def exc_to_unicode(ev, encoding='utf-8'):
196823b8294SYaroslav Brustinov        return str(ev)
197823b8294SYaroslav Brustinov
198823b8294SYaroslav Brustinovdef format_exception(exc_info, encoding='UTF-8'):
199823b8294SYaroslav Brustinov    ec, ev, tb = exc_info
200823b8294SYaroslav Brustinov
201823b8294SYaroslav Brustinov    # Our exception object may have been turned into a string, and Python 3's
202823b8294SYaroslav Brustinov    # traceback.format_exception() doesn't take kindly to that (it expects an
203823b8294SYaroslav Brustinov    # actual exception object).  So we work around it, by doing the work
204823b8294SYaroslav Brustinov    # ourselves if ev is not an exception object.
205823b8294SYaroslav Brustinov    if not is_base_exception(ev):
206823b8294SYaroslav Brustinov        tb_data = force_unicode(
207823b8294SYaroslav Brustinov                ''.join(traceback.format_tb(tb)),
208823b8294SYaroslav Brustinov                encoding)
209823b8294SYaroslav Brustinov        ev = exc_to_unicode(ev)
210823b8294SYaroslav Brustinov        return tb_data + ev
211823b8294SYaroslav Brustinov    else:
212823b8294SYaroslav Brustinov        return force_unicode(
213823b8294SYaroslav Brustinov                ''.join(traceback.format_exception(*exc_info)),
214823b8294SYaroslav Brustinov                encoding)