From eb702b20d5ccc41d0661f319dd7beb0bb28c5fca Mon Sep 17 00:00:00 2001 From: Chris Withers Date: Tue, 28 Jan 2020 07:26:45 +0000 Subject: [PATCH 1/7] use the `: pass` and `: yield` patterns for code that isn't expected to ever be exectuted. --- Lib/unittest/test/testmock/testasync.py | 59 ++++++++----------------- 1 file changed, 18 insertions(+), 41 deletions(-) diff --git a/Lib/unittest/test/testmock/testasync.py b/Lib/unittest/test/testmock/testasync.py index 992076db787060c..bf936220ccaf2c9 100644 --- a/Lib/unittest/test/testmock/testasync.py +++ b/Lib/unittest/test/testmock/testasync.py @@ -16,38 +16,28 @@ def tearDownModule(): class AsyncClass: - def __init__(self): - pass - async def async_method(self): - pass - def normal_method(self): - pass + def __init__(self): pass + async def async_method(self): pass + def normal_method(self): pass @classmethod - async def async_class_method(cls): - pass + async def async_class_method(cls): pass @staticmethod - async def async_static_method(): - pass + async def async_static_method(): pass class AwaitableClass: - def __await__(self): - yield + def __await__(self): yield -async def async_func(): - pass +async def async_func(): pass -async def async_func_args(a, b, *, c): - pass +async def async_func_args(a, b, *, c): pass -def normal_func(): - pass +def normal_func(): pass class NormalClass(object): - def a(self): - pass + def a(self): pass async_foo_name = f'{__name__}.AsyncClass' @@ -402,8 +392,7 @@ def test_magicmock_lambda_spec(self): class AsyncArguments(IsolatedAsyncioTestCase): async def test_add_return_value(self): - async def addition(self, var): - return var + 1 + async def addition(self, var): pass mock = AsyncMock(addition, return_value=10) output = await mock(5) @@ -411,8 +400,7 @@ async def addition(self, var): self.assertEqual(output, 10) async def test_add_side_effect_exception(self): - async def addition(var): - return var + 1 + async def addition(var): pass mock = AsyncMock(addition, side_effect=Exception('err')) with self.assertRaises(Exception): await mock(5) @@ -553,18 +541,14 @@ def test_magic_methods_are_async_functions(self): class AsyncContextManagerTest(unittest.TestCase): class WithAsyncContextManager: - async def __aenter__(self, *args, **kwargs): - return self + async def __aenter__(self, *args, **kwargs): pass - async def __aexit__(self, *args, **kwargs): - pass + async def __aexit__(self, *args, **kwargs): pass class WithSyncContextManager: - def __enter__(self, *args, **kwargs): - return self + def __enter__(self, *args, **kwargs): pass - def __exit__(self, *args, **kwargs): - pass + def __exit__(self, *args, **kwargs): pass class ProductionCode: # Example real-world(ish) code @@ -673,16 +657,9 @@ class WithAsyncIterator(object): def __init__(self): self.items = ["foo", "NormalFoo", "baz"] - def __aiter__(self): - return self - - async def __anext__(self): - try: - return self.items.pop() - except IndexError: - pass + def __aiter__(self): pass - raise StopAsyncIteration + async def __anext__(self): pass def test_aiter_set_return_value(self): mock_iter = AsyncMock(name="tester") From e4f8647eddd953c79d6a0e6cba77e0a7fe79f86d Mon Sep 17 00:00:00 2001 From: Chris Withers Date: Tue, 28 Jan 2020 07:53:16 +0000 Subject: [PATCH 2/7] These items are only ever of length two. The assertion makes sure this continues to be the case! --- Lib/unittest/mock.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/Lib/unittest/mock.py b/Lib/unittest/mock.py index a3d8b6eab41a986..4d561a66f2b85ac 100644 --- a/Lib/unittest/mock.py +++ b/Lib/unittest/mock.py @@ -1042,8 +1042,7 @@ class _AnyComparer(list): the left.""" def __contains__(self, item): for _call in self: - if len(item) != len(_call): - continue + assert len(item) == len(_call) if all([ expected == actual for expected, actual in zip(item, _call) From 8e6313989aad067a0335dd48deb0eee0352fcec2 Mon Sep 17 00:00:00 2001 From: Chris Withers Date: Tue, 28 Jan 2020 08:02:28 +0000 Subject: [PATCH 3/7] fix typo --- Lib/unittest/mock.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Lib/unittest/mock.py b/Lib/unittest/mock.py index 4d561a66f2b85ac..7b350b8e20bc875 100644 --- a/Lib/unittest/mock.py +++ b/Lib/unittest/mock.py @@ -2167,7 +2167,7 @@ def __init__(self, /, *args, **kwargs): self.__dict__['__code__'] = code_mock async def _execute_mock_call(self, /, *args, **kwargs): - # This is nearly just like super(), except for sepcial handling + # This is nearly just like super(), except for special handling # of coroutines _call = self.call_args From cc8d3c10bc698269ea779b01ffd248be3ffb5466 Mon Sep 17 00:00:00 2001 From: Chris Withers Date: Tue, 28 Jan 2020 08:03:36 +0000 Subject: [PATCH 4/7] Fix bug highlighted by lack of coverage of an except branch. Before this change, the stop-without-start blows up with `TypeError: 'NoneType' object is not iterable` --- Lib/unittest/mock.py | 3 ++- Lib/unittest/test/testmock/testpatch.py | 8 ++++++++ 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/Lib/unittest/mock.py b/Lib/unittest/mock.py index 7b350b8e20bc875..d9c598bcbe7429f 100644 --- a/Lib/unittest/mock.py +++ b/Lib/unittest/mock.py @@ -1855,7 +1855,8 @@ def _unpatch_dict(self): def __exit__(self, *args): """Unpatch the dict.""" - self._unpatch_dict() + if self._original is not None: + self._unpatch_dict() return False diff --git a/Lib/unittest/test/testmock/testpatch.py b/Lib/unittest/test/testmock/testpatch.py index 438dfd8cfbcc091..f1bc0e1cd40a277 100644 --- a/Lib/unittest/test/testmock/testpatch.py +++ b/Lib/unittest/test/testmock/testpatch.py @@ -770,6 +770,14 @@ def test_patch_dict_start_stop(self): self.assertEqual(d, original) + def test_patch_dict_stop_without_start(self): + d = {'foo': 'bar'} + original = d.copy() + patcher = patch.dict(d, [('spam', 'eggs')], clear=True) + self.assertEqual(patcher.stop(), False) + self.assertEqual(d, original) + + def test_patch_dict_class_decorator(self): this = self d = {'spam': 'eggs'} From 9130b1658256045457c912f4cdcdd159d7e4bda2 Mon Sep 17 00:00:00 2001 From: Chris Withers Date: Tue, 28 Jan 2020 08:10:22 +0000 Subject: [PATCH 5/7] The fix for bpo-37972 means _Call.count and _Call.index are no longer needed. --- Lib/unittest/mock.py | 6 ------ 1 file changed, 6 deletions(-) diff --git a/Lib/unittest/mock.py b/Lib/unittest/mock.py index d9c598bcbe7429f..1049d4a21d7d39b 100644 --- a/Lib/unittest/mock.py +++ b/Lib/unittest/mock.py @@ -2541,12 +2541,6 @@ def __getattribute__(self, attr): return tuple.__getattribute__(self, attr) - def count(self, /, *args, **kwargs): - return self.__getattr__('count')(*args, **kwargs) - - def index(self, /, *args, **kwargs): - return self.__getattr__('index')(*args, **kwargs) - def _get_call_arguments(self): if len(self) == 2: args, kwargs = self From 7812d7b43bcf8160bc8254d8244e46c7708cf28d Mon Sep 17 00:00:00 2001 From: Chris Withers Date: Tue, 28 Jan 2020 08:24:20 +0000 Subject: [PATCH 6/7] add coverage for calling next() on a mock_open with readline.return_value set. --- Lib/unittest/test/testmock/testmock.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/Lib/unittest/test/testmock/testmock.py b/Lib/unittest/test/testmock/testmock.py index 677346725bdd244..9b9e066cc545da9 100644 --- a/Lib/unittest/test/testmock/testmock.py +++ b/Lib/unittest/test/testmock/testmock.py @@ -1868,6 +1868,11 @@ def test_mock_open_using_next(self): with self.assertRaises(StopIteration): next(f1) + def test_mock_open_next_with_readline_with_return_value(self): + mopen = mock.mock_open(read_data='foo\nbarn') + mopen.return_value.readline.return_value = 'abc' + self.assertEqual('abc', next(mopen())) + def test_mock_open_write(self): # Test exception in file writing write() mock_namedtemp = mock.mock_open(mock.MagicMock(name='JLV')) From 576fd3ea7d944549b37fb57410830400eb1e2c39 Mon Sep 17 00:00:00 2001 From: Chris Withers Date: Tue, 28 Jan 2020 08:24:33 +0000 Subject: [PATCH 7/7] __aiter__ is defined on the Mock so this is never called. --- Lib/unittest/mock.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/Lib/unittest/mock.py b/Lib/unittest/mock.py index 1049d4a21d7d39b..8ea463fa58083fe 100644 --- a/Lib/unittest/mock.py +++ b/Lib/unittest/mock.py @@ -2915,9 +2915,6 @@ def __init__(self, iterator): code_mock.co_flags = inspect.CO_ITERABLE_COROUTINE self.__dict__['__code__'] = code_mock - def __aiter__(self): - return self - async def __anext__(self): try: return next(self.iterator)