This issue tracker has been migrated to GitHub, and is currently read-only.
For more information, see the GitHub FAQs in the Python's Developer Guide.

Author xtreak
Recipients cjw296, julianhille, lisroach, mariocj89, marseel, michael.foord, xtreak
Date 2019-11-25.09:44:19
SpamBayes Score -1.0
Marked as misclassified Yes
Message-id <1574675060.43.0.717029667938.issue38895@roundup.psfhosted.org>
In-reply-to
Content
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:
History
Date User Action Args
2019-11-25 09:44:20xtreaksetrecipients: + xtreak, cjw296, michael.foord, lisroach, mariocj89, julianhille, marseel
2019-11-25 09:44:20xtreaksetmessageid: <1574675060.43.0.717029667938.issue38895@roundup.psfhosted.org>
2019-11-25 09:44:20xtreaklinkissue38895 messages
2019-11-25 09:44:19xtreakcreate