My favorites | Sign in
Project Home Downloads Issues Source
New issue   Search
for
  Advanced search   Search tips   Subscriptions

Issue 77 attachment: isolate.patch3 (4.1 KB)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
diff -r 3dddb6d9bc6e mock.py
--- a/mock.py Mon May 30 13:01:58 2011 +0100
+++ b/mock.py Mon May 30 09:30:06 2011 -0700
@@ -1111,6 +1111,117 @@
patch.dict = _patch_dict


+class _isolate(object):
+ def __init__(self, target, excludes=None):
+ self.target = target
+ self.excludes = []
+ if excludes is not None:
+ self.excludes = excludes
+ self.names_under_test = set(self.get_names_under_test())
+
+ def get_names_under_test(self):
+ module = sys.modules[self.target.__module__]
+ for name, value in module.__dict__.items():
+ if value is self.target or name in self.excludes:
+ yield name
+
+ def __enter__(self):
+ module_name = self.target.__module__
+ self.module = sys.modules[module_name]
+ old_module_dict = self.module.__dict__.copy()
+ module_keys = set(self.module.__dict__.keys())
+
+ dunders = set([k for k in module_keys
+ if k.startswith('__') and k.endswith('__')])
+ replaced_keys = (module_keys - dunders - self.names_under_test)
+ for key in replaced_keys:
+ self.module.__dict__[key] = Mock()
+ self.module.__dict__['__mock_isolated_dict__'] = old_module_dict
+
+ def __exit__(self, *_):
+ old_module_dict = self.module.__dict__['__mock_isolated_dict__']
+ self.module.__dict__.clear()
+ self.module.__dict__.update(old_module_dict)
+
+ def __call__(self, thing, *args, **kwargs):
+ if isinstance(thing, ClassTypes):
+ return self.decorate_class(thing)
+ else:
+ return self.decorate_callable(thing)
+
+ def decorate_callable(self, func):
+ @wraps(func)
+ def patched(*args, **keywargs):
+ # don't use a with here (backwards compatability with 2.5)
+ self.__enter__()
+ try:
+ return func(*args, **keywargs)
+ finally:
+ self.__exit__()
+
+ if hasattr(func, 'func_code'):
+ # not in Python 3
+ patched.compat_co_firstlineno = getattr(func, "compat_co_firstlineno",
+ func.func_code.co_firstlineno)
+ return patched
+
+ def decorate_class(self, klass, *args):
+ # wrapping setUp allows further shared customization of mocks
+ setup = getattr(klass, 'setUp', None)
+ teardown = getattr(klass, 'tearDown', None)
+ if not setup:
+ setattr(klass, 'setUp', self.start)
+ else:
+ def wrap_setup(*args):
+ self.start()
+ setup(*args)
+ setattr(klass, setup.__name__, wrap_setup)
+
+ if not teardown:
+ setattr(klass, 'tearDown', self.stop)
+ else:
+ def wrap_teardown(*args):
+ self.stop()
+ teardown(*args)
+ setattr(klass, teardown.__name__, wrap_teardown)
+
+ return klass
+
+ start = __enter__
+ stop = __exit__
+
+
+def isolate(target, excludes=None):
+ """
+ ``isolate`` acts as a function decorator, class decorator or
+ context manager. Within the function, TestCase methods or context all
+ objects within the targets module will be patched with a new ``Mock``
+ object. On exiting the function or context the patch is undone. For a
+ ``TestCase`` setUp and tearDown are wrapped.
+
+ ``isolate`` is useful to quickly mock out everything in a module except
+ the ``target``.
+
+ ``excludes`` is either a string of form `'package.module.objectname'` or
+ a list of such strings. The named objects will not be patched.
+
+ If applied to a TestCase ``isolate`` will wrap the setUp and tearDown
+ methods. This allows configuration of the mocked module attributes
+ during setUp.
+
+ ``isolate`` borrows heavily from DingusTestCase.
+ """
+ target = _importer(target)
+ return _isolate(target, excludes)
+
+
+def _isolate_object(*args, **kwargs):
+ return _isolate(*args, **kwargs)
+
+
+isolate.object = _isolate_object
+
+
magic_methods = (
"lt le gt ge eq ne "
"getitem setitem delitem "
Powered by Google Project Hosting