mirror of
https://github.com/python/cpython.git
synced 2025-09-27 10:50:04 +00:00
gh-83076: 3.8x speed improvement in (Async)Mock instantiation (#100252)
This commit is contained in:
parent
a98d9ea56e
commit
c5726b727e
3 changed files with 36 additions and 16 deletions
|
@ -300,6 +300,19 @@ class AsyncSpecTest(unittest.TestCase):
|
||||||
self.assertIsInstance(mock.async_method, AsyncMock)
|
self.assertIsInstance(mock.async_method, AsyncMock)
|
||||||
self.assertIsInstance(mock.normal_method, Mock)
|
self.assertIsInstance(mock.normal_method, Mock)
|
||||||
|
|
||||||
|
def test_spec_async_attributes_instance(self):
|
||||||
|
async_instance = AsyncClass()
|
||||||
|
async_instance.async_func_attr = async_func
|
||||||
|
async_instance.later_async_func_attr = normal_func
|
||||||
|
|
||||||
|
mock_async_instance = Mock(spec_set=async_instance)
|
||||||
|
|
||||||
|
async_instance.later_async_func_attr = async_func
|
||||||
|
|
||||||
|
self.assertIsInstance(mock_async_instance.async_func_attr, AsyncMock)
|
||||||
|
# only the shape of the spec at the time of mock construction matters
|
||||||
|
self.assertNotIsInstance(mock_async_instance.later_async_func_attr, AsyncMock)
|
||||||
|
|
||||||
def test_spec_mock_type_kw(self):
|
def test_spec_mock_type_kw(self):
|
||||||
def inner_test(mock_type):
|
def inner_test(mock_type):
|
||||||
async_mock = mock_type(spec=async_func)
|
async_mock = mock_type(spec=async_func)
|
||||||
|
|
|
@ -411,15 +411,18 @@ class NonCallableMock(Base):
|
||||||
# necessary.
|
# necessary.
|
||||||
_lock = RLock()
|
_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
|
# every instance has its own class
|
||||||
# so we can create magic methods on the
|
# so we can create magic methods on the
|
||||||
# class without stomping on other mocks
|
# class without stomping on other mocks
|
||||||
bases = (cls,)
|
bases = (cls,)
|
||||||
if not issubclass(cls, AsyncMockMixin):
|
if not issubclass(cls, AsyncMockMixin):
|
||||||
# Check if spec is an async object or function
|
# Check if spec is an async object or function
|
||||||
bound_args = _MOCK_SIG.bind_partial(cls, *args, **kw).arguments
|
spec_arg = spec_set or spec
|
||||||
spec_arg = bound_args.get('spec_set', bound_args.get('spec'))
|
|
||||||
if spec_arg is not None and _is_async_obj(spec_arg):
|
if spec_arg is not None and _is_async_obj(spec_arg):
|
||||||
bases = (AsyncMockMixin, cls)
|
bases = (AsyncMockMixin, cls)
|
||||||
new = type(cls.__name__, bases, {'__doc__': cls.__doc__})
|
new = type(cls.__name__, bases, {'__doc__': cls.__doc__})
|
||||||
|
@ -505,10 +508,6 @@ class NonCallableMock(Base):
|
||||||
_spec_signature = None
|
_spec_signature = None
|
||||||
_spec_asyncs = []
|
_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 spec is not None and not _is_list(spec):
|
||||||
if isinstance(spec, type):
|
if isinstance(spec, type):
|
||||||
_spec_class = spec
|
_spec_class = spec
|
||||||
|
@ -518,7 +517,13 @@ class NonCallableMock(Base):
|
||||||
_spec_as_instance, _eat_self)
|
_spec_as_instance, _eat_self)
|
||||||
_spec_signature = res and res[1]
|
_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__ = self.__dict__
|
||||||
__dict__['_spec_class'] = _spec_class
|
__dict__['_spec_class'] = _spec_class
|
||||||
|
@ -1057,9 +1062,6 @@ class NonCallableMock(Base):
|
||||||
return f"\n{prefix}: {safe_repr(self.mock_calls)}."
|
return f"\n{prefix}: {safe_repr(self.mock_calls)}."
|
||||||
|
|
||||||
|
|
||||||
_MOCK_SIG = inspect.signature(NonCallableMock.__init__)
|
|
||||||
|
|
||||||
|
|
||||||
class _AnyComparer(list):
|
class _AnyComparer(list):
|
||||||
"""A list which checks if it contains a call which may have an
|
"""A list which checks if it contains a call which may have an
|
||||||
argument of ANY, flipping the components of item and self from
|
argument of ANY, flipping the components of item and self from
|
||||||
|
@ -2138,10 +2140,8 @@ class NonCallableMagicMock(MagicMixin, NonCallableMock):
|
||||||
|
|
||||||
|
|
||||||
class AsyncMagicMixin(MagicMixin):
|
class AsyncMagicMixin(MagicMixin):
|
||||||
def __init__(self, /, *args, **kw):
|
pass
|
||||||
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
|
|
||||||
|
|
||||||
class MagicMock(MagicMixin, Mock):
|
class MagicMock(MagicMixin, Mock):
|
||||||
"""
|
"""
|
||||||
|
@ -2183,6 +2183,10 @@ class MagicProxy(Base):
|
||||||
return self.create_mock()
|
return self.create_mock()
|
||||||
|
|
||||||
|
|
||||||
|
_CODE_ATTRS = dir(CodeType)
|
||||||
|
_CODE_SIG = inspect.signature(partial(CodeType.__init__, None))
|
||||||
|
|
||||||
|
|
||||||
class AsyncMockMixin(Base):
|
class AsyncMockMixin(Base):
|
||||||
await_count = _delegating_property('await_count')
|
await_count = _delegating_property('await_count')
|
||||||
await_args = _delegating_property('await_args')
|
await_args = _delegating_property('await_args')
|
||||||
|
@ -2200,7 +2204,9 @@ class AsyncMockMixin(Base):
|
||||||
self.__dict__['_mock_await_count'] = 0
|
self.__dict__['_mock_await_count'] = 0
|
||||||
self.__dict__['_mock_await_args'] = None
|
self.__dict__['_mock_await_args'] = None
|
||||||
self.__dict__['_mock_await_args_list'] = _CallList()
|
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
|
code_mock.co_flags = inspect.CO_COROUTINE
|
||||||
self.__dict__['__code__'] = code_mock
|
self.__dict__['__code__'] = code_mock
|
||||||
self.__dict__['__name__'] = 'AsyncMock'
|
self.__dict__['__name__'] = 'AsyncMock'
|
||||||
|
|
|
@ -0,0 +1 @@
|
||||||
|
Instantiation of ``Mock()`` and ``AsyncMock()`` is now 3.8x faster.
|
Loading…
Add table
Add a link
Reference in a new issue