diff options
-rw-r--r-- | extendmock.py | 133 | ||||
-rw-r--r-- | mock.py | 10 | ||||
-rw-r--r-- | tests/testpatch.py | 29 |
3 files changed, 23 insertions, 149 deletions
diff --git a/extendmock.py b/extendmock.py index 07f0ec0..0550d9f 100644 --- a/extendmock.py +++ b/extendmock.py @@ -1,132 +1 @@ -# NOTE: merged into moock.py in Mock 0.7 - -import inspect -from mock import Mock - -__all__ = ('mocksignature', 'MagicMock') - -""" -extendmock adds additional features to the mock module. - -The mocksignature function creates wrapper functions that call a mock whilst -preserving the signature of the original function. - -Note that when a function signature is mocked with mocksignature it will *always* be -called with positional arguments and not keyword arguments (except where it -collects arguments with \*\*). Defaults will explicitly be supplied where the function -is called with missing arguments (as normal). - -MagicMock is a class that adds magic method support to Mock. You add magic methods -to mock instances in the same way you mock other attributes:: - - mock = Mock() - mock.__repr__ = lambda self: 'some string' - -Note that functions (or callable objects like a Mock instance) that are used for -magic methods must take self as the first argument. - -The only unsupported magic methods (that I'm aware of) are: - -* Used by Mock: __init__, __new__, __getattr__, __setattr__, __delattr__, __call__ -* Rare: __dir__, -* Can cause bad interactions with other functionality: - - - __reversed__, __missing__, __del__, __unicode__, __getattribute__ - - __get__, __set__, __delete__ - -* Context manager methods are not directly supported (__enter__ and __exit__) as - Python doesn't lookup these methods on the class as it should (in Python 2.5 / 2.6 - anyway). - -For more examples of how to use mocksignature and MagicMock see the tests. -""" - -# getsignature and mocksignature heavily "inspired" by -# the decorator module: http://pypi.python.org/pypi/decorator/ -# by Michele Simionato - -DELEGATE = MISSING = object() - -def getsignature(func): - assert inspect.ismethod(func) or inspect.isfunction(func) - regargs, varargs, varkwargs, defaults = inspect.getargspec(func) - - # instance methods need to lose the self argument - im_self = getattr(func, 'im_self', None) - if im_self is not None: - regargs = regargs[1:] - - argnames = list(regargs) - if varargs: - argnames.append(varargs) - if varkwargs: - argnames.append(varkwargs) - assert '_mock_' not in argnames, ("_mock_ is a reserved argument name, can't mock signatures using _mock_") - signature = inspect.formatargspec(regargs, varargs, varkwargs, defaults, formatvalue=lambda value: "") - return signature[1:-1] - - -def mocksignature(func, mock): - signature = getsignature(func) - src = "lambda %(signature)s: _mock_(%(signature)s)" % {'signature': signature} - - funcopy = eval(src, dict(_mock_=mock)) - funcopy.__name__ = func.__name__ - funcopy.__doc__ = func.__doc__ - funcopy.__dict__.update(func.__dict__) - funcopy.__module__ = func.__module__ - funcopy.func_defaults = func.func_defaults - return funcopy - - -class MagicMock(Mock): - def __new__(cls, *args, **kw): - class MagicMock(cls): - # every instance has its own class - # so we can create magic methods on the - # class without stomping on other mocks - pass - return Mock.__new__(MagicMock, *args, **kw) - - def __setattr__(self, name, value): - if name in _all_magics: - method = _all_magics[name] - setattr(self.__class__, name, method) - return Mock.__setattr__(self, name, value) - - def __delattr__(self, name): - if name in _all_magics and name in self.__class__.__dict__: - delattr(self.__class__, name) - return Mock.__delattr__(self, name) - - -magic_methods = ( - "lt le gt ge eq ne " - "getitem setitem delitem " - "len contains iter " - "hash repr str " - "nonzero " - "divmod neg pos abs invert " - "complex int long float oct hex index " -) - -numerics = "add sub mul div truediv floordiv mod lshift rshift and xor or " -inplace = ' '.join('i%s' % n for n in numerics.split()) -right = ' '.join('r%s' % n for n in numerics.split()) - - -def get_method(name): - def func(self, *args, **kw): - return self.__dict__[name](self, *args, **kw) - func.__name__ = name - return func - -_all_magics = {} -for method in sum([methods.split() for methods in [magic_methods, numerics, inplace, right]], []): - name = '__%s__' % method - _all_magics[name] = get_method(name) - -import mock -if mock.__version__ > '0.6.0': - mockssignature = mock.mocksignature - MagicMock = mock.Mock
\ No newline at end of file +# merged into mock.py in Mock 0.7 @@ -621,8 +621,8 @@ class _patch(object): if original is DEFAULT and not self.create: # for mocking signature on methods with # patch.object(...) - original = getattr(self.target, self.attribute) - new_attr = mocksignature(original, new) + original_for_sig = getattr(self.target, self.attribute) + new_attr = mocksignature(original_for_sig, new) self.temp_original = original setattr(self.target, self.attribute, new_attr) @@ -636,6 +636,9 @@ class _patch(object): delattr(self.target, self.attribute) del self.temp_original + start = __enter__ + stop = __exit__ + def _patch_object(target, attribute, new=DEFAULT, spec=None, create=False, mocksignature=False, spec_set=None): @@ -805,6 +808,9 @@ class _patch_dict(object): self._unpatch_dict() return False + start = __enter__ + stop = __exit__ + def _clear_dict(in_dict): try: diff --git a/tests/testpatch.py b/tests/testpatch.py index bd856a6..d3f4719 100644 --- a/tests/testpatch.py +++ b/tests/testpatch.py @@ -3,6 +3,7 @@ # http://www.voidspace.org.uk/python/mock/ import os +import sys import warnings from tests import support @@ -18,6 +19,8 @@ if not inPy3k: else: builtin_string = 'builtins' +PTModule = sys.modules[__name__] + # for use in the test something = sentinel.Something @@ -99,17 +102,12 @@ class PatchTest(unittest2.TestCase): def testPatch(self): - import __main__ - __main__.something = sentinel.Something - @apply - @patch('__main__.something', sentinel.Something2) + @patch('%s.something' % __name__, sentinel.Something2) def test(): - self.assertEqual(__main__.something, sentinel.Something2, "unpatched") - - self.assertEqual(__main__.something, sentinel.Something, "patch not restored") + self.assertEqual(PTModule.something, sentinel.Something2, "unpatched") - import tests.testpatch as PTModule + self.assertEqual(PTModule.something, sentinel.Something, "patch not restored") @patch('tests.testpatch.something', sentinel.Something2) @patch('tests.testpatch.something_else', sentinel.SomethingElse) @@ -374,25 +372,22 @@ class PatchTest(unittest2.TestCase): def testPatchClassDecorator(self): - import __main__ - __main__.something = sentinel.Something - class Something(object): attribute = sentinel.Original class Foo(object): def test_method(other_self, mock_something): - self.assertEqual(__main__.something, mock_something, "unpatched") + self.assertEqual(PTModule.something, mock_something, "unpatched") def not_test_method(other_self): - self.assertEqual(__main__.something, sentinel.Something, "non-test method patched") - Foo = patch('__main__.something')(Foo) + self.assertEqual(PTModule.something, sentinel.Something, "non-test method patched") + Foo = patch('%s.something' % __name__)(Foo) f = Foo() f.test_method() f.not_test_method() self.assertEqual(Something.attribute, sentinel.Original, "patch not restored") - self.assertEqual(__main__.something, sentinel.Something, "patch not restored") + self.assertEqual(PTModule.something, sentinel.Something, "patch not restored") @unittest2.skipUnless(with_available, "test requires Python >= 2.5") def testPatchObjectDeprecation(self): @@ -632,6 +627,10 @@ class PatchTest(unittest2.TestCase): self.assertRaises(AttributeError, test) + def DONTtestPatchStartStop(self): + patcher = patch() + + if __name__ == '__main__': unittest2.main() |