gh-100287: Fix unittest.mock.seal with AsyncMock (#100496)

This commit is contained in:
Shantanu 2022-12-24 13:39:39 -06:00 committed by GitHub
parent 46e6a28308
commit e4b43ebb3a
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 18 additions and 5 deletions

View file

@ -11,7 +11,7 @@ support.requires_working_socket(module=True)
from asyncio import run, iscoroutinefunction from asyncio import run, iscoroutinefunction
from unittest import IsolatedAsyncioTestCase from unittest import IsolatedAsyncioTestCase
from unittest.mock import (ANY, call, AsyncMock, patch, MagicMock, Mock, from unittest.mock import (ANY, call, AsyncMock, patch, MagicMock, Mock,
create_autospec, sentinel, _CallList) create_autospec, sentinel, _CallList, seal)
def tearDownModule(): def tearDownModule():
@ -300,6 +300,14 @@ 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_normal_methods_on_class_with_mock_seal(self):
mock = Mock(AsyncClass)
seal(mock)
with self.assertRaises(AttributeError):
mock.normal_method
with self.assertRaises(AttributeError):
mock.async_method
def test_spec_async_attributes_instance(self): def test_spec_async_attributes_instance(self):
async_instance = AsyncClass() async_instance = AsyncClass()
async_instance.async_func_attr = async_func async_instance.async_func_attr = async_func
@ -1089,3 +1097,7 @@ class AsyncMockAssert(unittest.TestCase):
'Actual: [call(1)]'))) as cm: 'Actual: [call(1)]'))) as cm:
self.mock.assert_has_awaits([call(), call(1, 2)]) self.mock.assert_has_awaits([call(), call(1, 2)])
self.assertIsInstance(cm.exception.__cause__, TypeError) self.assertIsInstance(cm.exception.__cause__, TypeError)
if __name__ == '__main__':
unittest.main()

View file

@ -1019,15 +1019,15 @@ class NonCallableMock(Base):
For non-callable mocks the callable variant will be used (rather than For non-callable mocks the callable variant will be used (rather than
any custom subclass).""" any custom subclass)."""
_new_name = kw.get("_new_name")
if _new_name in self.__dict__['_spec_asyncs']:
return AsyncMock(**kw)
if self._mock_sealed: if self._mock_sealed:
attribute = f".{kw['name']}" if "name" in kw else "()" attribute = f".{kw['name']}" if "name" in kw else "()"
mock_name = self._extract_mock_name() + attribute mock_name = self._extract_mock_name() + attribute
raise AttributeError(mock_name) raise AttributeError(mock_name)
_new_name = kw.get("_new_name")
if _new_name in self.__dict__['_spec_asyncs']:
return AsyncMock(**kw)
_type = type(self) _type = type(self)
if issubclass(_type, MagicMock) and _new_name in _async_method_magics: if issubclass(_type, MagicMock) and _new_name in _async_method_magics:
# Any asynchronous magic becomes an AsyncMock # Any asynchronous magic becomes an AsyncMock

View file

@ -0,0 +1 @@
Fix the interaction of :func:`unittest.mock.seal` with :class:`unittest.mock.AsyncMock`.