Whole file
testing-cabal/mock
The author described this change as “Attempting to set an unsupported magic method now raises an AttributeError Change version number to 0.7.0 beta 4 Change copying test to work on Python 3 (no sys.maxint)”. It counts as a record because the check below fails on the code as it stood at f9f31e1bd and passes on f70d393c7, with nothing else changed between the two runs.
Projecttesting-cabal/mock
Fix saved2010-10-18
Sharing licenceBSD-2-Clause · LICENSE.txt
Change size+740 −731
What the code was meant to do, written into the code itself as a save note
Attempting to set an unsupported magic method now raises an AttributeError Change version number to 0.7.0 beta 4 Change copying test to work on Python 3 (no sys.maxint)
The change
| 23 | 23 | 'DEFAULT' | |
| 24 | 24 | ) | |
| 25 | 25 | ||
| 26 | - | __version__ = '0.7.0b3' | |
| 27 | - | ||
| 28 | - | import sys | |
| 29 | - | import warnings | |
| 30 | - | ||
| 31 | - | try: | |
| 32 | - | import inspect | |
| 33 | - | except ImportError: | |
| 34 | - | # for alternative platforms that | |
| 35 | - | # may not have inspect | |
| 36 | - | inspect = None | |
| 37 | - | ||
| 38 | - | try: | |
| 39 | - | BaseException | |
| 40 | - | except NameError: | |
| 41 | - | # Python 2.4 compatibility | |
| 42 | - | BaseException = Exception | |
| 43 | - | ||
| 44 | - | try: | |
| 45 | - | from functools import wraps | |
| 46 | - | except ImportError: | |
| 47 | - | # Python 2.4 compatibility | |
| 48 | - | def wraps(original): | |
| 49 | - | def inner(f): | |
| 50 | - | f.__name__ = original.__name__ | |
| 51 | - | return f | |
| 52 | - | return inner | |
| 53 | - | ||
| 54 | - | try: | |
| 55 | - | unicode | |
| 56 | - | except NameError: | |
| 57 | - | # Python 3 | |
| 58 | - | basestring = unicode = str | |
| 59 | - | ||
| 60 | - | try: | |
| 61 | - | long | |
| 62 | - | except NameError: | |
| 63 | - | # Python 3 | |
| 64 | - | long = int | |
| 65 | - | ||
| 66 | - | inPy3k = sys.version_info[0] == 3 | |
| 67 | - | ||
| 68 | - | if inPy3k: | |
| 69 | - | self = '__self__' | |
| 70 | - | else: | |
| 71 | - | self = 'im_self' | |
| 72 | - | ||
| 73 | - | ||
| 74 | - | # getsignature and mocksignature heavily "inspired" by | |
| 75 | - | # the decorator module: http://pypi.python.org/pypi/decorator/ | |
| 76 | - | # by Michele Simionato | |
| 77 | - | ||
| 78 | - | def _getsignature(func, skipfirst): | |
| 79 | - | if inspect is None: | |
| 80 | - | raise ImportError('inspect module not available') | |
| 81 | - | ||
| 82 | - | if inspect.isclass(func): | |
| 83 | - | func = func.__init__ | |
| 84 | - | # will have a self arg | |
| 85 | - | skipfirst = True | |
| 86 | - | elif not (inspect.ismethod(func) or inspect.isfunction(func)): | |
| 87 | - | func = func.__call__ | |
| 88 | - | ||
| 89 | - | regargs, varargs, varkwargs, defaults = inspect.getargspec(func) | |
| 90 | - | ||
| 91 | - | # instance methods need to lose the self argument | |
| 92 | - | if getattr(func, self, None) is not None: | |
| 93 | - | regargs = regargs[1:] | |
| 94 | - | ||
| 95 | - | _msg = "_mock_ is a reserved argument name, can't mock signatures using _mock_" | |
| 96 | - | assert '_mock_' not in regargs, _msg | |
| 97 | - | if varargs is not None: | |
| 98 | - | assert '_mock_' not in varargs, _msg | |
| 99 | - | if varkwargs is not None: | |
| 100 | - | assert '_mock_' not in varkwargs, _msg | |
| 101 | - | if skipfirst: | |
| 102 | - | regargs = regargs[1:] | |
| 103 | - | signature = inspect.formatargspec(regargs, varargs, varkwargs, defaults, formatvalue=lambda value: "") | |
| 104 | - | return signature[1:-1], func | |
| 105 | - | ||
| 106 | - | ||
| 107 | - | def _copy_func_details(func, funcopy): | |
| 108 | - | funcopy.__name__ = func.__name__ | |
| 109 | - | funcopy.__doc__ = func.__doc__ | |
| 110 | - | funcopy.__dict__.update(func.__dict__) | |
| 111 | - | funcopy.__module__ = func.__module__ | |
| 112 | - | if not inPy3k: | |
| 113 | - | funcopy.func_defaults = func.func_defaults | |
| 114 | - | else: | |
| 115 | - | funcopy.__defaults__ = func.__defaults__ | |
| 116 | - | funcopy.__kwdefaults__ = func.__kwdefaults__ | |
| 117 | - | ||
| 118 | - | ||
| 119 | - | def mocksignature(func, mock=None, skipfirst=False): | |
| 120 | - | """ | |
| 121 | - | mocksignature(func, mock=None, skipfirst=False) | |
| 122 | - | ||
| 123 | - | Create a new function with the same signature as `func` that delegates | |
| 124 | - | to `mock`. If `skipfirst` is True the first argument is skipped, useful | |
| 125 | - | for methods where `self` needs to be omitted from the new function. | |
| 126 | - | ||
| 127 | - | If you don't pass in a `mock` then one will be created for you. | |
| 128 | - | ||
| 129 | - | The mock is set as the `mock` attribute of the returned function for easy | |
| 130 | - | access. | |
| 131 | - | ||
| 132 | - | `mocksignature` can also be used with classes. It copies the signature of | |
| 133 | - | the `__init__` method. | |
| 134 | - | ||
| 135 | - | When used with callable objects (instances) it copies the signature of the | |
| 136 | - | `__call__` method. | |
| 137 | - | """ | |
| 138 | - | if mock is None: | |
| 139 | - | mock = Mock() | |
| 140 | - | signature, func = _getsignature(func, skipfirst) | |
| 141 | - | src = "lambda %(signature)s: _mock_(%(signature)s)" % {'signature': signature} | |
| 142 | - | ||
| 143 | - | funcopy = eval(src, dict(_mock_=mock)) | |
| 144 | - | _copy_func_details(func, funcopy) | |
| 145 | - | funcopy.mock = mock | |
| 146 | - | return funcopy | |
| 147 | - | ||
| 148 | - | ||
| 149 | - | def _is_magic(name): | |
| 150 | - | return '__%s__' % name[2:-2] == name | |
| 151 | - | ||
| 152 | - | ||
| 153 | - | class SentinelObject(object): | |
| 154 | - | "A unique, named, sentinel object." | |
| 155 | - | def __init__(self, name): | |
| 156 | - | self.name = name | |
| 157 | - | ||
| 158 | - | def __repr__(self): | |
| 159 | - | return '<SentinelObject "%s">' % self.name | |
| 160 | - | ||
| 161 | - | ||
| 162 | - | class Sentinel(object): | |
| 163 | - | """Access attributes to return a named object, usable as a sentinel.""" | |
| 164 | - | def __init__(self): | |
| 165 | - | self._sentinels = {} | |
| 166 | - | ||
| 167 | - | def __getattr__(self, name): | |
| 168 | - | if name == '__bases__': | |
| 169 | - | # Without this help(mock) raises an exception | |
| 170 | - | raise AttributeError | |
| 171 | - | return self._sentinels.setdefault(name, SentinelObject(name)) | |
| 172 | - | ||
| 173 | - | ||
| 174 | - | sentinel = Sentinel() | |
| 175 | - | ||
| 176 | - | DEFAULT = sentinel.DEFAULT | |
| 177 | - | ||
| 178 | - | class OldStyleClass: | |
| 179 | - | pass | |
| 180 | - | ClassType = type(OldStyleClass) | |
| 181 | - | ||
| 182 | - | def _copy(value): | |
| 183 | - | if type(value) in (dict, list, tuple, set): | |
| 184 | - | return type(value)(value) | |
| 185 | - | return value | |
| 186 | - | ||
| 187 | - | ||
| 188 | - | if inPy3k: | |
| 189 | - | class_types = type | |
| 190 | - | else: | |
| 191 | - | class_types = (type, ClassType) | |
| 192 | - | ||
| 193 | - | ||
| 194 | - | class Mock(object): | |
| 195 | - | """ | |
| 196 | - | Mock(spec=None, side_effect=None, return_value=DEFAULT, wraps=None, name=None) | |
| 197 | - | ||
| 198 | - | Create a new ``Mock`` object. ``Mock`` takes several optional arguments | |
| 199 | - | that specify the behaviour of the Mock object: | |
| 200 | - | ||
| 201 | - | * ``spec``: This can be either a list of strings or an existing object (a | |
| 202 | - | class or instance) that acts as the specification for the mock object. If | |
| 203 | - | you pass in an object then a list of strings is formed by calling dir on | |
| 204 | - | the object (excluding unsupported magic attributes and methods). Accessing | |
| 205 | - | any attribute not in this list will raise an ``AttributeError``. | |
| 206 | - | ||
| 207 | - | If ``spec`` is an object (rather than a list of strings) then | |
| 208 | - | `mock.__class__` returns the class of the spec object. This allows mocks | |
| 209 | - | to pass `isinstance` tests. | |
| 210 | - | ||
| 211 | - | * ``side_effect``: A function to be called whenever the Mock is called. See | |
| 212 | - | the :attr:`Mock.side_effect` attribute. Useful for raising exceptions or | |
| 213 | - | dynamically changing return values. The function is called with the same | |
| 214 | - | arguments as the mock, and unless it returns :data:`DEFAULT`, the return | |
| 215 | - | value of this function is used as the return value. | |
| 216 | - | ||
| 217 | - | Alternatively ``side_effect`` can be an exception class or instance. In | |
| 218 | - | this case the exception will be raised when the mock is called. | |
| 219 | - | ||
| 220 | - | * ``return_value``: The value returned when the mock is called. By default | |
| 221 | - | this is a new Mock (created on first access). See the | |
| 222 | - | :attr:`Mock.return_value` attribute. | |
| 223 | - | ||
| 224 | - | * ``wraps``: Item for the mock object to wrap. If ``wraps`` is not None | |
| 225 | - | then calling the Mock will pass the call through to the wrapped object | |
| 226 | - | (returning the real result and ignoring ``return_value``). Attribute | |
| 227 | - | access on the mock will return a Mock object that wraps the corresponding | |
| 228 | - | attribute of the wrapped object (so attempting to access an attribute that | |
| 229 | - | doesn't exist will raise an ``AttributeError``). | |
| 230 | - | ||
| 231 | - | If the mock has an explicit ``return_value`` set then calls are not passed | |
| 232 | - | to the wrapped object and the ``return_value`` is returned instead. | |
| 233 | - | ||
| 234 | - | * ``name``: If the mock has a name then it will be used in the repr of the | |
| 235 | - | mock. This can be useful for debugging. | |
| 236 | - | """ | |
| 237 | - | def __new__(cls, *args, **kw): | |
| 238 | - | # every instance has its own class | |
| 239 | - | # so we can create magic methods on the | |
| 240 | - | # class without stomping on other mocks | |
| 241 | - | new = type(cls.__name__, (cls,), {'__doc__': cls.__doc__}) | |
| 242 | - | return object.__new__(new) | |
| 243 | - | ||
| 244 | - | def __init__(self, spec=None, side_effect=None, return_value=DEFAULT, | |
| 245 | - | wraps=None, name=None, parent=None): | |
| 246 | - | self._parent = parent | |
| 247 | - | self._name = name | |
| 248 | - | _spec_class = None | |
| 249 | - | if spec is not None and not isinstance(spec, list): | |
| 250 | - | if isinstance(spec, type): | |
| 251 | - | _spec_class = spec | |
| 252 | - | else: | |
| 253 | - | _spec_class = spec.__class__ | |
| 254 | - | spec = dir(spec) | |
| 255 | - | ||
| 256 | - | self._spec_class = _spec_class | |
| 257 | - | self._methods = spec | |
| 258 | - | self._children = {} | |
| 259 | - | self._return_value = return_value | |
| 260 | - | self.side_effect = side_effect | |
| 261 | - | self._wraps = wraps | |
| 262 | - | ||
| 263 | - | self.reset_mock() | |
| 264 | - | ||
| 265 | - | @property | |
| 266 | - | def __class__(self): | |
| 267 | - | if self._spec_class is None: | |
| 268 | - | return type(self) | |
| 269 | - | return self._spec_class | |
| 270 | - | ||
| 271 | - | def reset_mock(self): | |
| 272 | - | "Restore the mock object to its initial state." | |
| 273 | - | self.called = False | |
| 274 | - | self.call_args = None | |
| 275 | - | self.call_count = 0 | |
| 276 | - | self.call_args_list = [] | |
| 277 | - | self.method_calls = [] | |
| 278 | - | for child in self._children.values(): | |
| 279 | - | child.reset_mock() | |
| 280 | - | if isinstance(self._return_value, Mock): | |
| 281 | - | self._return_value.reset_mock() | |
| 282 | - | ||
| 283 | - | ||
| 284 | - | def __get_return_value(self): | |
| 285 | - | if self._return_value is DEFAULT: | |
| 286 | - | self._return_value = Mock() | |
| 287 | - | return self._return_value | |
| 288 | - | ||
| 289 | - | def __set_return_value(self, value): | |
| 290 | - | self._return_value = value | |
| 291 | - | ||
| 292 | - | __return_value_doc = "The value to be returned when the mock is called." | |
| 293 | - | return_value = property(__get_return_value, __set_return_value, | |
| 294 | - | __return_value_doc) | |
| 295 | - | ||
| 296 | - | ||
| 297 | - | def __call__(self, *args, **kwargs): | |
| 298 | - | self.called = True | |
| 299 | - | self.call_count += 1 | |
| 300 | - | self.call_args = callargs((args, kwargs)) | |
| 301 | - | self.call_args_list.append(callargs((args, kwargs))) | |
| 302 | - | ||
| 303 | - | parent = self._parent | |
| 304 | - | name = self._name | |
| 305 | - | while parent is not None: | |
| 306 | - | parent.method_calls.append(callargs((name, args, kwargs))) | |
| 307 | - | if parent._parent is None: | |
| 308 | - | break | |
| 309 | - | name = parent._name + '.' + name | |
| 310 | - | parent = parent._parent | |
| 311 | - | ||
| 312 | - | ret_val = DEFAULT | |
| 313 | - | if self.side_effect is not None: | |
| 314 | - | if (isinstance(self.side_effect, BaseException) or | |
| 315 | - | isinstance(self.side_effect, class_types) and | |
| 316 | - | issubclass(self.side_effect, BaseException)): | |
| 317 | - | raise self.side_effect | |
| 318 | - | ||
| 319 | - | ret_val = self.side_effect(*args, **kwargs) | |
| 320 | - | if ret_val is DEFAULT: | |
| 321 | - | ret_val = self.return_value | |
| 322 | - | ||
| 323 | - | if self._wraps is not None and self._return_value is DEFAULT: | |
| 324 | - | return self._wraps(*args, **kwargs) | |
| 325 | - | if ret_val is DEFAULT: | |
| 326 | - | ret_val = self.return_value | |
| 327 | - | return ret_val | |
| 328 | - | ||
| 329 | - | ||
| 330 | - | def __getattr__(self, name): | |
| 331 | - | if name == '_methods': | |
| 332 | - | raise AttributeError(name) | |
| 333 | - | elif self._methods is not None: | |
| 334 | - | if name not in self._methods or name in _all_magics: | |
| 335 | - | raise AttributeError("Mock object has no attribute '%s'" % name) | |
| 336 | - | elif _is_magic(name): | |
| 337 | - | raise AttributeError(name) | |
| 338 | - | ||
| 339 | - | if name not in self._children: | |
| 340 | - | wraps = None | |
| 341 | - | if self._wraps is not None: | |
| 342 | - | wraps = getattr(self._wraps, name) | |
| 343 | - | self._children[name] = Mock(parent=self, name=name, wraps=wraps) | |
| 344 | - | ||
| 345 | - | return self._children[name] | |
| 346 | - | ||
| 347 | - | def __repr__(self): | |
| 348 | - | if self._name is None: | |
| 349 | - | return object.__repr__(self) | |
| 350 | - | ||
| 351 | - | def get_name(name): | |
| 352 | - | if name is None: | |
| 353 | - | return 'mock' | |
| 354 | - | return name | |
| 355 | - | parent = self._parent | |
| 356 | - | name = self._name | |
| 357 | - | while parent is not None: | |
| 358 | - | name = get_name(parent._name) + '.' + name | |
| 359 | - | parent = parent._parent | |
| 360 | - | return "<%s name=%r id='%s'>" % (type(self).__name__, name, id(self)) | |
| 361 | - | ||
| 362 | - | def __setattr__(self, name, value): | |
| 363 | - | if name in _all_magics: | |
| 364 | - | if self._methods is not None and name not in self._methods: | |
| 365 | - | raise AttributeError("Mock object has no attribute '%s'" % name) | |
| 366 | - | ||
| 367 | - | if not isinstance(value, Mock): | |
| 368 | - | setattr(type(self), name, get_method(name, value)) | |
| 369 | - | original = value | |
| 370 | - | real = lambda *args, **kw: original(self, *args, **kw) | |
| 371 | - | value = mocksignature(value, real, skipfirst=True) | |
| 372 | - | else: | |
| 373 | - | setattr(type(self), name, value) | |
| 374 | - | return object.__setattr__(self, name, value) | |
| 375 | - | ||
| 376 | - | def __delattr__(self, name): | |
| 377 | - | if name in _all_magics and name in type(self).__dict__: | |
| 378 | - | delattr(type(self), name) | |
| 379 | - | return object.__delattr__(self, name) | |
| 380 | - | ||
| 381 | - | def assert_called_with(self, *args, **kwargs): | |
| 382 | - | """ | |
| 383 | - | assert that the mock was called with the specified arguments. | |
| 384 | - | ||
| 385 | - | Raises an AttributeError if the args and keyword args passed in are | |
| 386 | - | different to the last call to the mock. | |
| 387 | - | """ | |
| 388 | - | if self.call_args is None: | |
| 389 | - | raise AssertionError('Expected: %s\nNot called' % ((args, kwargs),)) | |
| 390 | - | assert self.call_args == (args, kwargs), 'Expected: %s\nCalled with: %s' % ((args, kwargs), self.call_args) | |
| 391 | - | ||
| 392 | - | ||
| 393 | - | class callargs(tuple): | |
| 394 | - | """ | |
| 395 | - | A tuple for holding the results of a call to a mock, either in the form | |
| 396 | - | `(args, kwargs)` or `(name, args, kwargs)`. | |
| 397 | - | ||
| 398 | - | If args or kwargs are empty then a callargs tuple will compare equal to | |
| 399 | - | a tuple without those values. This makes comparisons less verbose:: | |
| 400 | - | ||
| 401 | - | callargs('name', (), {}) == ('name',) | |
| 402 | - | callargs('name', (1,), {}) == ('name', (1,)) | |
| 403 | - | callargs((), {'a': 'b'}) == ({'a': 'b'},) | |
| 404 | - | """ | |
| 405 | - | def __eq__(self, other): | |
| 406 | - | if len(self) == 3: | |
| 407 | - | if other[0] != self[0]: | |
| 408 | - | return False | |
| 409 | - | args_kwargs = self[1:] | |
| 410 | - | other_args_kwargs = other[1:] | |
| 411 | - | else: | |
| 412 | - | args_kwargs = tuple(self) | |
| 413 | - | other_args_kwargs = other | |
| 414 | - | ||
| 415 | - | if len(other_args_kwargs) == 0: | |
| 416 | - | other_args, other_kwargs = (), {} | |
| 417 | - | elif len(other_args_kwargs) == 1: | |
| 418 | - | if isinstance(other_args_kwargs[0], tuple): | |
| 419 | - | other_args = other_args_kwargs[0] | |
| 420 | - | other_kwargs = {} | |
| 421 | - | else: | |
| 422 | - | other_args = () | |
| 1077 further changed lines not shown | |||
The check that tells the two apart
fail→pass·tests/testmagicmethods.py::TestMockingMagicMethods::testSettingUnsupportedMagicMethod
Check file tests/testmagicmethods.py, taken without changes from the fix and copied onto the older code, so the exact same check runs against both versions.
Origin and history
The code before itf9f31e1bddfc605437ad217dc2d536535ee6ce2c
Broken version dated2010-10-18
Modulemock
Units changedMock
Fingerprint4631dac457fbe879
Checked2026-08-18 by goldset/0.1
Every field above is generated by our program. None of it is written by hand.
Other bugs found in testing-cabal/mock
- 2023-04-16_get_signature_object
- 2022-12-28create_autospec
- 2020-01-29_patch_dict
- 2020-01-29_patch_object
- 2012-01-22_Call
- 2011-07-19Fix callargs call comparisons