[3.10] gh-100287: Fix unittest.mock.seal with AsyncMock (GH-100496) (#100508)

(cherry picked from commit e4b43ebb3a)

Co-authored-by: Shantanu <12621235+hauntsaninja@users.noreply.github.com>
This commit is contained in:
Shantanu 2022-12-24 14:39:27 -06:00 committed by GitHub
parent ad8d2ef54f
commit 9975d4e7ba
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 18 additions and 5 deletions

View file

@ -1010,15 +1010,15 @@ def _get_child_mock(self, /, **kw):
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

@ -8,7 +8,7 @@
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():
@ -298,6 +298,14 @@ def test_spec_normal_methods_on_class_with_mock(self):
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_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)
@ -1074,3 +1082,7 @@ async def f(x=None): pass
'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

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