1import functools 2import sys 3 4 5PY3K = sys.version_info >= (3,) 6 7 8methods = set([ 9 "__iter__", 10 "__len__", 11 "__contains__", 12 "__getitem__", 13 "__setitem__", 14 "__delitem__", 15 16 "__enter__", 17 "__exit__", 18 19 "__lt__", 20 "__le__", 21 "__eq__", 22 "__ne__", 23 "__gt__", 24 "__ge__", 25 26 "__add__", 27 "__and__", 28 "__divmod__", 29 "__floordiv__", 30 "__lshift__", 31 "__mod__", 32 "__mul__", 33 "__or__", 34 "__pow__", 35 "__rshift__", 36 "__sub__", 37 "__truediv__", 38 "__xor__", 39 40 "__call__", 41 "__repr__", 42]) 43if PY3K: 44 methods.add("__next__") 45 methods.add("__bool__") 46else: 47 methods.add("__div__") 48 methods.add("__nonzero__") 49MAGIC_METHODS = frozenset(methods) 50del methods 51 52 53def _build_magic_dispatcher(method): 54 def inner(self, *args, **kwargs): 55 return self.__dict__[method](*args, **kwargs) 56 inner.__name__ = method 57 return inner 58 59 60class stub(object): 61 _classes_cache = {} 62 63 def __new__(cls, **kwargs): 64 magic_methods_present = MAGIC_METHODS.intersection(kwargs) 65 if magic_methods_present not in cls._classes_cache: 66 attrs = dict( 67 (method, _build_magic_dispatcher(method)) 68 for method in magic_methods_present 69 ) 70 attrs["__module__"] = cls.__module__ 71 cls._classes_cache[magic_methods_present] = ( 72 type("stub", (cls,), attrs) 73 ) 74 new_cls = cls._classes_cache[magic_methods_present] 75 return super(stub, new_cls).__new__(new_cls) 76 77 def __init__(self, **kwargs): 78 self.__dict__.update(kwargs) 79 80 def __repr__(self): 81 return '<stub(%s)>' % ', '.join([ 82 '%s=%r' % (key, val) 83 for key, val in self.__dict__.items() 84 ]) 85 86 87def raiser(exc): 88 if ( 89 not ( 90 isinstance(exc, BaseException) or 91 isinstance(exc, type) and issubclass(exc, BaseException) 92 ) 93 ): 94 raise TypeError("exc must be either an exception instance or class.") 95 96 def inner(*args, **kwargs): 97 raise exc 98 return inner 99 100 101class call(object): 102 def __init__(self, *args, **kwargs): 103 self.args = args 104 self.kwargs = kwargs 105 106 def __eq__(self, other): 107 if not isinstance(other, call): 108 return NotImplemented 109 return self.args == other.args and self.kwargs == other.kwargs 110 111 def __ne__(self, other): 112 return not (self == other) 113 114 def __hash__(self): 115 return hash(( 116 self.args, 117 frozenset(self.kwargs.items()) 118 )) 119 120 def __repr__(self): 121 args = ", ".join(map(repr, self.args)) 122 kwargs = ", ".join("%s=%r" % (k, v) for k, v in self.kwargs.items()) 123 comma = ", " if args and kwargs else "" 124 return "<call(%s%s%s)>" % (args, comma, kwargs) 125 126 127def call_recorder(func): 128 @functools.wraps(func) 129 def inner(*args, **kwargs): 130 inner.calls.append(call(*args, **kwargs)) 131 return func(*args, **kwargs) 132 inner.calls = [] 133 return inner 134