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.

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

2323 'DEFAULT'
2424 )
2525
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

failpass·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