1""" 2This module contains fixups for using nose under different versions of Python. 3""" 4import sys 5import os 6import traceback 7import types 8import inspect 9import nose.util 10 11__all__ = ['make_instancemethod', 'cmp_to_key', 'sort_list', 'ClassType', 12 'TypeType', 'UNICODE_STRINGS', 'unbound_method', 'ismethod', 13 'bytes_', 'is_base_exception', 'force_unicode', 'exc_to_unicode', 14 'format_exception'] 15 16# In Python 3.x, all strings are unicode (the call to 'unicode()' in the 2.x 17# source will be replaced with 'str()' when running 2to3, so this test will 18# then become true) 19UNICODE_STRINGS = (type(str()) == type(str())) 20 21if sys.version_info[:2] < (3, 0): 22 def force_unicode(s, encoding='UTF-8'): 23 try: 24 s = str(s) 25 except UnicodeDecodeError: 26 s = str(s).decode(encoding, 'replace') 27 28 return s 29else: 30 def force_unicode(s, encoding='UTF-8'): 31 return str(s) 32 33# new.instancemethod() is obsolete for new-style classes (Python 3.x) 34# We need to use descriptor methods instead. 35try: 36 import new 37 def make_instancemethod(function, instance): 38 return new.instancemethod(function.__func__, instance, 39 instance.__class__) 40except ImportError: 41 def make_instancemethod(function, instance): 42 return function.__get__(instance, instance.__class__) 43 44# To be forward-compatible, we do all list sorts using keys instead of cmp 45# functions. However, part of the unittest.TestLoader API involves a 46# user-provideable cmp function, so we need some way to convert that. 47def cmp_to_key(mycmp): 48 'Convert a cmp= function into a key= function' 49 class Key(object): 50 def __init__(self, obj): 51 self.obj = obj 52 def __lt__(self, other): 53 return mycmp(self.obj, other.obj) < 0 54 def __gt__(self, other): 55 return mycmp(self.obj, other.obj) > 0 56 def __eq__(self, other): 57 return mycmp(self.obj, other.obj) == 0 58 return Key 59 60# Python 2.3 also does not support list-sorting by key, so we need to convert 61# keys to cmp functions if we're running on old Python.. 62if sys.version_info < (2, 4): 63 def sort_list(l, key, reverse=False): 64 if reverse: 65 return l.sort(lambda a, b: cmp(key(b), key(a))) 66 else: 67 return l.sort(lambda a, b: cmp(key(a), key(b))) 68else: 69 def sort_list(l, key, reverse=False): 70 return l.sort(key=key, reverse=reverse) 71 72# In Python 3.x, all objects are "new style" objects descended from 'type', and 73# thus types.ClassType and types.TypeType don't exist anymore. For 74# compatibility, we make sure they still work. 75if hasattr(types, 'ClassType'): 76 ClassType = type 77 TypeType = type 78else: 79 ClassType = type 80 TypeType = type 81 82# The following emulates the behavior (we need) of an 'unbound method' under 83# Python 3.x (namely, the ability to have a class associated with a function 84# definition so that things can do stuff based on its associated class) 85class UnboundMethod: 86 def __init__(self, cls, func): 87 # Make sure we have all the same attributes as the original function, 88 # so that the AttributeSelector plugin will work correctly... 89 self.__dict__ = func.__dict__.copy() 90 self._func = func 91 self.__self__ = UnboundSelf(cls) 92 if sys.version_info < (3, 0): 93 self.__self__.__class__ = cls 94 self.__doc__ = getattr(func, '__doc__', None) 95 96 def address(self): 97 cls = self.__self__.cls 98 modname = cls.__module__ 99 module = sys.modules[modname] 100 filename = getattr(module, '__file__', None) 101 if filename is not None: 102 filename = os.path.abspath(filename) 103 return (nose.util.src(filename), modname, "%s.%s" % (cls.__name__, 104 self._func.__name__)) 105 106 def __call__(self, *args, **kwargs): 107 return self._func(*args, **kwargs) 108 109 def __getattr__(self, attr): 110 return getattr(self._func, attr) 111 112 def __repr__(self): 113 return '<unbound method %s.%s>' % (self.__self__.cls.__name__, 114 self._func.__name__) 115 116class UnboundSelf: 117 def __init__(self, cls): 118 self.cls = cls 119 120 # We have to do this hackery because Python won't let us override the 121 # __class__ attribute... 122 def __getattribute__(self, attr): 123 if attr == '__class__': 124 return self.cls 125 else: 126 return object.__getattribute__(self, attr) 127 128def unbound_method(cls, func): 129 if inspect.ismethod(func): 130 return func 131 if not inspect.isfunction(func): 132 raise TypeError('%s is not a function' % (repr(func),)) 133 return UnboundMethod(cls, func) 134 135def ismethod(obj): 136 return inspect.ismethod(obj) or isinstance(obj, UnboundMethod) 137 138 139# Make a pseudo-bytes function that can be called without the encoding arg: 140if sys.version_info >= (3, 0): 141 def bytes_(s, encoding='utf8'): 142 if isinstance(s, bytes): 143 return s 144 return bytes(s, encoding) 145else: 146 def bytes_(s, encoding=None): 147 return str(s) 148 149 150if sys.version_info[:2] >= (2, 6): 151 def isgenerator(o): 152 if isinstance(o, UnboundMethod): 153 o = o._func 154 return inspect.isgeneratorfunction(o) or inspect.isgenerator(o) 155else: 156 try: 157 from compiler.consts import CO_GENERATOR 158 except ImportError: 159 # IronPython doesn't have a complier module 160 CO_GENERATOR=0x20 161 162 def isgenerator(func): 163 try: 164 return func.__code__.co_flags & CO_GENERATOR != 0 165 except AttributeError: 166 return False 167 168# Make a function to help check if an exception is derived from BaseException. 169# In Python 2.4, we just use Exception instead. 170if sys.version_info[:2] < (2, 5): 171 def is_base_exception(exc): 172 return isinstance(exc, Exception) 173else: 174 def is_base_exception(exc): 175 return isinstance(exc, BaseException) 176 177if sys.version_info[:2] < (3, 0): 178 def exc_to_unicode(ev, encoding='utf-8'): 179 if is_base_exception(ev): 180 if not hasattr(ev, '__unicode__'): 181 # 2.5- 182 if not hasattr(ev, 'message'): 183 # 2.4 184 msg = len(ev.args) and ev.args[0] or '' 185 else: 186 msg = ev.message 187 msg = force_unicode(msg, encoding=encoding) 188 clsname = force_unicode(ev.__class__.__name__, 189 encoding=encoding) 190 ev = '%s: %s' % (clsname, msg) 191 elif not isinstance(ev, str): 192 ev = repr(ev) 193 194 return force_unicode(ev, encoding=encoding) 195else: 196 def exc_to_unicode(ev, encoding='utf-8'): 197 return str(ev) 198 199def format_exception(exc_info, encoding='UTF-8'): 200 ec, ev, tb = exc_info 201 202 # Our exception object may have been turned into a string, and Python 3's 203 # traceback.format_exception() doesn't take kindly to that (it expects an 204 # actual exception object). So we work around it, by doing the work 205 # ourselves if ev is not an exception object. 206 if not is_base_exception(ev): 207 tb_data = force_unicode( 208 ''.join(traceback.format_tb(tb)), 209 encoding) 210 ev = exc_to_unicode(ev) 211 return tb_data + ev 212 else: 213 return force_unicode( 214 ''.join(traceback.format_exception(*exc_info)), 215 encoding) 216