Karthikeyan Singaravelan <tir.kar...@gmail.com> added the comment:
Another point is that _spec_asyncs is a list of attributes that pass asyncio.iscoroutinefunction which could be also little expensive [0]. The check is made for the attribute to be async only when the child mock is created to return an AsyncMock [1] during creation. This could be moved to _get_child_mock so that the Mock creation itself for all other mocks and common use case is faster. Creating child mocks will have the iscoroutine function check performed where maybe we can populate the _spec_async list and use it for subsequent calls. # Baseline 3.7 $ python3.7 -m timeit -s 'from unittest.mock import Mock' 'Mock()' 20000 loops, best of 5: 17.6 usec per loop # Move NonCallableMock.__init__ signature to module level attribute. (Python 3.8 branch HEAD) $ ./python.exe -m timeit -s 'from unittest.mock import Mock' 'Mock()' 5000 loops, best of 5: 62.1 usec per loop # Move the iscoroutinefunction check to the child mock creation. I didn't do the child mock creation benchmark yet and populating _spec_async as the attribute is found to be async would resolve doing iscoroutinefunction check everytime. (Python 3.8 branch HEAD) $ ./python.exe -m timeit -s 'from unittest.mock import Mock' 'Mock()' 10000 loops, best of 5: 28.3 usec per loop [0] https://github.com/python/cpython/blob/27fc3b6f3fc49a36d3f962caac5c5495696d12ed/Lib/unittest/mock.py#L488-L492 [1] https://github.com/python/cpython/blob/27fc3b6f3fc49a36d3f962caac5c5495696d12ed/Lib/unittest/mock.py#L987 diff --git a/Lib/unittest/mock.py b/Lib/unittest/mock.py index 488ab1c23d..7ff99407ab 100644 --- a/Lib/unittest/mock.py +++ b/Lib/unittest/mock.py @@ -403,7 +403,6 @@ class NonCallableMock(Base): bases = (cls,) if not issubclass(cls, AsyncMock): # 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() @@ -491,11 +490,6 @@ class NonCallableMock(Base): _eat_self=False): _spec_class = None _spec_signature = None - _spec_asyncs = [] - - for attr in dir(spec): - if asyncio.iscoroutinefunction(getattr(spec, attr, None)): - _spec_asyncs.append(attr) if spec is not None and not _is_list(spec): if isinstance(spec, type): @@ -513,7 +507,6 @@ class NonCallableMock(Base): __dict__['_spec_set'] = spec_set __dict__['_spec_signature'] = _spec_signature __dict__['_mock_methods'] = spec - __dict__['_spec_asyncs'] = _spec_asyncs def __get_return_value(self): ret = self._mock_return_value @@ -989,7 +982,8 @@ class NonCallableMock(Base): For non-callable mocks the callable variant will be used (rather than any custom subclass).""" _new_name = kw.get("_new_name") - if _new_name in self.__dict__['_spec_asyncs']: + attribute = getattr(self.__dict__['_spec_class'], _new_name, None) + if asyncio.iscoroutinefunction(attribute): return AsyncMock(**kw) _type = type(self) @@ -1032,6 +1026,8 @@ class NonCallableMock(Base): return f"\n{prefix}: {safe_repr(self.mock_calls)}." +sig = inspect.signature(NonCallableMock.__init__) + def _try_iter(obj): if obj is None: ---------- _______________________________________ Python tracker <rep...@bugs.python.org> <https://bugs.python.org/issue38895> _______________________________________ _______________________________________________ Python-bugs-list mailing list Unsubscribe: https://mail.python.org/mailman/options/python-bugs-list/archive%40mail-archive.com