From 8c14534df6c7bd561fac31985fba60306e181265 Mon Sep 17 00:00:00 2001 From: Kushal Das Date: Wed, 16 Apr 2014 23:32:21 +0530 Subject: Closes Issue 21238: New keyword argument `unsafe` to Mock. It raises `AttributeError` incase of an attribute startswith assert or assret. --- Doc/library/unittest.mock.rst | 8 +++++++- Lib/unittest/mock.py | 8 ++++++-- Lib/unittest/test/testmock/testmock.py | 11 +++++++++++ Misc/NEWS | 3 +++ 4 files changed, 27 insertions(+), 3 deletions(-) diff --git a/Doc/library/unittest.mock.rst b/Doc/library/unittest.mock.rst index 6d4b82b..e6ca908 100644 --- a/Doc/library/unittest.mock.rst +++ b/Doc/library/unittest.mock.rst @@ -198,7 +198,7 @@ a `MagicMock` for you. You can specify an alternative class of `Mock` using the `new_callable` argument to `patch`. -.. class:: Mock(spec=None, side_effect=None, return_value=DEFAULT, wraps=None, name=None, spec_set=None, **kwargs) +.. class:: Mock(spec=None, side_effect=None, return_value=DEFAULT, wraps=None, name=None, spec_set=None, unsafe=False, **kwargs) Create a new `Mock` object. `Mock` takes several optional arguments that specify the behaviour of the Mock object: @@ -235,6 +235,12 @@ the `new_callable` argument to `patch`. this is a new Mock (created on first access). See the :attr:`return_value` attribute. + * `unsafe`: By default if any attribute starts with *assert* or + *assret* will raise an `AttributeError`. Passing `unsafe=True` will allow + access to these attributes. + + .. versionadded:: 3.5 + * `wraps`: Item for the mock object to wrap. If `wraps` is not None then calling the Mock will pass the call through to the wrapped object (returning the real result). Attribute access on the mock will return a diff --git a/Lib/unittest/mock.py b/Lib/unittest/mock.py index 42cf23a..48e7dd0 100644 --- a/Lib/unittest/mock.py +++ b/Lib/unittest/mock.py @@ -379,7 +379,7 @@ class NonCallableMock(Base): def __init__( self, spec=None, wraps=None, name=None, spec_set=None, parent=None, _spec_state=None, _new_name='', _new_parent=None, - _spec_as_instance=False, _eat_self=None, **kwargs + _spec_as_instance=False, _eat_self=None, unsafe=False, **kwargs ): if _new_parent is None: _new_parent = parent @@ -409,6 +409,7 @@ class NonCallableMock(Base): __dict__['_mock_mock_calls'] = _CallList() __dict__['method_calls'] = _CallList() + __dict__['_mock_unsafe'] = unsafe if kwargs: self.configure_mock(**kwargs) @@ -565,13 +566,16 @@ class NonCallableMock(Base): def __getattr__(self, name): - if name == '_mock_methods': + if name in {'_mock_methods', '_mock_unsafe'}: raise AttributeError(name) elif self._mock_methods is not None: if name not in self._mock_methods or name in _all_magics: raise AttributeError("Mock object has no attribute %r" % name) elif _is_magic(name): raise AttributeError(name) + if not self._mock_unsafe: + if name.startswith(('assert', 'assret')): + raise AttributeError(name) result = self._mock_children.get(name) if result is _deleted: diff --git a/Lib/unittest/test/testmock/testmock.py b/Lib/unittest/test/testmock/testmock.py index 23675b9..59353a0 100644 --- a/Lib/unittest/test/testmock/testmock.py +++ b/Lib/unittest/test/testmock/testmock.py @@ -1187,6 +1187,17 @@ class MockTest(unittest.TestCase): m = mock.create_autospec(object(), name='sweet_func') self.assertIn('sweet_func', repr(m)) + #Issue21238 + def test_mock_unsafe(self): + m = Mock() + with self.assertRaises(AttributeError): + m.assert_foo_call() + with self.assertRaises(AttributeError): + m.assret_foo_call() + m = Mock(unsafe=True) + m.assert_foo_call() + m.assret_foo_call() + def test_mock_add_spec(self): class _One(object): one = 1 diff --git a/Misc/NEWS b/Misc/NEWS index 2ebfe3f..dd1c4af 100644 --- a/Misc/NEWS +++ b/Misc/NEWS @@ -50,6 +50,9 @@ Core and Builtins Library ------- +- Issue #21238: New keyword argument `unsafe` to Mock. It raises + `AttributeError` incase of an attribute startswith assert or assret. + - Issue #20896: ssl.get_server_certificate() now uses PROTOCOL_SSLv23, not PROTOCOL_SSLv3, for maximum compatibility. -- cgit v0.12