From 301d852a6ca9ea008e8ca7b61c29a92cd77a193f Mon Sep 17 00:00:00 2001 From: Michael Foord Date: Fri, 13 Sep 2019 18:40:56 +0200 Subject: [PATCH 1/2] [3.8] bpo-38122: minor fixes to AsyncMock spec handling (GH-16099). (cherry picked from commit 14fd925a18fe3db0922a7d798e373102fe7a8a9c) Co-authored-by: Michael Foord --- Lib/unittest/mock.py | 35 ++++++++++++++++++++++++----------- 1 file changed, 24 insertions(+), 11 deletions(-) diff --git a/Lib/unittest/mock.py b/Lib/unittest/mock.py index cbc4d76b851362..b86f52ae82b1bc 100644 --- a/Lib/unittest/mock.py +++ b/Lib/unittest/mock.py @@ -402,18 +402,12 @@ def __new__(cls, /, *args, **kw): # so we can create magic methods on the # class without stomping on other mocks bases = (cls,) - if not issubclass(cls, AsyncMock): + if not issubclass(cls, AsyncMockMixin): # Check if spec is an async object or function - sig = inspect.signature(NonCallableMock.__init__) - bound_args = sig.bind_partial(cls, *args, **kw).arguments - spec_arg = [ - arg for arg in bound_args.keys() - if arg.startswith('spec') - ] - if spec_arg: - # what if spec_set is different than spec? - if _is_async_obj(bound_args[spec_arg[0]]): - bases = (AsyncMockMixin, cls,) + bound_args = _MOCK_SIG.bind_partial(cls, *args, **kw).arguments + spec_arg = bound_args.get('spec_set', bound_args.get('spec')) + if spec_arg and _is_async_obj(spec_arg): + bases = (AsyncMockMixin, cls) new = type(cls.__name__, bases, {'__doc__': cls.__doc__}) instance = object.__new__(new) return instance @@ -1019,6 +1013,25 @@ def _calls_repr(self, prefix="Calls"): return f"\n{prefix}: {safe_repr(self.mock_calls)}." +_MOCK_SIG = inspect.signature(NonCallableMock.__init__) + + +class _AnyComparer(list): + """A list which checks if it contains a call which may have an + argument of ANY, flipping the components of item and self from + their traditional locations so that ANY is guaranteed to be on + the left.""" + def __contains__(self, item): + for _call in self: + if len(item) != len(_call): + continue + if all([ + expected == actual + for expected, actual in zip(item, _call) + ]): + return True + return False + def _try_iter(obj): if obj is None: From 6fa0593d87438556ea7905eea7ef28f56998ebea Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Wirtel?= Date: Sat, 14 Sep 2019 09:35:47 +0200 Subject: [PATCH 2/2] Remove unused _AnyComparer class --- Lib/unittest/mock.py | 17 ----------------- 1 file changed, 17 deletions(-) diff --git a/Lib/unittest/mock.py b/Lib/unittest/mock.py index b86f52ae82b1bc..5a2f0b2e2b58f1 100644 --- a/Lib/unittest/mock.py +++ b/Lib/unittest/mock.py @@ -1016,23 +1016,6 @@ def _calls_repr(self, prefix="Calls"): _MOCK_SIG = inspect.signature(NonCallableMock.__init__) -class _AnyComparer(list): - """A list which checks if it contains a call which may have an - argument of ANY, flipping the components of item and self from - their traditional locations so that ANY is guaranteed to be on - the left.""" - def __contains__(self, item): - for _call in self: - if len(item) != len(_call): - continue - if all([ - expected == actual - for expected, actual in zip(item, _call) - ]): - return True - return False - - def _try_iter(obj): if obj is None: return obj