Merge lp:~vds/desktopcouch/remove_mocker into lp:desktopcouch
- remove_mocker
- Merge into trunk
Proposed by
Vincenzo Di Somma
Status: | Merged | ||||
---|---|---|---|---|---|
Approved by: | Eric Casteleijn | ||||
Approved revision: | 188 | ||||
Merged at revision: | 191 | ||||
Proposed branch: | lp:~vds/desktopcouch/remove_mocker | ||||
Merge into: | lp:desktopcouch | ||||
Diff against target: |
2073 lines (+0/-2068) 1 file modified
contrib/mocker.py (+0/-2068) |
||||
To merge this branch: | bzr merge lp:~vds/desktopcouch/remove_mocker | ||||
Related bugs: |
|
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Eric Casteleijn (community) | Approve | ||
Manuel de la Peña (community) | Approve | ||
Review via email: mp+39796@code.launchpad.net |
Commit message
Getting rid of mocker in contrib. It's in the distribution now.
Description of the change
Getting rid of mocker in contrib. It's in the distribution now.
To post a comment you must log in.
- 188. By Vincenzo Di Somma
-
we don't want a contribolder
Preview Diff
[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1 | === removed directory 'contrib' |
2 | === removed file 'contrib/mocker.py' |
3 | --- contrib/mocker.py 2009-08-06 13:57:30 +0000 |
4 | +++ contrib/mocker.py 1970-01-01 00:00:00 +0000 |
5 | @@ -1,2068 +0,0 @@ |
6 | -""" |
7 | -Copyright (c) 2007 Gustavo Niemeyer <gustavo@niemeyer.net> |
8 | - |
9 | -Graceful platform for test doubles in Python (mocks, stubs, fakes, and dummies). |
10 | -""" |
11 | -import __builtin__ |
12 | -import tempfile |
13 | -import unittest |
14 | -import inspect |
15 | -import shutil |
16 | -import types |
17 | -import sys |
18 | -import os |
19 | -import gc |
20 | - |
21 | - |
22 | -if sys.version_info < (2, 4): |
23 | - from sets import Set as set # pragma: nocover |
24 | - |
25 | - |
26 | -__all__ = ["Mocker", "expect", "IS", "CONTAINS", "IN", "MATCH", |
27 | - "ANY", "ARGS", "KWARGS"] |
28 | - |
29 | - |
30 | -__author__ = "Gustavo Niemeyer <gustavo@niemeyer.net>" |
31 | -__license__ = "PSF License" |
32 | -__version__ = "0.10.1" |
33 | - |
34 | - |
35 | -ERROR_PREFIX = "[Mocker] " |
36 | - |
37 | - |
38 | -# -------------------------------------------------------------------- |
39 | -# Exceptions |
40 | - |
41 | -class MatchError(AssertionError): |
42 | - """Raised when an unknown expression is seen in playback mode.""" |
43 | - |
44 | - |
45 | -# -------------------------------------------------------------------- |
46 | -# Helper for chained-style calling. |
47 | - |
48 | -class expect(object): |
49 | - """This is a simple helper that allows a different call-style. |
50 | - |
51 | - With this class one can comfortably do chaining of calls to the |
52 | - mocker object responsible by the object being handled. For instance:: |
53 | - |
54 | - expect(obj.attr).result(3).count(1, 2) |
55 | - |
56 | - Is the same as:: |
57 | - |
58 | - obj.attr |
59 | - mocker.result(3) |
60 | - mocker.count(1, 2) |
61 | - |
62 | - """ |
63 | - |
64 | - def __init__(self, mock, attr=None): |
65 | - self._mock = mock |
66 | - self._attr = attr |
67 | - |
68 | - def __getattr__(self, attr): |
69 | - return self.__class__(self._mock, attr) |
70 | - |
71 | - def __call__(self, *args, **kwargs): |
72 | - getattr(self._mock.__mocker__, self._attr)(*args, **kwargs) |
73 | - return self |
74 | - |
75 | - |
76 | -# -------------------------------------------------------------------- |
77 | -# Extensions to Python's unittest. |
78 | - |
79 | -class MockerTestCase(unittest.TestCase): |
80 | - """unittest.TestCase subclass with Mocker support. |
81 | - |
82 | - @ivar mocker: The mocker instance. |
83 | - |
84 | - This is a convenience only. Mocker may easily be used with the |
85 | - standard C{unittest.TestCase} class if wanted. |
86 | - |
87 | - Test methods have a Mocker instance available on C{self.mocker}. |
88 | - At the end of each test method, expectations of the mocker will |
89 | - be verified, and any requested changes made to the environment |
90 | - will be restored. |
91 | - |
92 | - In addition to the integration with Mocker, this class provides |
93 | - a few additional helper methods. |
94 | - """ |
95 | - |
96 | - expect = expect |
97 | - |
98 | - def __init__(self, methodName="runTest"): |
99 | - # So here is the trick: we take the real test method, wrap it on |
100 | - # a function that do the job we have to do, and insert it in the |
101 | - # *instance* dictionary, so that getattr() will return our |
102 | - # replacement rather than the class method. |
103 | - test_method = getattr(self, methodName, None) |
104 | - if test_method is not None: |
105 | - def test_method_wrapper(): |
106 | - try: |
107 | - result = test_method() |
108 | - except: |
109 | - raise |
110 | - else: |
111 | - if (self.mocker.is_recording() and |
112 | - self.mocker.get_events()): |
113 | - raise RuntimeError("Mocker must be put in replay " |
114 | - "mode with self.mocker.replay()") |
115 | - if (hasattr(result, "addCallback") and |
116 | - hasattr(result, "addErrback")): |
117 | - def verify(result): |
118 | - self.mocker.verify() |
119 | - return result |
120 | - result.addCallback(verify) |
121 | - else: |
122 | - self.mocker.verify() |
123 | - return result |
124 | - # Copy all attributes from the original method.. |
125 | - for attr in dir(test_method): |
126 | - # .. unless they're present in our wrapper already. |
127 | - if not hasattr(test_method_wrapper, attr) or attr == "__doc__": |
128 | - setattr(test_method_wrapper, attr, |
129 | - getattr(test_method, attr)) |
130 | - setattr(self, methodName, test_method_wrapper) |
131 | - |
132 | - # We could overload run() normally, but other well-known testing |
133 | - # frameworks do it as well, and some of them won't call the super, |
134 | - # which might mean that cleanup wouldn't happen. With that in mind, |
135 | - # we make integration easier by using the following trick. |
136 | - run_method = self.run |
137 | - def run_wrapper(*args, **kwargs): |
138 | - try: |
139 | - return run_method(*args, **kwargs) |
140 | - finally: |
141 | - self.__cleanup() |
142 | - self.run = run_wrapper |
143 | - |
144 | - self.mocker = Mocker() |
145 | - |
146 | - self.__cleanup_funcs = [] |
147 | - self.__cleanup_paths = [] |
148 | - |
149 | - super(MockerTestCase, self).__init__(methodName) |
150 | - |
151 | - def __cleanup(self): |
152 | - for path in self.__cleanup_paths: |
153 | - if os.path.isfile(path): |
154 | - os.unlink(path) |
155 | - elif os.path.isdir(path): |
156 | - shutil.rmtree(path) |
157 | - self.mocker.restore() |
158 | - for func, args, kwargs in self.__cleanup_funcs: |
159 | - func(*args, **kwargs) |
160 | - |
161 | - def addCleanup(self, func, *args, **kwargs): |
162 | - self.__cleanup_funcs.append((func, args, kwargs)) |
163 | - |
164 | - def makeFile(self, content=None, suffix="", prefix="tmp", basename=None, |
165 | - dirname=None, path=None): |
166 | - """Create a temporary file and return the path to it. |
167 | - |
168 | - @param content: Initial content for the file. |
169 | - @param suffix: Suffix to be given to the file's basename. |
170 | - @param prefix: Prefix to be given to the file's basename. |
171 | - @param basename: Full basename for the file. |
172 | - @param dirname: Put file inside this directory. |
173 | - |
174 | - The file is removed after the test runs. |
175 | - """ |
176 | - if path is not None: |
177 | - self.__cleanup_paths.append(path) |
178 | - elif basename is not None: |
179 | - if dirname is None: |
180 | - dirname = tempfile.mkdtemp() |
181 | - self.__cleanup_paths.append(dirname) |
182 | - path = os.path.join(dirname, basename) |
183 | - else: |
184 | - fd, path = tempfile.mkstemp(suffix, prefix, dirname) |
185 | - self.__cleanup_paths.append(path) |
186 | - os.close(fd) |
187 | - if content is None: |
188 | - os.unlink(path) |
189 | - if content is not None: |
190 | - file = open(path, "w") |
191 | - file.write(content) |
192 | - file.close() |
193 | - return path |
194 | - |
195 | - def makeDir(self, suffix="", prefix="tmp", dirname=None, path=None): |
196 | - """Create a temporary directory and return the path to it. |
197 | - |
198 | - @param suffix: Suffix to be given to the file's basename. |
199 | - @param prefix: Prefix to be given to the file's basename. |
200 | - @param dirname: Put directory inside this parent directory. |
201 | - |
202 | - The directory is removed after the test runs. |
203 | - """ |
204 | - if path is not None: |
205 | - os.makedirs(path) |
206 | - else: |
207 | - path = tempfile.mkdtemp(suffix, prefix, dirname) |
208 | - self.__cleanup_paths.append(path) |
209 | - return path |
210 | - |
211 | - def failUnlessIs(self, first, second, msg=None): |
212 | - """Assert that C{first} is the same object as C{second}.""" |
213 | - if first is not second: |
214 | - raise self.failureException(msg or "%r is not %r" % (first, second)) |
215 | - |
216 | - def failIfIs(self, first, second, msg=None): |
217 | - """Assert that C{first} is not the same object as C{second}.""" |
218 | - if first is second: |
219 | - raise self.failureException(msg or "%r is %r" % (first, second)) |
220 | - |
221 | - def failUnlessIn(self, first, second, msg=None): |
222 | - """Assert that C{first} is contained in C{second}.""" |
223 | - if first not in second: |
224 | - raise self.failureException(msg or "%r not in %r" % (first, second)) |
225 | - |
226 | - def failUnlessStartsWith(self, first, second, msg=None): |
227 | - """Assert that C{first} starts with C{second}.""" |
228 | - if first[:len(second)] != second: |
229 | - raise self.failureException(msg or "%r doesn't start with %r" % |
230 | - (first, second)) |
231 | - |
232 | - def failIfStartsWith(self, first, second, msg=None): |
233 | - """Assert that C{first} doesn't start with C{second}.""" |
234 | - if first[:len(second)] == second: |
235 | - raise self.failureException(msg or "%r starts with %r" % |
236 | - (first, second)) |
237 | - |
238 | - def failUnlessEndsWith(self, first, second, msg=None): |
239 | - """Assert that C{first} starts with C{second}.""" |
240 | - if first[len(first)-len(second):] != second: |
241 | - raise self.failureException(msg or "%r doesn't end with %r" % |
242 | - (first, second)) |
243 | - |
244 | - def failIfEndsWith(self, first, second, msg=None): |
245 | - """Assert that C{first} doesn't start with C{second}.""" |
246 | - if first[len(first)-len(second):] == second: |
247 | - raise self.failureException(msg or "%r ends with %r" % |
248 | - (first, second)) |
249 | - |
250 | - def failIfIn(self, first, second, msg=None): |
251 | - """Assert that C{first} is not contained in C{second}.""" |
252 | - if first in second: |
253 | - raise self.failureException(msg or "%r in %r" % (first, second)) |
254 | - |
255 | - def failUnlessApproximates(self, first, second, tolerance, msg=None): |
256 | - """Assert that C{first} is near C{second} by at most C{tolerance}.""" |
257 | - if abs(first - second) > tolerance: |
258 | - raise self.failureException(msg or "abs(%r - %r) > %r" % |
259 | - (first, second, tolerance)) |
260 | - |
261 | - def failIfApproximates(self, first, second, tolerance, msg=None): |
262 | - """Assert that C{first} is far from C{second} by at least C{tolerance}. |
263 | - """ |
264 | - if abs(first - second) <= tolerance: |
265 | - raise self.failureException(msg or "abs(%r - %r) <= %r" % |
266 | - (first, second, tolerance)) |
267 | - |
268 | - def failUnlessMethodsMatch(self, first, second): |
269 | - """Assert that public methods in C{first} are present in C{second}. |
270 | - |
271 | - This method asserts that all public methods found in C{first} are also |
272 | - present in C{second} and accept the same arguments. C{first} may |
273 | - have its own private methods, though, and may not have all methods |
274 | - found in C{second}. Note that if a private method in C{first} matches |
275 | - the name of one in C{second}, their specification is still compared. |
276 | - |
277 | - This is useful to verify if a fake or stub class have the same API as |
278 | - the real class being simulated. |
279 | - """ |
280 | - first_methods = dict(inspect.getmembers(first, inspect.ismethod)) |
281 | - second_methods = dict(inspect.getmembers(second, inspect.ismethod)) |
282 | - for name, first_method in first_methods.items(): |
283 | - first_argspec = inspect.getargspec(first_method) |
284 | - first_formatted = inspect.formatargspec(*first_argspec) |
285 | - |
286 | - second_method = second_methods.get(name) |
287 | - if second_method is None: |
288 | - if name[:1] == "_": |
289 | - continue # First may have its own private methods. |
290 | - raise self.failureException("%s.%s%s not present in %s" % |
291 | - (first.__name__, name, first_formatted, second.__name__)) |
292 | - |
293 | - second_argspec = inspect.getargspec(second_method) |
294 | - if first_argspec != second_argspec: |
295 | - second_formatted = inspect.formatargspec(*second_argspec) |
296 | - raise self.failureException("%s.%s%s != %s.%s%s" % |
297 | - (first.__name__, name, first_formatted, |
298 | - second.__name__, name, second_formatted)) |
299 | - |
300 | - |
301 | - assertIs = failUnlessIs |
302 | - assertIsNot = failIfIs |
303 | - assertIn = failUnlessIn |
304 | - assertNotIn = failIfIn |
305 | - assertStartsWith = failUnlessStartsWith |
306 | - assertNotStartsWith = failIfStartsWith |
307 | - assertEndsWith = failUnlessEndsWith |
308 | - assertNotEndsWith = failIfEndsWith |
309 | - assertApproximates = failUnlessApproximates |
310 | - assertNotApproximates = failIfApproximates |
311 | - assertMethodsMatch = failUnlessMethodsMatch |
312 | - |
313 | - # The following are missing in Python < 2.4. |
314 | - assertTrue = unittest.TestCase.failUnless |
315 | - assertFalse = unittest.TestCase.failIf |
316 | - |
317 | - # The following is provided for compatibility with Twisted's trial. |
318 | - assertIdentical = assertIs |
319 | - assertNotIdentical = assertIsNot |
320 | - failUnlessIdentical = failUnlessIs |
321 | - failIfIdentical = failIfIs |
322 | - |
323 | - |
324 | -# -------------------------------------------------------------------- |
325 | -# Mocker. |
326 | - |
327 | -class classinstancemethod(object): |
328 | - |
329 | - def __init__(self, method): |
330 | - self.method = method |
331 | - |
332 | - def __get__(self, obj, cls=None): |
333 | - def bound_method(*args, **kwargs): |
334 | - return self.method(cls, obj, *args, **kwargs) |
335 | - return bound_method |
336 | - |
337 | - |
338 | -class MockerBase(object): |
339 | - """Controller of mock objects. |
340 | - |
341 | - A mocker instance is used to command recording and replay of |
342 | - expectations on any number of mock objects. |
343 | - |
344 | - Expectations should be expressed for the mock object while in |
345 | - record mode (the initial one) by using the mock object itself, |
346 | - and using the mocker (and/or C{expect()} as a helper) to define |
347 | - additional behavior for each event. For instance:: |
348 | - |
349 | - mock = mocker.mock() |
350 | - mock.hello() |
351 | - mocker.result("Hi!") |
352 | - mocker.replay() |
353 | - assert mock.hello() == "Hi!" |
354 | - mock.restore() |
355 | - mock.verify() |
356 | - |
357 | - In this short excerpt a mock object is being created, then an |
358 | - expectation of a call to the C{hello()} method was recorded, and |
359 | - when called the method should return the value C{10}. Then, the |
360 | - mocker is put in replay mode, and the expectation is satisfied by |
361 | - calling the C{hello()} method, which indeed returns 10. Finally, |
362 | - a call to the L{restore()} method is performed to undo any needed |
363 | - changes made in the environment, and the L{verify()} method is |
364 | - called to ensure that all defined expectations were met. |
365 | - |
366 | - The same logic can be expressed more elegantly using the |
367 | - C{with mocker:} statement, as follows:: |
368 | - |
369 | - mock = mocker.mock() |
370 | - mock.hello() |
371 | - mocker.result("Hi!") |
372 | - with mocker: |
373 | - assert mock.hello() == "Hi!" |
374 | - |
375 | - Also, the MockerTestCase class, which integrates the mocker on |
376 | - a unittest.TestCase subclass, may be used to reduce the overhead |
377 | - of controlling the mocker. A test could be written as follows:: |
378 | - |
379 | - class SampleTest(MockerTestCase): |
380 | - |
381 | - def test_hello(self): |
382 | - mock = self.mocker.mock() |
383 | - mock.hello() |
384 | - self.mocker.result("Hi!") |
385 | - self.mocker.replay() |
386 | - self.assertEquals(mock.hello(), "Hi!") |
387 | - """ |
388 | - |
389 | - _recorders = [] |
390 | - |
391 | - # For convenience only. |
392 | - on = expect |
393 | - |
394 | - class __metaclass__(type): |
395 | - def __init__(self, name, bases, dict): |
396 | - # Make independent lists on each subclass, inheriting from parent. |
397 | - self._recorders = list(getattr(self, "_recorders", ())) |
398 | - |
399 | - def __init__(self): |
400 | - self._recorders = self._recorders[:] |
401 | - self._events = [] |
402 | - self._recording = True |
403 | - self._ordering = False |
404 | - self._last_orderer = None |
405 | - |
406 | - def is_recording(self): |
407 | - """Return True if in recording mode, False if in replay mode. |
408 | - |
409 | - Recording is the initial state. |
410 | - """ |
411 | - return self._recording |
412 | - |
413 | - def replay(self): |
414 | - """Change to replay mode, where recorded events are reproduced. |
415 | - |
416 | - If already in replay mode, the mocker will be restored, with all |
417 | - expectations reset, and then put again in replay mode. |
418 | - |
419 | - An alternative and more comfortable way to replay changes is |
420 | - using the 'with' statement, as follows:: |
421 | - |
422 | - mocker = Mocker() |
423 | - <record events> |
424 | - with mocker: |
425 | - <reproduce events> |
426 | - |
427 | - The 'with' statement will automatically put mocker in replay |
428 | - mode, and will also verify if all events were correctly reproduced |
429 | - at the end (using L{verify()}), and also restore any changes done |
430 | - in the environment (with L{restore()}). |
431 | - |
432 | - Also check the MockerTestCase class, which integrates the |
433 | - unittest.TestCase class with mocker. |
434 | - """ |
435 | - if not self._recording: |
436 | - for event in self._events: |
437 | - event.restore() |
438 | - else: |
439 | - self._recording = False |
440 | - for event in self._events: |
441 | - event.replay() |
442 | - |
443 | - def restore(self): |
444 | - """Restore changes in the environment, and return to recording mode. |
445 | - |
446 | - This should always be called after the test is complete (succeeding |
447 | - or not). There are ways to call this method automatically on |
448 | - completion (e.g. using a C{with mocker:} statement, or using the |
449 | - L{MockerTestCase} class. |
450 | - """ |
451 | - if not self._recording: |
452 | - self._recording = True |
453 | - for event in self._events: |
454 | - event.restore() |
455 | - |
456 | - def reset(self): |
457 | - """Reset the mocker state. |
458 | - |
459 | - This will restore environment changes, if currently in replay |
460 | - mode, and then remove all events previously recorded. |
461 | - """ |
462 | - if not self._recording: |
463 | - self.restore() |
464 | - self.unorder() |
465 | - del self._events[:] |
466 | - |
467 | - def get_events(self): |
468 | - """Return all recorded events.""" |
469 | - return self._events[:] |
470 | - |
471 | - def add_event(self, event): |
472 | - """Add an event. |
473 | - |
474 | - This method is used internally by the implementation, and |
475 | - shouldn't be needed on normal mocker usage. |
476 | - """ |
477 | - self._events.append(event) |
478 | - if self._ordering: |
479 | - orderer = event.add_task(Orderer(event.path)) |
480 | - if self._last_orderer: |
481 | - orderer.add_dependency(self._last_orderer) |
482 | - self._last_orderer = orderer |
483 | - return event |
484 | - |
485 | - def verify(self): |
486 | - """Check if all expectations were met, and raise AssertionError if not. |
487 | - |
488 | - The exception message will include a nice description of which |
489 | - expectations were not met, and why. |
490 | - """ |
491 | - errors = [] |
492 | - for event in self._events: |
493 | - try: |
494 | - event.verify() |
495 | - except AssertionError, e: |
496 | - error = str(e) |
497 | - if not error: |
498 | - raise RuntimeError("Empty error message from %r" |
499 | - % event) |
500 | - errors.append(error) |
501 | - if errors: |
502 | - message = [ERROR_PREFIX + "Unmet expectations:", ""] |
503 | - for error in errors: |
504 | - lines = error.splitlines() |
505 | - message.append("=> " + lines.pop(0)) |
506 | - message.extend([" " + line for line in lines]) |
507 | - message.append("") |
508 | - raise AssertionError(os.linesep.join(message)) |
509 | - |
510 | - def mock(self, spec_and_type=None, spec=None, type=None, |
511 | - name=None, count=True): |
512 | - """Return a new mock object. |
513 | - |
514 | - @param spec_and_type: Handy positional argument which sets both |
515 | - spec and type. |
516 | - @param spec: Method calls will be checked for correctness against |
517 | - the given class. |
518 | - @param type: If set, the Mock's __class__ attribute will return |
519 | - the given type. This will make C{isinstance()} calls |
520 | - on the object work. |
521 | - @param name: Name for the mock object, used in the representation of |
522 | - expressions. The name is rarely needed, as it's usually |
523 | - guessed correctly from the variable name used. |
524 | - @param count: If set to false, expressions may be executed any number |
525 | - of times, unless an expectation is explicitly set using |
526 | - the L{count()} method. By default, expressions are |
527 | - expected once. |
528 | - """ |
529 | - if spec_and_type is not None: |
530 | - spec = type = spec_and_type |
531 | - return Mock(self, spec=spec, type=type, name=name, count=count) |
532 | - |
533 | - def proxy(self, object, spec=True, type=True, name=None, count=True, |
534 | - passthrough=True): |
535 | - """Return a new mock object which proxies to the given object. |
536 | - |
537 | - Proxies are useful when only part of the behavior of an object |
538 | - is to be mocked. Unknown expressions may be passed through to |
539 | - the real implementation implicitly (if the C{passthrough} argument |
540 | - is True), or explicitly (using the L{passthrough()} method |
541 | - on the event). |
542 | - |
543 | - @param object: Real object to be proxied, and replaced by the mock |
544 | - on replay mode. It may also be an "import path", |
545 | - such as C{"time.time"}, in which case the object |
546 | - will be the C{time} function from the C{time} module. |
547 | - @param spec: Method calls will be checked for correctness against |
548 | - the given object, which may be a class or an instance |
549 | - where attributes will be looked up. Defaults to the |
550 | - the C{object} parameter. May be set to None explicitly, |
551 | - in which case spec checking is disabled. Checks may |
552 | - also be disabled explicitly on a per-event basis with |
553 | - the L{nospec()} method. |
554 | - @param type: If set, the Mock's __class__ attribute will return |
555 | - the given type. This will make C{isinstance()} calls |
556 | - on the object work. Defaults to the type of the |
557 | - C{object} parameter. May be set to None explicitly. |
558 | - @param name: Name for the mock object, used in the representation of |
559 | - expressions. The name is rarely needed, as it's usually |
560 | - guessed correctly from the variable name used. |
561 | - @param count: If set to false, expressions may be executed any number |
562 | - of times, unless an expectation is explicitly set using |
563 | - the L{count()} method. By default, expressions are |
564 | - expected once. |
565 | - @param passthrough: If set to False, passthrough of actions on the |
566 | - proxy to the real object will only happen when |
567 | - explicitly requested via the L{passthrough()} |
568 | - method. |
569 | - """ |
570 | - if isinstance(object, basestring): |
571 | - if name is None: |
572 | - name = object |
573 | - import_stack = object.split(".") |
574 | - attr_stack = [] |
575 | - while import_stack: |
576 | - module_path = ".".join(import_stack) |
577 | - try: |
578 | - object = __import__(module_path, {}, {}, [""]) |
579 | - except ImportError: |
580 | - attr_stack.insert(0, import_stack.pop()) |
581 | - if not import_stack: |
582 | - raise |
583 | - continue |
584 | - else: |
585 | - for attr in attr_stack: |
586 | - object = getattr(object, attr) |
587 | - break |
588 | - if spec is True: |
589 | - spec = object |
590 | - if type is True: |
591 | - type = __builtin__.type(object) |
592 | - return Mock(self, spec=spec, type=type, object=object, |
593 | - name=name, count=count, passthrough=passthrough) |
594 | - |
595 | - def replace(self, object, spec=True, type=True, name=None, count=True, |
596 | - passthrough=True): |
597 | - """Create a proxy, and replace the original object with the mock. |
598 | - |
599 | - On replay, the original object will be replaced by the returned |
600 | - proxy in all dictionaries found in the running interpreter via |
601 | - the garbage collecting system. This should cover module |
602 | - namespaces, class namespaces, instance namespaces, and so on. |
603 | - |
604 | - @param object: Real object to be proxied, and replaced by the mock |
605 | - on replay mode. It may also be an "import path", |
606 | - such as C{"time.time"}, in which case the object |
607 | - will be the C{time} function from the C{time} module. |
608 | - @param spec: Method calls will be checked for correctness against |
609 | - the given object, which may be a class or an instance |
610 | - where attributes will be looked up. Defaults to the |
611 | - the C{object} parameter. May be set to None explicitly, |
612 | - in which case spec checking is disabled. Checks may |
613 | - also be disabled explicitly on a per-event basis with |
614 | - the L{nospec()} method. |
615 | - @param type: If set, the Mock's __class__ attribute will return |
616 | - the given type. This will make C{isinstance()} calls |
617 | - on the object work. Defaults to the type of the |
618 | - C{object} parameter. May be set to None explicitly. |
619 | - @param name: Name for the mock object, used in the representation of |
620 | - expressions. The name is rarely needed, as it's usually |
621 | - guessed correctly from the variable name used. |
622 | - @param passthrough: If set to False, passthrough of actions on the |
623 | - proxy to the real object will only happen when |
624 | - explicitly requested via the L{passthrough()} |
625 | - method. |
626 | - """ |
627 | - mock = self.proxy(object, spec, type, name, count, passthrough) |
628 | - event = self._get_replay_restore_event() |
629 | - event.add_task(ProxyReplacer(mock)) |
630 | - return mock |
631 | - |
632 | - def patch(self, object, spec=True): |
633 | - """Patch an existing object to reproduce recorded events. |
634 | - |
635 | - @param object: Class or instance to be patched. |
636 | - @param spec: Method calls will be checked for correctness against |
637 | - the given object, which may be a class or an instance |
638 | - where attributes will be looked up. Defaults to the |
639 | - the C{object} parameter. May be set to None explicitly, |
640 | - in which case spec checking is disabled. Checks may |
641 | - also be disabled explicitly on a per-event basis with |
642 | - the L{nospec()} method. |
643 | - |
644 | - The result of this method is still a mock object, which can be |
645 | - used like any other mock object to record events. The difference |
646 | - is that when the mocker is put on replay mode, the *real* object |
647 | - will be modified to behave according to recorded expectations. |
648 | - |
649 | - Patching works in individual instances, and also in classes. |
650 | - When an instance is patched, recorded events will only be |
651 | - considered on this specific instance, and other instances should |
652 | - behave normally. When a class is patched, the reproduction of |
653 | - events will be considered on any instance of this class once |
654 | - created (collectively). |
655 | - |
656 | - Observe that, unlike with proxies which catch only events done |
657 | - through the mock object, *all* accesses to recorded expectations |
658 | - will be considered; even these coming from the object itself |
659 | - (e.g. C{self.hello()} is considered if this method was patched). |
660 | - While this is a very powerful feature, and many times the reason |
661 | - to use patches in the first place, it's important to keep this |
662 | - behavior in mind. |
663 | - |
664 | - Patching of the original object only takes place when the mocker |
665 | - is put on replay mode, and the patched object will be restored |
666 | - to its original state once the L{restore()} method is called |
667 | - (explicitly, or implicitly with alternative conventions, such as |
668 | - a C{with mocker:} block, or a MockerTestCase class). |
669 | - """ |
670 | - if spec is True: |
671 | - spec = object |
672 | - patcher = Patcher() |
673 | - event = self._get_replay_restore_event() |
674 | - event.add_task(patcher) |
675 | - mock = Mock(self, object=object, patcher=patcher, |
676 | - passthrough=True, spec=spec) |
677 | - object.__mocker_mock__ = mock |
678 | - return mock |
679 | - |
680 | - def act(self, path): |
681 | - """This is called by mock objects whenever something happens to them. |
682 | - |
683 | - This method is part of the implementation between the mocker |
684 | - and mock objects. |
685 | - """ |
686 | - if self._recording: |
687 | - event = self.add_event(Event(path)) |
688 | - for recorder in self._recorders: |
689 | - recorder(self, event) |
690 | - return Mock(self, path) |
691 | - else: |
692 | - # First run events that may run, then run unsatisfied events, then |
693 | - # ones not previously run. We put the index in the ordering tuple |
694 | - # instead of the actual event because we want a stable sort |
695 | - # (ordering between 2 events is undefined). |
696 | - events = self._events |
697 | - order = [(events[i].satisfied()*2 + events[i].has_run(), i) |
698 | - for i in range(len(events))] |
699 | - order.sort() |
700 | - postponed = None |
701 | - for weight, i in order: |
702 | - event = events[i] |
703 | - if event.matches(path): |
704 | - if event.may_run(path): |
705 | - return event.run(path) |
706 | - elif postponed is None: |
707 | - postponed = event |
708 | - if postponed is not None: |
709 | - return postponed.run(path) |
710 | - raise MatchError(ERROR_PREFIX + "Unexpected expression: %s" % path) |
711 | - |
712 | - def get_recorders(cls, self): |
713 | - """Return recorders associated with this mocker class or instance. |
714 | - |
715 | - This method may be called on mocker instances and also on mocker |
716 | - classes. See the L{add_recorder()} method for more information. |
717 | - """ |
718 | - return (self or cls)._recorders[:] |
719 | - get_recorders = classinstancemethod(get_recorders) |
720 | - |
721 | - def add_recorder(cls, self, recorder): |
722 | - """Add a recorder to this mocker class or instance. |
723 | - |
724 | - @param recorder: Callable accepting C{(mocker, event)} as parameters. |
725 | - |
726 | - This is part of the implementation of mocker. |
727 | - |
728 | - All registered recorders are called for translating events that |
729 | - happen during recording into expectations to be met once the state |
730 | - is switched to replay mode. |
731 | - |
732 | - This method may be called on mocker instances and also on mocker |
733 | - classes. When called on a class, the recorder will be used by |
734 | - all instances, and also inherited on subclassing. When called on |
735 | - instances, the recorder is added only to the given instance. |
736 | - """ |
737 | - (self or cls)._recorders.append(recorder) |
738 | - return recorder |
739 | - add_recorder = classinstancemethod(add_recorder) |
740 | - |
741 | - def remove_recorder(cls, self, recorder): |
742 | - """Remove the given recorder from this mocker class or instance. |
743 | - |
744 | - This method may be called on mocker classes and also on mocker |
745 | - instances. See the L{add_recorder()} method for more information. |
746 | - """ |
747 | - (self or cls)._recorders.remove(recorder) |
748 | - remove_recorder = classinstancemethod(remove_recorder) |
749 | - |
750 | - def result(self, value): |
751 | - """Make the last recorded event return the given value on replay. |
752 | - |
753 | - @param value: Object to be returned when the event is replayed. |
754 | - """ |
755 | - self.call(lambda *args, **kwargs: value) |
756 | - |
757 | - def generate(self, sequence): |
758 | - """Last recorded event will return a generator with the given sequence. |
759 | - |
760 | - @param sequence: Sequence of values to be generated. |
761 | - """ |
762 | - def generate(*args, **kwargs): |
763 | - for value in sequence: |
764 | - yield value |
765 | - self.call(generate) |
766 | - |
767 | - def throw(self, exception): |
768 | - """Make the last recorded event raise the given exception on replay. |
769 | - |
770 | - @param exception: Class or instance of exception to be raised. |
771 | - """ |
772 | - def raise_exception(*args, **kwargs): |
773 | - raise exception |
774 | - self.call(raise_exception) |
775 | - |
776 | - def call(self, func): |
777 | - """Make the last recorded event cause the given function to be called. |
778 | - |
779 | - @param func: Function to be called. |
780 | - |
781 | - The result of the function will be used as the event result. |
782 | - """ |
783 | - self._events[-1].add_task(FunctionRunner(func)) |
784 | - |
785 | - def count(self, min, max=False): |
786 | - """Last recorded event must be replayed between min and max times. |
787 | - |
788 | - @param min: Minimum number of times that the event must happen. |
789 | - @param max: Maximum number of times that the event must happen. If |
790 | - not given, it defaults to the same value of the C{min} |
791 | - parameter. If set to None, there is no upper limit, and |
792 | - the expectation is met as long as it happens at least |
793 | - C{min} times. |
794 | - """ |
795 | - event = self._events[-1] |
796 | - for task in event.get_tasks(): |
797 | - if isinstance(task, RunCounter): |
798 | - event.remove_task(task) |
799 | - event.add_task(RunCounter(min, max)) |
800 | - |
801 | - def is_ordering(self): |
802 | - """Return true if all events are being ordered. |
803 | - |
804 | - See the L{order()} method. |
805 | - """ |
806 | - return self._ordering |
807 | - |
808 | - def unorder(self): |
809 | - """Disable the ordered mode. |
810 | - |
811 | - See the L{order()} method for more information. |
812 | - """ |
813 | - self._ordering = False |
814 | - self._last_orderer = None |
815 | - |
816 | - def order(self, *path_holders): |
817 | - """Create an expectation of order between two or more events. |
818 | - |
819 | - @param path_holders: Objects returned as the result of recorded events. |
820 | - |
821 | - By default, mocker won't force events to happen precisely in |
822 | - the order they were recorded. Calling this method will change |
823 | - this behavior so that events will only match if reproduced in |
824 | - the correct order. |
825 | - |
826 | - There are two ways in which this method may be used. Which one |
827 | - is used in a given occasion depends only on convenience. |
828 | - |
829 | - If no arguments are passed, the mocker will be put in a mode where |
830 | - all the recorded events following the method call will only be met |
831 | - if they happen in order. When that's used, the mocker may be put |
832 | - back in unordered mode by calling the L{unorder()} method, or by |
833 | - using a 'with' block, like so:: |
834 | - |
835 | - with mocker.ordered(): |
836 | - <record events> |
837 | - |
838 | - In this case, only expressions in <record events> will be ordered, |
839 | - and the mocker will be back in unordered mode after the 'with' block. |
840 | - |
841 | - The second way to use it is by specifying precisely which events |
842 | - should be ordered. As an example:: |
843 | - |
844 | - mock = mocker.mock() |
845 | - expr1 = mock.hello() |
846 | - expr2 = mock.world |
847 | - expr3 = mock.x.y.z |
848 | - mocker.order(expr1, expr2, expr3) |
849 | - |
850 | - This method of ordering only works when the expression returns |
851 | - another object. |
852 | - |
853 | - Also check the L{after()} and L{before()} methods, which are |
854 | - alternative ways to perform this. |
855 | - """ |
856 | - if not path_holders: |
857 | - self._ordering = True |
858 | - return OrderedContext(self) |
859 | - |
860 | - last_orderer = None |
861 | - for path_holder in path_holders: |
862 | - if type(path_holder) is Path: |
863 | - path = path_holder |
864 | - else: |
865 | - path = path_holder.__mocker_path__ |
866 | - for event in self._events: |
867 | - if event.path is path: |
868 | - for task in event.get_tasks(): |
869 | - if isinstance(task, Orderer): |
870 | - orderer = task |
871 | - break |
872 | - else: |
873 | - orderer = Orderer(path) |
874 | - event.add_task(orderer) |
875 | - if last_orderer: |
876 | - orderer.add_dependency(last_orderer) |
877 | - last_orderer = orderer |
878 | - break |
879 | - |
880 | - def after(self, *path_holders): |
881 | - """Last recorded event must happen after events referred to. |
882 | - |
883 | - @param path_holders: Objects returned as the result of recorded events |
884 | - which should happen before the last recorded event |
885 | - |
886 | - As an example, the idiom:: |
887 | - |
888 | - expect(mock.x).after(mock.y, mock.z) |
889 | - |
890 | - is an alternative way to say:: |
891 | - |
892 | - expr_x = mock.x |
893 | - expr_y = mock.y |
894 | - expr_z = mock.z |
895 | - mocker.order(expr_y, expr_x) |
896 | - mocker.order(expr_z, expr_x) |
897 | - |
898 | - See L{order()} for more information. |
899 | - """ |
900 | - last_path = self._events[-1].path |
901 | - for path_holder in path_holders: |
902 | - self.order(path_holder, last_path) |
903 | - |
904 | - def before(self, *path_holders): |
905 | - """Last recorded event must happen before events referred to. |
906 | - |
907 | - @param path_holders: Objects returned as the result of recorded events |
908 | - which should happen after the last recorded event |
909 | - |
910 | - As an example, the idiom:: |
911 | - |
912 | - expect(mock.x).before(mock.y, mock.z) |
913 | - |
914 | - is an alternative way to say:: |
915 | - |
916 | - expr_x = mock.x |
917 | - expr_y = mock.y |
918 | - expr_z = mock.z |
919 | - mocker.order(expr_x, expr_y) |
920 | - mocker.order(expr_x, expr_z) |
921 | - |
922 | - See L{order()} for more information. |
923 | - """ |
924 | - last_path = self._events[-1].path |
925 | - for path_holder in path_holders: |
926 | - self.order(last_path, path_holder) |
927 | - |
928 | - def nospec(self): |
929 | - """Don't check method specification of real object on last event. |
930 | - |
931 | - By default, when using a mock created as the result of a call to |
932 | - L{proxy()}, L{replace()}, and C{patch()}, or when passing the spec |
933 | - attribute to the L{mock()} method, method calls on the given object |
934 | - are checked for correctness against the specification of the real |
935 | - object (or the explicitly provided spec). |
936 | - |
937 | - This method will disable that check specifically for the last |
938 | - recorded event. |
939 | - """ |
940 | - event = self._events[-1] |
941 | - for task in event.get_tasks(): |
942 | - if isinstance(task, SpecChecker): |
943 | - event.remove_task(task) |
944 | - |
945 | - def passthrough(self, result_callback=None): |
946 | - """Make the last recorded event run on the real object once seen. |
947 | - |
948 | - @param result_callback: If given, this function will be called with |
949 | - the result of the *real* method call as the only argument. |
950 | - |
951 | - This can only be used on proxies, as returned by the L{proxy()} |
952 | - and L{replace()} methods, or on mocks representing patched objects, |
953 | - as returned by the L{patch()} method. |
954 | - """ |
955 | - event = self._events[-1] |
956 | - if event.path.root_object is None: |
957 | - raise TypeError("Mock object isn't a proxy") |
958 | - event.add_task(PathExecuter(result_callback)) |
959 | - |
960 | - def __enter__(self): |
961 | - """Enter in a 'with' context. This will run replay().""" |
962 | - self.replay() |
963 | - return self |
964 | - |
965 | - def __exit__(self, type, value, traceback): |
966 | - """Exit from a 'with' context. |
967 | - |
968 | - This will run restore() at all times, but will only run verify() |
969 | - if the 'with' block itself hasn't raised an exception. Exceptions |
970 | - in that block are never swallowed. |
971 | - """ |
972 | - self.restore() |
973 | - if type is None: |
974 | - self.verify() |
975 | - return False |
976 | - |
977 | - def _get_replay_restore_event(self): |
978 | - """Return unique L{ReplayRestoreEvent}, creating if needed. |
979 | - |
980 | - Some tasks only want to replay/restore. When that's the case, |
981 | - they shouldn't act on other events during replay. Also, they |
982 | - can all be put in a single event when that's the case. Thus, |
983 | - we add a single L{ReplayRestoreEvent} as the first element of |
984 | - the list. |
985 | - """ |
986 | - if not self._events or type(self._events[0]) != ReplayRestoreEvent: |
987 | - self._events.insert(0, ReplayRestoreEvent()) |
988 | - return self._events[0] |
989 | - |
990 | - |
991 | -class OrderedContext(object): |
992 | - |
993 | - def __init__(self, mocker): |
994 | - self._mocker = mocker |
995 | - |
996 | - def __enter__(self): |
997 | - return None |
998 | - |
999 | - def __exit__(self, type, value, traceback): |
1000 | - self._mocker.unorder() |
1001 | - |
1002 | - |
1003 | -class Mocker(MockerBase): |
1004 | - __doc__ = MockerBase.__doc__ |
1005 | - |
1006 | -# Decorator to add recorders on the standard Mocker class. |
1007 | -recorder = Mocker.add_recorder |
1008 | - |
1009 | - |
1010 | -# -------------------------------------------------------------------- |
1011 | -# Mock object. |
1012 | - |
1013 | -class Mock(object): |
1014 | - |
1015 | - def __init__(self, mocker, path=None, name=None, spec=None, type=None, |
1016 | - object=None, passthrough=False, patcher=None, count=True): |
1017 | - self.__mocker__ = mocker |
1018 | - self.__mocker_path__ = path or Path(self, object) |
1019 | - self.__mocker_name__ = name |
1020 | - self.__mocker_spec__ = spec |
1021 | - self.__mocker_object__ = object |
1022 | - self.__mocker_passthrough__ = passthrough |
1023 | - self.__mocker_patcher__ = patcher |
1024 | - self.__mocker_replace__ = False |
1025 | - self.__mocker_type__ = type |
1026 | - self.__mocker_count__ = count |
1027 | - |
1028 | - def __mocker_act__(self, kind, args=(), kwargs={}, object=None): |
1029 | - if self.__mocker_name__ is None: |
1030 | - self.__mocker_name__ = find_object_name(self, 2) |
1031 | - action = Action(kind, args, kwargs, self.__mocker_path__) |
1032 | - path = self.__mocker_path__ + action |
1033 | - if object is not None: |
1034 | - path.root_object = object |
1035 | - try: |
1036 | - return self.__mocker__.act(path) |
1037 | - except MatchError, exception: |
1038 | - root_mock = path.root_mock |
1039 | - if (path.root_object is not None and |
1040 | - root_mock.__mocker_passthrough__): |
1041 | - return path.execute(path.root_object) |
1042 | - # Reinstantiate to show raise statement on traceback, and |
1043 | - # also to make the traceback shown shorter. |
1044 | - raise MatchError(str(exception)) |
1045 | - except AssertionError, e: |
1046 | - lines = str(e).splitlines() |
1047 | - message = [ERROR_PREFIX + "Unmet expectation:", ""] |
1048 | - message.append("=> " + lines.pop(0)) |
1049 | - message.extend([" " + line for line in lines]) |
1050 | - message.append("") |
1051 | - raise AssertionError(os.linesep.join(message)) |
1052 | - |
1053 | - def __getattribute__(self, name): |
1054 | - if name.startswith("__mocker_"): |
1055 | - return super(Mock, self).__getattribute__(name) |
1056 | - if name == "__class__": |
1057 | - if self.__mocker__.is_recording() or self.__mocker_type__ is None: |
1058 | - return type(self) |
1059 | - return self.__mocker_type__ |
1060 | - return self.__mocker_act__("getattr", (name,)) |
1061 | - |
1062 | - def __setattr__(self, name, value): |
1063 | - if name.startswith("__mocker_"): |
1064 | - return super(Mock, self).__setattr__(name, value) |
1065 | - return self.__mocker_act__("setattr", (name, value)) |
1066 | - |
1067 | - def __delattr__(self, name): |
1068 | - return self.__mocker_act__("delattr", (name,)) |
1069 | - |
1070 | - def __call__(self, *args, **kwargs): |
1071 | - return self.__mocker_act__("call", args, kwargs) |
1072 | - |
1073 | - def __contains__(self, value): |
1074 | - return self.__mocker_act__("contains", (value,)) |
1075 | - |
1076 | - def __getitem__(self, key): |
1077 | - return self.__mocker_act__("getitem", (key,)) |
1078 | - |
1079 | - def __setitem__(self, key, value): |
1080 | - return self.__mocker_act__("setitem", (key, value)) |
1081 | - |
1082 | - def __delitem__(self, key): |
1083 | - return self.__mocker_act__("delitem", (key,)) |
1084 | - |
1085 | - def __len__(self): |
1086 | - # MatchError is turned on an AttributeError so that list() and |
1087 | - # friends act properly when trying to get length hints on |
1088 | - # something that doesn't offer them. |
1089 | - try: |
1090 | - result = self.__mocker_act__("len") |
1091 | - except MatchError, e: |
1092 | - raise AttributeError(str(e)) |
1093 | - if type(result) is Mock: |
1094 | - return 0 |
1095 | - return result |
1096 | - |
1097 | - def __nonzero__(self): |
1098 | - try: |
1099 | - return self.__mocker_act__("nonzero") |
1100 | - except MatchError, e: |
1101 | - return True |
1102 | - |
1103 | - def __iter__(self): |
1104 | - # XXX On py3k, when next() becomes __next__(), we'll be able |
1105 | - # to return the mock itself because it will be considered |
1106 | - # an iterator (we'll be mocking __next__ as well, which we |
1107 | - # can't now). |
1108 | - result = self.__mocker_act__("iter") |
1109 | - if type(result) is Mock: |
1110 | - return iter([]) |
1111 | - return result |
1112 | - |
1113 | - # When adding a new action kind here, also add support for it on |
1114 | - # Action.execute() and Path.__str__(). |
1115 | - |
1116 | - |
1117 | -def find_object_name(obj, depth=0): |
1118 | - """Try to detect how the object is named on a previous scope.""" |
1119 | - try: |
1120 | - frame = sys._getframe(depth+1) |
1121 | - except: |
1122 | - return None |
1123 | - for name, frame_obj in frame.f_locals.iteritems(): |
1124 | - if frame_obj is obj: |
1125 | - return name |
1126 | - self = frame.f_locals.get("self") |
1127 | - if self is not None: |
1128 | - try: |
1129 | - items = list(self.__dict__.iteritems()) |
1130 | - except: |
1131 | - pass |
1132 | - else: |
1133 | - for name, self_obj in items: |
1134 | - if self_obj is obj: |
1135 | - return name |
1136 | - return None |
1137 | - |
1138 | - |
1139 | -# -------------------------------------------------------------------- |
1140 | -# Action and path. |
1141 | - |
1142 | -class Action(object): |
1143 | - |
1144 | - def __init__(self, kind, args, kwargs, path=None): |
1145 | - self.kind = kind |
1146 | - self.args = args |
1147 | - self.kwargs = kwargs |
1148 | - self.path = path |
1149 | - self._execute_cache = {} |
1150 | - |
1151 | - def __repr__(self): |
1152 | - if self.path is None: |
1153 | - return "Action(%r, %r, %r)" % (self.kind, self.args, self.kwargs) |
1154 | - return "Action(%r, %r, %r, %r)" % \ |
1155 | - (self.kind, self.args, self.kwargs, self.path) |
1156 | - |
1157 | - def __eq__(self, other): |
1158 | - return (self.kind == other.kind and |
1159 | - self.args == other.args and |
1160 | - self.kwargs == other.kwargs) |
1161 | - |
1162 | - def __ne__(self, other): |
1163 | - return not self.__eq__(other) |
1164 | - |
1165 | - def matches(self, other): |
1166 | - return (self.kind == other.kind and |
1167 | - match_params(self.args, self.kwargs, other.args, other.kwargs)) |
1168 | - |
1169 | - def execute(self, object): |
1170 | - # This caching scheme may fail if the object gets deallocated before |
1171 | - # the action, as the id might get reused. It's somewhat easy to fix |
1172 | - # that with a weakref callback. For our uses, though, the object |
1173 | - # should never get deallocated before the action itself, so we'll |
1174 | - # just keep it simple. |
1175 | - if id(object) in self._execute_cache: |
1176 | - return self._execute_cache[id(object)] |
1177 | - execute = getattr(object, "__mocker_execute__", None) |
1178 | - if execute is not None: |
1179 | - result = execute(self, object) |
1180 | - else: |
1181 | - kind = self.kind |
1182 | - if kind == "getattr": |
1183 | - result = getattr(object, self.args[0]) |
1184 | - elif kind == "setattr": |
1185 | - result = setattr(object, self.args[0], self.args[1]) |
1186 | - elif kind == "delattr": |
1187 | - result = delattr(object, self.args[0]) |
1188 | - elif kind == "call": |
1189 | - result = object(*self.args, **self.kwargs) |
1190 | - elif kind == "contains": |
1191 | - result = self.args[0] in object |
1192 | - elif kind == "getitem": |
1193 | - result = object[self.args[0]] |
1194 | - elif kind == "setitem": |
1195 | - result = object[self.args[0]] = self.args[1] |
1196 | - elif kind == "delitem": |
1197 | - del object[self.args[0]] |
1198 | - result = None |
1199 | - elif kind == "len": |
1200 | - result = len(object) |
1201 | - elif kind == "nonzero": |
1202 | - result = bool(object) |
1203 | - elif kind == "iter": |
1204 | - result = iter(object) |
1205 | - else: |
1206 | - raise RuntimeError("Don't know how to execute %r kind." % kind) |
1207 | - self._execute_cache[id(object)] = result |
1208 | - return result |
1209 | - |
1210 | - |
1211 | -class Path(object): |
1212 | - |
1213 | - def __init__(self, root_mock, root_object=None, actions=()): |
1214 | - self.root_mock = root_mock |
1215 | - self.root_object = root_object |
1216 | - self.actions = tuple(actions) |
1217 | - self.__mocker_replace__ = False |
1218 | - |
1219 | - def parent_path(self): |
1220 | - if not self.actions: |
1221 | - return None |
1222 | - return self.actions[-1].path |
1223 | - parent_path = property(parent_path) |
1224 | - |
1225 | - def __add__(self, action): |
1226 | - """Return a new path which includes the given action at the end.""" |
1227 | - return self.__class__(self.root_mock, self.root_object, |
1228 | - self.actions + (action,)) |
1229 | - |
1230 | - def __eq__(self, other): |
1231 | - """Verify if the two paths are equal. |
1232 | - |
1233 | - Two paths are equal if they refer to the same mock object, and |
1234 | - have the actions with equal kind, args and kwargs. |
1235 | - """ |
1236 | - if (self.root_mock is not other.root_mock or |
1237 | - self.root_object is not other.root_object or |
1238 | - len(self.actions) != len(other.actions)): |
1239 | - return False |
1240 | - for action, other_action in zip(self.actions, other.actions): |
1241 | - if action != other_action: |
1242 | - return False |
1243 | - return True |
1244 | - |
1245 | - def matches(self, other): |
1246 | - """Verify if the two paths are equivalent. |
1247 | - |
1248 | - Two paths are equal if they refer to the same mock object, and |
1249 | - have the same actions performed on them. |
1250 | - """ |
1251 | - if (self.root_mock is not other.root_mock or |
1252 | - len(self.actions) != len(other.actions)): |
1253 | - return False |
1254 | - for action, other_action in zip(self.actions, other.actions): |
1255 | - if not action.matches(other_action): |
1256 | - return False |
1257 | - return True |
1258 | - |
1259 | - def execute(self, object): |
1260 | - """Execute all actions sequentially on object, and return result. |
1261 | - """ |
1262 | - for action in self.actions: |
1263 | - object = action.execute(object) |
1264 | - return object |
1265 | - |
1266 | - def __str__(self): |
1267 | - """Transform the path into a nice string such as obj.x.y('z').""" |
1268 | - result = self.root_mock.__mocker_name__ or "<mock>" |
1269 | - for action in self.actions: |
1270 | - if action.kind == "getattr": |
1271 | - result = "%s.%s" % (result, action.args[0]) |
1272 | - elif action.kind == "setattr": |
1273 | - result = "%s.%s = %r" % (result, action.args[0], action.args[1]) |
1274 | - elif action.kind == "delattr": |
1275 | - result = "del %s.%s" % (result, action.args[0]) |
1276 | - elif action.kind == "call": |
1277 | - args = [repr(x) for x in action.args] |
1278 | - items = list(action.kwargs.iteritems()) |
1279 | - items.sort() |
1280 | - for pair in items: |
1281 | - args.append("%s=%r" % pair) |
1282 | - result = "%s(%s)" % (result, ", ".join(args)) |
1283 | - elif action.kind == "contains": |
1284 | - result = "%r in %s" % (action.args[0], result) |
1285 | - elif action.kind == "getitem": |
1286 | - result = "%s[%r]" % (result, action.args[0]) |
1287 | - elif action.kind == "setitem": |
1288 | - result = "%s[%r] = %r" % (result, action.args[0], |
1289 | - action.args[1]) |
1290 | - elif action.kind == "delitem": |
1291 | - result = "del %s[%r]" % (result, action.args[0]) |
1292 | - elif action.kind == "len": |
1293 | - result = "len(%s)" % result |
1294 | - elif action.kind == "nonzero": |
1295 | - result = "bool(%s)" % result |
1296 | - elif action.kind == "iter": |
1297 | - result = "iter(%s)" % result |
1298 | - else: |
1299 | - raise RuntimeError("Don't know how to format kind %r" % |
1300 | - action.kind) |
1301 | - return result |
1302 | - |
1303 | - |
1304 | -class SpecialArgument(object): |
1305 | - """Base for special arguments for matching parameters.""" |
1306 | - |
1307 | - def __init__(self, object=None): |
1308 | - self.object = object |
1309 | - |
1310 | - def __repr__(self): |
1311 | - if self.object is None: |
1312 | - return self.__class__.__name__ |
1313 | - else: |
1314 | - return "%s(%r)" % (self.__class__.__name__, self.object) |
1315 | - |
1316 | - def matches(self, other): |
1317 | - return True |
1318 | - |
1319 | - def __eq__(self, other): |
1320 | - return type(other) == type(self) and self.object == other.object |
1321 | - |
1322 | - |
1323 | -class ANY(SpecialArgument): |
1324 | - """Matches any single argument.""" |
1325 | - |
1326 | -ANY = ANY() |
1327 | - |
1328 | - |
1329 | -class ARGS(SpecialArgument): |
1330 | - """Matches zero or more positional arguments.""" |
1331 | - |
1332 | -ARGS = ARGS() |
1333 | - |
1334 | - |
1335 | -class KWARGS(SpecialArgument): |
1336 | - """Matches zero or more keyword arguments.""" |
1337 | - |
1338 | -KWARGS = KWARGS() |
1339 | - |
1340 | - |
1341 | -class IS(SpecialArgument): |
1342 | - |
1343 | - def matches(self, other): |
1344 | - return self.object is other |
1345 | - |
1346 | - def __eq__(self, other): |
1347 | - return type(other) == type(self) and self.object is other.object |
1348 | - |
1349 | - |
1350 | -class CONTAINS(SpecialArgument): |
1351 | - |
1352 | - def matches(self, other): |
1353 | - try: |
1354 | - other.__contains__ |
1355 | - except AttributeError: |
1356 | - try: |
1357 | - iter(other) |
1358 | - except TypeError: |
1359 | - # If an object can't be iterated, and has no __contains__ |
1360 | - # hook, it'd blow up on the test below. We test this in |
1361 | - # advance to prevent catching more errors than we really |
1362 | - # want. |
1363 | - return False |
1364 | - return self.object in other |
1365 | - |
1366 | - |
1367 | -class IN(SpecialArgument): |
1368 | - |
1369 | - def matches(self, other): |
1370 | - return other in self.object |
1371 | - |
1372 | - |
1373 | -class MATCH(SpecialArgument): |
1374 | - |
1375 | - def matches(self, other): |
1376 | - return bool(self.object(other)) |
1377 | - |
1378 | - def __eq__(self, other): |
1379 | - return type(other) == type(self) and self.object is other.object |
1380 | - |
1381 | - |
1382 | -def match_params(args1, kwargs1, args2, kwargs2): |
1383 | - """Match the two sets of parameters, considering special parameters.""" |
1384 | - |
1385 | - has_args = ARGS in args1 |
1386 | - has_kwargs = KWARGS in args1 |
1387 | - |
1388 | - if has_kwargs: |
1389 | - args1 = [arg1 for arg1 in args1 if arg1 is not KWARGS] |
1390 | - elif len(kwargs1) != len(kwargs2): |
1391 | - return False |
1392 | - |
1393 | - if not has_args and len(args1) != len(args2): |
1394 | - return False |
1395 | - |
1396 | - # Either we have the same number of kwargs, or unknown keywords are |
1397 | - # accepted (KWARGS was used), so check just the ones in kwargs1. |
1398 | - for key, arg1 in kwargs1.iteritems(): |
1399 | - if key not in kwargs2: |
1400 | - return False |
1401 | - arg2 = kwargs2[key] |
1402 | - if isinstance(arg1, SpecialArgument): |
1403 | - if not arg1.matches(arg2): |
1404 | - return False |
1405 | - elif arg1 != arg2: |
1406 | - return False |
1407 | - |
1408 | - # Keywords match. Now either we have the same number of |
1409 | - # arguments, or ARGS was used. If ARGS wasn't used, arguments |
1410 | - # must match one-on-one necessarily. |
1411 | - if not has_args: |
1412 | - for arg1, arg2 in zip(args1, args2): |
1413 | - if isinstance(arg1, SpecialArgument): |
1414 | - if not arg1.matches(arg2): |
1415 | - return False |
1416 | - elif arg1 != arg2: |
1417 | - return False |
1418 | - return True |
1419 | - |
1420 | - # Easy choice. Keywords are matching, and anything on args is accepted. |
1421 | - if (ARGS,) == args1: |
1422 | - return True |
1423 | - |
1424 | - # We have something different there. If we don't have positional |
1425 | - # arguments on the original call, it can't match. |
1426 | - if not args2: |
1427 | - # Unless we have just several ARGS (which is bizarre, but..). |
1428 | - for arg1 in args1: |
1429 | - if arg1 is not ARGS: |
1430 | - return False |
1431 | - return True |
1432 | - |
1433 | - # Ok, all bets are lost. We have to actually do the more expensive |
1434 | - # matching. This is an algorithm based on the idea of the Levenshtein |
1435 | - # Distance between two strings, but heavily hacked for this purpose. |
1436 | - args2l = len(args2) |
1437 | - if args1[0] is ARGS: |
1438 | - args1 = args1[1:] |
1439 | - array = [0]*args2l |
1440 | - else: |
1441 | - array = [1]*args2l |
1442 | - for i in range(len(args1)): |
1443 | - last = array[0] |
1444 | - if args1[i] is ARGS: |
1445 | - for j in range(1, args2l): |
1446 | - last, array[j] = array[j], min(array[j-1], array[j], last) |
1447 | - else: |
1448 | - array[0] = i or int(args1[i] != args2[0]) |
1449 | - for j in range(1, args2l): |
1450 | - last, array[j] = array[j], last or int(args1[i] != args2[j]) |
1451 | - if 0 not in array: |
1452 | - return False |
1453 | - if array[-1] != 0: |
1454 | - return False |
1455 | - return True |
1456 | - |
1457 | - |
1458 | -# -------------------------------------------------------------------- |
1459 | -# Event and task base. |
1460 | - |
1461 | -class Event(object): |
1462 | - """Aggregation of tasks that keep track of a recorded action. |
1463 | - |
1464 | - An event represents something that may or may not happen while the |
1465 | - mocked environment is running, such as an attribute access, or a |
1466 | - method call. The event is composed of several tasks that are |
1467 | - orchestrated together to create a composed meaning for the event, |
1468 | - including for which actions it should be run, what happens when it |
1469 | - runs, and what's the expectations about the actions run. |
1470 | - """ |
1471 | - |
1472 | - def __init__(self, path=None): |
1473 | - self.path = path |
1474 | - self._tasks = [] |
1475 | - self._has_run = False |
1476 | - |
1477 | - def add_task(self, task): |
1478 | - """Add a new task to this taks.""" |
1479 | - self._tasks.append(task) |
1480 | - return task |
1481 | - |
1482 | - def remove_task(self, task): |
1483 | - self._tasks.remove(task) |
1484 | - |
1485 | - def get_tasks(self): |
1486 | - return self._tasks[:] |
1487 | - |
1488 | - def matches(self, path): |
1489 | - """Return true if *all* tasks match the given path.""" |
1490 | - for task in self._tasks: |
1491 | - if not task.matches(path): |
1492 | - return False |
1493 | - return bool(self._tasks) |
1494 | - |
1495 | - def has_run(self): |
1496 | - return self._has_run |
1497 | - |
1498 | - def may_run(self, path): |
1499 | - """Verify if any task would certainly raise an error if run. |
1500 | - |
1501 | - This will call the C{may_run()} method on each task and return |
1502 | - false if any of them returns false. |
1503 | - """ |
1504 | - for task in self._tasks: |
1505 | - if not task.may_run(path): |
1506 | - return False |
1507 | - return True |
1508 | - |
1509 | - def run(self, path): |
1510 | - """Run all tasks with the given action. |
1511 | - |
1512 | - @param path: The path of the expression run. |
1513 | - |
1514 | - Running an event means running all of its tasks individually and in |
1515 | - order. An event should only ever be run if all of its tasks claim to |
1516 | - match the given action. |
1517 | - |
1518 | - The result of this method will be the last result of a task |
1519 | - which isn't None, or None if they're all None. |
1520 | - """ |
1521 | - self._has_run = True |
1522 | - result = None |
1523 | - errors = [] |
1524 | - for task in self._tasks: |
1525 | - try: |
1526 | - task_result = task.run(path) |
1527 | - except AssertionError, e: |
1528 | - error = str(e) |
1529 | - if not error: |
1530 | - raise RuntimeError("Empty error message from %r" % task) |
1531 | - errors.append(error) |
1532 | - else: |
1533 | - if task_result is not None: |
1534 | - result = task_result |
1535 | - if errors: |
1536 | - message = [str(self.path)] |
1537 | - if str(path) != message[0]: |
1538 | - message.append("- Run: %s" % path) |
1539 | - for error in errors: |
1540 | - lines = error.splitlines() |
1541 | - message.append("- " + lines.pop(0)) |
1542 | - message.extend([" " + line for line in lines]) |
1543 | - raise AssertionError(os.linesep.join(message)) |
1544 | - return result |
1545 | - |
1546 | - def satisfied(self): |
1547 | - """Return true if all tasks are satisfied. |
1548 | - |
1549 | - Being satisfied means that there are no unmet expectations. |
1550 | - """ |
1551 | - for task in self._tasks: |
1552 | - try: |
1553 | - task.verify() |
1554 | - except AssertionError: |
1555 | - return False |
1556 | - return True |
1557 | - |
1558 | - def verify(self): |
1559 | - """Run verify on all tasks. |
1560 | - |
1561 | - The verify method is supposed to raise an AssertionError if the |
1562 | - task has unmet expectations, with a one-line explanation about |
1563 | - why this item is unmet. This method should be safe to be called |
1564 | - multiple times without side effects. |
1565 | - """ |
1566 | - errors = [] |
1567 | - for task in self._tasks: |
1568 | - try: |
1569 | - task.verify() |
1570 | - except AssertionError, e: |
1571 | - error = str(e) |
1572 | - if not error: |
1573 | - raise RuntimeError("Empty error message from %r" % task) |
1574 | - errors.append(error) |
1575 | - if errors: |
1576 | - message = [str(self.path)] |
1577 | - for error in errors: |
1578 | - lines = error.splitlines() |
1579 | - message.append("- " + lines.pop(0)) |
1580 | - message.extend([" " + line for line in lines]) |
1581 | - raise AssertionError(os.linesep.join(message)) |
1582 | - |
1583 | - def replay(self): |
1584 | - """Put all tasks in replay mode.""" |
1585 | - self._has_run = False |
1586 | - for task in self._tasks: |
1587 | - task.replay() |
1588 | - |
1589 | - def restore(self): |
1590 | - """Restore the state of all tasks.""" |
1591 | - for task in self._tasks: |
1592 | - task.restore() |
1593 | - |
1594 | - |
1595 | -class ReplayRestoreEvent(Event): |
1596 | - """Helper event for tasks which need replay/restore but shouldn't match.""" |
1597 | - |
1598 | - def matches(self, path): |
1599 | - return False |
1600 | - |
1601 | - |
1602 | -class Task(object): |
1603 | - """Element used to track one specific aspect on an event. |
1604 | - |
1605 | - A task is responsible for adding any kind of logic to an event. |
1606 | - Examples of that are counting the number of times the event was |
1607 | - made, verifying parameters if any, and so on. |
1608 | - """ |
1609 | - |
1610 | - def matches(self, path): |
1611 | - """Return true if the task is supposed to be run for the given path. |
1612 | - """ |
1613 | - return True |
1614 | - |
1615 | - def may_run(self, path): |
1616 | - """Return false if running this task would certainly raise an error.""" |
1617 | - return True |
1618 | - |
1619 | - def run(self, path): |
1620 | - """Perform the task item, considering that the given action happened. |
1621 | - """ |
1622 | - |
1623 | - def verify(self): |
1624 | - """Raise AssertionError if expectations for this item are unmet. |
1625 | - |
1626 | - The verify method is supposed to raise an AssertionError if the |
1627 | - task has unmet expectations, with a one-line explanation about |
1628 | - why this item is unmet. This method should be safe to be called |
1629 | - multiple times without side effects. |
1630 | - """ |
1631 | - |
1632 | - def replay(self): |
1633 | - """Put the task in replay mode. |
1634 | - |
1635 | - Any expectations of the task should be reset. |
1636 | - """ |
1637 | - |
1638 | - def restore(self): |
1639 | - """Restore any environmental changes made by the task. |
1640 | - |
1641 | - Verify should continue to work after this is called. |
1642 | - """ |
1643 | - |
1644 | - |
1645 | -# -------------------------------------------------------------------- |
1646 | -# Task implementations. |
1647 | - |
1648 | -class OnRestoreCaller(Task): |
1649 | - """Call a given callback when restoring.""" |
1650 | - |
1651 | - def __init__(self, callback): |
1652 | - self._callback = callback |
1653 | - |
1654 | - def restore(self): |
1655 | - self._callback() |
1656 | - |
1657 | - |
1658 | -class PathMatcher(Task): |
1659 | - """Match the action path against a given path.""" |
1660 | - |
1661 | - def __init__(self, path): |
1662 | - self.path = path |
1663 | - |
1664 | - def matches(self, path): |
1665 | - return self.path.matches(path) |
1666 | - |
1667 | -def path_matcher_recorder(mocker, event): |
1668 | - event.add_task(PathMatcher(event.path)) |
1669 | - |
1670 | -Mocker.add_recorder(path_matcher_recorder) |
1671 | - |
1672 | - |
1673 | -class RunCounter(Task): |
1674 | - """Task which verifies if the number of runs are within given boundaries. |
1675 | - """ |
1676 | - |
1677 | - def __init__(self, min, max=False): |
1678 | - self.min = min |
1679 | - if max is None: |
1680 | - self.max = sys.maxint |
1681 | - elif max is False: |
1682 | - self.max = min |
1683 | - else: |
1684 | - self.max = max |
1685 | - self._runs = 0 |
1686 | - |
1687 | - def replay(self): |
1688 | - self._runs = 0 |
1689 | - |
1690 | - def may_run(self, path): |
1691 | - return self._runs < self.max |
1692 | - |
1693 | - def run(self, path): |
1694 | - self._runs += 1 |
1695 | - if self._runs > self.max: |
1696 | - self.verify() |
1697 | - |
1698 | - def verify(self): |
1699 | - if not self.min <= self._runs <= self.max: |
1700 | - if self._runs < self.min: |
1701 | - raise AssertionError("Performed fewer times than expected.") |
1702 | - raise AssertionError("Performed more times than expected.") |
1703 | - |
1704 | - |
1705 | -class ImplicitRunCounter(RunCounter): |
1706 | - """RunCounter inserted by default on any event. |
1707 | - |
1708 | - This is a way to differentiate explicitly added counters and |
1709 | - implicit ones. |
1710 | - """ |
1711 | - |
1712 | -def run_counter_recorder(mocker, event): |
1713 | - """Any event may be repeated once, unless disabled by default.""" |
1714 | - if event.path.root_mock.__mocker_count__: |
1715 | - event.add_task(ImplicitRunCounter(1)) |
1716 | - |
1717 | -Mocker.add_recorder(run_counter_recorder) |
1718 | - |
1719 | -def run_counter_removal_recorder(mocker, event): |
1720 | - """ |
1721 | - Events created by getattr actions which lead to other events |
1722 | - may be repeated any number of times. For that, we remove implicit |
1723 | - run counters of any getattr actions leading to the current one. |
1724 | - """ |
1725 | - parent_path = event.path.parent_path |
1726 | - for event in mocker.get_events()[::-1]: |
1727 | - if (event.path is parent_path and |
1728 | - event.path.actions[-1].kind == "getattr"): |
1729 | - for task in event.get_tasks(): |
1730 | - if type(task) is ImplicitRunCounter: |
1731 | - event.remove_task(task) |
1732 | - |
1733 | -Mocker.add_recorder(run_counter_removal_recorder) |
1734 | - |
1735 | - |
1736 | -class MockReturner(Task): |
1737 | - """Return a mock based on the action path.""" |
1738 | - |
1739 | - def __init__(self, mocker): |
1740 | - self.mocker = mocker |
1741 | - |
1742 | - def run(self, path): |
1743 | - return Mock(self.mocker, path) |
1744 | - |
1745 | -def mock_returner_recorder(mocker, event): |
1746 | - """Events that lead to other events must return mock objects.""" |
1747 | - parent_path = event.path.parent_path |
1748 | - for event in mocker.get_events(): |
1749 | - if event.path is parent_path: |
1750 | - for task in event.get_tasks(): |
1751 | - if isinstance(task, MockReturner): |
1752 | - break |
1753 | - else: |
1754 | - event.add_task(MockReturner(mocker)) |
1755 | - break |
1756 | - |
1757 | -Mocker.add_recorder(mock_returner_recorder) |
1758 | - |
1759 | - |
1760 | -class FunctionRunner(Task): |
1761 | - """Task that runs a function everything it's run. |
1762 | - |
1763 | - Arguments of the last action in the path are passed to the function, |
1764 | - and the function result is also returned. |
1765 | - """ |
1766 | - |
1767 | - def __init__(self, func): |
1768 | - self._func = func |
1769 | - |
1770 | - def run(self, path): |
1771 | - action = path.actions[-1] |
1772 | - return self._func(*action.args, **action.kwargs) |
1773 | - |
1774 | - |
1775 | -class PathExecuter(Task): |
1776 | - """Task that executes a path in the real object, and returns the result.""" |
1777 | - |
1778 | - def __init__(self, result_callback=None): |
1779 | - self._result_callback = result_callback |
1780 | - |
1781 | - def get_result_callback(self): |
1782 | - return self._result_callback |
1783 | - |
1784 | - def run(self, path): |
1785 | - result = path.execute(path.root_object) |
1786 | - if self._result_callback is not None: |
1787 | - self._result_callback(result) |
1788 | - return result |
1789 | - |
1790 | - |
1791 | -class Orderer(Task): |
1792 | - """Task to establish an order relation between two events. |
1793 | - |
1794 | - An orderer task will only match once all its dependencies have |
1795 | - been run. |
1796 | - """ |
1797 | - |
1798 | - def __init__(self, path): |
1799 | - self.path = path |
1800 | - self._run = False |
1801 | - self._dependencies = [] |
1802 | - |
1803 | - def replay(self): |
1804 | - self._run = False |
1805 | - |
1806 | - def has_run(self): |
1807 | - return self._run |
1808 | - |
1809 | - def may_run(self, path): |
1810 | - for dependency in self._dependencies: |
1811 | - if not dependency.has_run(): |
1812 | - return False |
1813 | - return True |
1814 | - |
1815 | - def run(self, path): |
1816 | - for dependency in self._dependencies: |
1817 | - if not dependency.has_run(): |
1818 | - raise AssertionError("Should be after: %s" % dependency.path) |
1819 | - self._run = True |
1820 | - |
1821 | - def add_dependency(self, orderer): |
1822 | - self._dependencies.append(orderer) |
1823 | - |
1824 | - def get_dependencies(self): |
1825 | - return self._dependencies |
1826 | - |
1827 | - |
1828 | -class SpecChecker(Task): |
1829 | - """Task to check if arguments of the last action conform to a real method. |
1830 | - """ |
1831 | - |
1832 | - def __init__(self, method): |
1833 | - self._method = method |
1834 | - self._unsupported = False |
1835 | - |
1836 | - if method: |
1837 | - try: |
1838 | - self._args, self._varargs, self._varkwargs, self._defaults = \ |
1839 | - inspect.getargspec(method) |
1840 | - except TypeError: |
1841 | - self._unsupported = True |
1842 | - else: |
1843 | - if self._defaults is None: |
1844 | - self._defaults = () |
1845 | - if type(method) is type(self.run): |
1846 | - self._args = self._args[1:] |
1847 | - |
1848 | - def get_method(self): |
1849 | - return self._method |
1850 | - |
1851 | - def _raise(self, message): |
1852 | - spec = inspect.formatargspec(self._args, self._varargs, |
1853 | - self._varkwargs, self._defaults) |
1854 | - raise AssertionError("Specification is %s%s: %s" % |
1855 | - (self._method.__name__, spec, message)) |
1856 | - |
1857 | - def verify(self): |
1858 | - if not self._method: |
1859 | - raise AssertionError("Method not found in real specification") |
1860 | - |
1861 | - def may_run(self, path): |
1862 | - try: |
1863 | - self.run(path) |
1864 | - except AssertionError: |
1865 | - return False |
1866 | - return True |
1867 | - |
1868 | - def run(self, path): |
1869 | - if not self._method: |
1870 | - raise AssertionError("Method not found in real specification") |
1871 | - if self._unsupported: |
1872 | - return # Can't check it. Happens with builtin functions. :-( |
1873 | - action = path.actions[-1] |
1874 | - obtained_len = len(action.args) |
1875 | - obtained_kwargs = action.kwargs.copy() |
1876 | - nodefaults_len = len(self._args) - len(self._defaults) |
1877 | - for i, name in enumerate(self._args): |
1878 | - if i < obtained_len and name in action.kwargs: |
1879 | - self._raise("%r provided twice" % name) |
1880 | - if (i >= obtained_len and i < nodefaults_len and |
1881 | - name not in action.kwargs): |
1882 | - self._raise("%r not provided" % name) |
1883 | - obtained_kwargs.pop(name, None) |
1884 | - if obtained_len > len(self._args) and not self._varargs: |
1885 | - self._raise("too many args provided") |
1886 | - if obtained_kwargs and not self._varkwargs: |
1887 | - self._raise("unknown kwargs: %s" % ", ".join(obtained_kwargs)) |
1888 | - |
1889 | -def spec_checker_recorder(mocker, event): |
1890 | - spec = event.path.root_mock.__mocker_spec__ |
1891 | - if spec: |
1892 | - actions = event.path.actions |
1893 | - if len(actions) == 1: |
1894 | - if actions[0].kind == "call": |
1895 | - method = getattr(spec, "__call__", None) |
1896 | - event.add_task(SpecChecker(method)) |
1897 | - elif len(actions) == 2: |
1898 | - if actions[0].kind == "getattr" and actions[1].kind == "call": |
1899 | - method = getattr(spec, actions[0].args[0], None) |
1900 | - event.add_task(SpecChecker(method)) |
1901 | - |
1902 | -Mocker.add_recorder(spec_checker_recorder) |
1903 | - |
1904 | - |
1905 | -class ProxyReplacer(Task): |
1906 | - """Task which installs and deinstalls proxy mocks. |
1907 | - |
1908 | - This task will replace a real object by a mock in all dictionaries |
1909 | - found in the running interpreter via the garbage collecting system. |
1910 | - """ |
1911 | - |
1912 | - def __init__(self, mock): |
1913 | - self.mock = mock |
1914 | - self.__mocker_replace__ = False |
1915 | - |
1916 | - def replay(self): |
1917 | - global_replace(self.mock.__mocker_object__, self.mock) |
1918 | - |
1919 | - def restore(self): |
1920 | - global_replace(self.mock, self.mock.__mocker_object__) |
1921 | - |
1922 | - |
1923 | -def global_replace(remove, install): |
1924 | - """Replace object 'remove' with object 'install' on all dictionaries.""" |
1925 | - for referrer in gc.get_referrers(remove): |
1926 | - if (type(referrer) is dict and |
1927 | - referrer.get("__mocker_replace__", True)): |
1928 | - for key, value in referrer.items(): |
1929 | - if value is remove: |
1930 | - referrer[key] = install |
1931 | - |
1932 | - |
1933 | -class Undefined(object): |
1934 | - |
1935 | - def __repr__(self): |
1936 | - return "Undefined" |
1937 | - |
1938 | -Undefined = Undefined() |
1939 | - |
1940 | - |
1941 | -class Patcher(Task): |
1942 | - |
1943 | - def __init__(self): |
1944 | - super(Patcher, self).__init__() |
1945 | - self._monitored = {} # {kind: {id(object): object}} |
1946 | - self._patched = {} |
1947 | - |
1948 | - def is_monitoring(self, obj, kind): |
1949 | - monitored = self._monitored.get(kind) |
1950 | - if monitored: |
1951 | - if id(obj) in monitored: |
1952 | - return True |
1953 | - cls = type(obj) |
1954 | - if issubclass(cls, type): |
1955 | - cls = obj |
1956 | - bases = set([id(base) for base in cls.__mro__]) |
1957 | - bases.intersection_update(monitored) |
1958 | - return bool(bases) |
1959 | - return False |
1960 | - |
1961 | - def monitor(self, obj, kind): |
1962 | - if kind not in self._monitored: |
1963 | - self._monitored[kind] = {} |
1964 | - self._monitored[kind][id(obj)] = obj |
1965 | - |
1966 | - def patch_attr(self, obj, attr, value): |
1967 | - original = obj.__dict__.get(attr, Undefined) |
1968 | - self._patched[id(obj), attr] = obj, attr, original |
1969 | - setattr(obj, attr, value) |
1970 | - |
1971 | - def get_unpatched_attr(self, obj, attr): |
1972 | - cls = type(obj) |
1973 | - if issubclass(cls, type): |
1974 | - cls = obj |
1975 | - result = Undefined |
1976 | - for mro_cls in cls.__mro__: |
1977 | - key = (id(mro_cls), attr) |
1978 | - if key in self._patched: |
1979 | - result = self._patched[key][2] |
1980 | - if result is not Undefined: |
1981 | - break |
1982 | - elif attr in mro_cls.__dict__: |
1983 | - result = mro_cls.__dict__.get(attr, Undefined) |
1984 | - break |
1985 | - if isinstance(result, object) and hasattr(type(result), "__get__"): |
1986 | - if cls is obj: |
1987 | - obj = None |
1988 | - return result.__get__(obj, cls) |
1989 | - return result |
1990 | - |
1991 | - def _get_kind_attr(self, kind): |
1992 | - if kind == "getattr": |
1993 | - return "__getattribute__" |
1994 | - return "__%s__" % kind |
1995 | - |
1996 | - def replay(self): |
1997 | - for kind in self._monitored: |
1998 | - attr = self._get_kind_attr(kind) |
1999 | - seen = set() |
2000 | - for obj in self._monitored[kind].itervalues(): |
2001 | - cls = type(obj) |
2002 | - if issubclass(cls, type): |
2003 | - cls = obj |
2004 | - if cls not in seen: |
2005 | - seen.add(cls) |
2006 | - unpatched = getattr(cls, attr, Undefined) |
2007 | - self.patch_attr(cls, attr, |
2008 | - PatchedMethod(kind, unpatched, |
2009 | - self.is_monitoring)) |
2010 | - self.patch_attr(cls, "__mocker_execute__", |
2011 | - self.execute) |
2012 | - |
2013 | - def restore(self): |
2014 | - for obj, attr, original in self._patched.itervalues(): |
2015 | - if original is Undefined: |
2016 | - delattr(obj, attr) |
2017 | - else: |
2018 | - setattr(obj, attr, original) |
2019 | - self._patched.clear() |
2020 | - |
2021 | - def execute(self, action, object): |
2022 | - attr = self._get_kind_attr(action.kind) |
2023 | - unpatched = self.get_unpatched_attr(object, attr) |
2024 | - try: |
2025 | - return unpatched(*action.args, **action.kwargs) |
2026 | - except AttributeError: |
2027 | - if action.kind == "getattr": |
2028 | - # The normal behavior of Python is to try __getattribute__, |
2029 | - # and if it raises AttributeError, try __getattr__. We've |
2030 | - # tried the unpatched __getattribute__ above, and we'll now |
2031 | - # try __getattr__. |
2032 | - try: |
2033 | - __getattr__ = unpatched("__getattr__") |
2034 | - except AttributeError: |
2035 | - pass |
2036 | - else: |
2037 | - return __getattr__(*action.args, **action.kwargs) |
2038 | - raise |
2039 | - |
2040 | - |
2041 | -class PatchedMethod(object): |
2042 | - |
2043 | - def __init__(self, kind, unpatched, is_monitoring): |
2044 | - self._kind = kind |
2045 | - self._unpatched = unpatched |
2046 | - self._is_monitoring = is_monitoring |
2047 | - |
2048 | - def __get__(self, obj, cls=None): |
2049 | - object = obj or cls |
2050 | - if not self._is_monitoring(object, self._kind): |
2051 | - return self._unpatched.__get__(obj, cls) |
2052 | - def method(*args, **kwargs): |
2053 | - if self._kind == "getattr" and args[0].startswith("__mocker_"): |
2054 | - return self._unpatched.__get__(obj, cls)(args[0]) |
2055 | - mock = object.__mocker_mock__ |
2056 | - return mock.__mocker_act__(self._kind, args, kwargs, object) |
2057 | - return method |
2058 | - |
2059 | - def __call__(self, obj, *args, **kwargs): |
2060 | - # At least with __getattribute__, Python seems to use *both* the |
2061 | - # descriptor API and also call the class attribute directly. It |
2062 | - # looks like an interpreter bug, or at least an undocumented |
2063 | - # inconsistency. |
2064 | - return self.__get__(obj)(*args, **kwargs) |
2065 | - |
2066 | - |
2067 | -def patcher_recorder(mocker, event): |
2068 | - mock = event.path.root_mock |
2069 | - if mock.__mocker_patcher__ and len(event.path.actions) == 1: |
2070 | - patcher = mock.__mocker_patcher__ |
2071 | - patcher.monitor(mock.__mocker_object__, event.path.actions[0].kind) |
2072 | - |
2073 | -Mocker.add_recorder(patcher_recorder) |
+1