diff options
author | Carl Meyer <carl@oddbird.net> | 2022-12-23 19:41:37 (GMT) |
---|---|---|
committer | GitHub <noreply@github.com> | 2022-12-23 19:41:37 (GMT) |
commit | c5726b727e26b81a267933654cf26b760a90d9aa (patch) | |
tree | a30c81c9fd052e6e8081c28cdd787ea6d4402a7b /Lib/unittest | |
parent | a98d9ea56e7b473af54438ecc487a6bf1b4d6530 (diff) | |
download | cpython-c5726b727e26b81a267933654cf26b760a90d9aa.zip cpython-c5726b727e26b81a267933654cf26b760a90d9aa.tar.gz cpython-c5726b727e26b81a267933654cf26b760a90d9aa.tar.bz2 |
gh-83076: 3.8x speed improvement in (Async)Mock instantiation (#100252)
Diffstat (limited to 'Lib/unittest')
-rw-r--r-- | Lib/unittest/mock.py | 38 |
1 files changed, 22 insertions, 16 deletions
diff --git a/Lib/unittest/mock.py b/Lib/unittest/mock.py index a273753..583ab74 100644 --- a/Lib/unittest/mock.py +++ b/Lib/unittest/mock.py @@ -411,15 +411,18 @@ class NonCallableMock(Base): # necessary. _lock = RLock() - def __new__(cls, /, *args, **kw): + def __new__( + cls, 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, unsafe=False, **kwargs + ): # every instance has its own class # so we can create magic methods on the # class without stomping on other mocks bases = (cls,) if not issubclass(cls, AsyncMockMixin): # Check if spec is an async object or function - bound_args = _MOCK_SIG.bind_partial(cls, *args, **kw).arguments - spec_arg = bound_args.get('spec_set', bound_args.get('spec')) + spec_arg = spec_set or spec if spec_arg is not None and _is_async_obj(spec_arg): bases = (AsyncMockMixin, cls) new = type(cls.__name__, bases, {'__doc__': cls.__doc__}) @@ -505,10 +508,6 @@ class NonCallableMock(Base): _spec_signature = None _spec_asyncs = [] - for attr in dir(spec): - if iscoroutinefunction(getattr(spec, attr, None)): - _spec_asyncs.append(attr) - if spec is not None and not _is_list(spec): if isinstance(spec, type): _spec_class = spec @@ -518,7 +517,13 @@ class NonCallableMock(Base): _spec_as_instance, _eat_self) _spec_signature = res and res[1] - spec = dir(spec) + spec_list = dir(spec) + + for attr in spec_list: + if iscoroutinefunction(getattr(spec, attr, None)): + _spec_asyncs.append(attr) + + spec = spec_list __dict__ = self.__dict__ __dict__['_spec_class'] = _spec_class @@ -1057,9 +1062,6 @@ class NonCallableMock(Base): 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 @@ -2138,10 +2140,8 @@ class NonCallableMagicMock(MagicMixin, NonCallableMock): class AsyncMagicMixin(MagicMixin): - def __init__(self, /, *args, **kw): - self._mock_set_magics() # make magic work for kwargs in init - _safe_super(AsyncMagicMixin, self).__init__(*args, **kw) - self._mock_set_magics() # fix magic broken by upper level init + pass + class MagicMock(MagicMixin, Mock): """ @@ -2183,6 +2183,10 @@ class MagicProxy(Base): return self.create_mock() +_CODE_ATTRS = dir(CodeType) +_CODE_SIG = inspect.signature(partial(CodeType.__init__, None)) + + class AsyncMockMixin(Base): await_count = _delegating_property('await_count') await_args = _delegating_property('await_args') @@ -2200,7 +2204,9 @@ class AsyncMockMixin(Base): self.__dict__['_mock_await_count'] = 0 self.__dict__['_mock_await_args'] = None self.__dict__['_mock_await_args_list'] = _CallList() - code_mock = NonCallableMock(spec_set=CodeType) + code_mock = NonCallableMock(spec_set=_CODE_ATTRS) + code_mock.__dict__["_spec_class"] = CodeType + code_mock.__dict__["_spec_signature"] = _CODE_SIG code_mock.co_flags = inspect.CO_COROUTINE self.__dict__['__code__'] = code_mock self.__dict__['__name__'] = 'AsyncMock' |