https://github.com/python/cpython/commit/79df5d1c54f9c2aaccae48a75e40ac629ed0cca0
commit: 79df5d1c54f9c2aaccae48a75e40ac629ed0cca0
branch: 3.14
author: Miss Islington (bot) <31488909+miss-isling...@users.noreply.github.com>
committer: sobolevn <m...@sobolevn.me>
date: 2025-06-14T09:11:31Z
summary:

[3.14] gh-135368: Fix mocks on dataclass specs with `instance=True` (GH-135421) 
(#135503)

gh-135368: Fix mocks on dataclass specs with `instance=True` (GH-135421)

* gh-135368: Fix mocks on dataclass specs with `instance=True`

* Extend dataclass mock_methods

---------
(cherry picked from commit c8319a3fea9ff7f9b49008be3b5d681112bbe7f3)

Co-authored-by: sobolevn <m...@sobolevn.me>
Co-authored-by: Marc Mueller <30130371+cdc...@users.noreply.github.com>

files:
A Misc/NEWS.d/next/Library/2025-06-12-10-45-02.gh-issue-135368.OjWVHL.rst
M Lib/test/test_unittest/testmock/testhelpers.py
M Lib/unittest/mock.py

diff --git a/Lib/test/test_unittest/testmock/testhelpers.py 
b/Lib/test/test_unittest/testmock/testhelpers.py
index d1e48bde982040..0e82c723ec3eaa 100644
--- a/Lib/test/test_unittest/testmock/testhelpers.py
+++ b/Lib/test/test_unittest/testmock/testhelpers.py
@@ -1050,6 +1050,7 @@ def __post_init__(self):
             create_autospec(WithPostInit()),
         ]:
             with self.subTest(mock=mock):
+                self.assertIsInstance(mock, WithPostInit)
                 self.assertIsInstance(mock.a, int)
                 self.assertIsInstance(mock.b, int)
 
@@ -1072,6 +1073,7 @@ class WithDefault:
             create_autospec(WithDefault(1)),
         ]:
             with self.subTest(mock=mock):
+                self.assertIsInstance(mock, WithDefault)
                 self.assertIsInstance(mock.a, int)
                 self.assertIsInstance(mock.b, int)
 
@@ -1087,6 +1089,7 @@ def b(self) -> int:
             create_autospec(WithMethod(1)),
         ]:
             with self.subTest(mock=mock):
+                self.assertIsInstance(mock, WithMethod)
                 self.assertIsInstance(mock.a, int)
                 mock.b.assert_not_called()
 
@@ -1102,11 +1105,29 @@ class WithNonFields:
             create_autospec(WithNonFields(1)),
         ]:
             with self.subTest(mock=mock):
+                self.assertIsInstance(mock, WithNonFields)
                 with self.assertRaisesRegex(AttributeError, msg):
                     mock.a
                 with self.assertRaisesRegex(AttributeError, msg):
                     mock.b
 
+    def test_dataclass_special_attrs(self):
+        @dataclass
+        class Description:
+            name: str
+
+        for mock in [
+            create_autospec(Description, instance=True),
+            create_autospec(Description(1)),
+        ]:
+            with self.subTest(mock=mock):
+                self.assertIsInstance(mock, Description)
+                self.assertIs(mock.__class__, Description)
+                self.assertIsInstance(mock.__dataclass_fields__, MagicMock)
+                self.assertIsInstance(mock.__dataclass_params__, MagicMock)
+                self.assertIsInstance(mock.__match_args__, MagicMock)
+                self.assertIsInstance(mock.__hash__, MagicMock)
+
 class TestCallList(unittest.TestCase):
 
     def test_args_list_contains_call_list(self):
diff --git a/Lib/unittest/mock.py b/Lib/unittest/mock.py
index 55cb4b1f6aff90..e370aa48b7c703 100644
--- a/Lib/unittest/mock.py
+++ b/Lib/unittest/mock.py
@@ -569,6 +569,11 @@ def _mock_add_spec(self, spec, spec_set, 
_spec_as_instance=False,
         __dict__['_mock_methods'] = spec
         __dict__['_spec_asyncs'] = _spec_asyncs
 
+    def _mock_extend_spec_methods(self, spec_methods):
+        methods = self.__dict__.get('_mock_methods') or []
+        methods.extend(spec_methods)
+        self.__dict__['_mock_methods'] = methods
+
     def __get_return_value(self):
         ret = self._mock_return_value
         if self._mock_delegate is not None:
@@ -2766,14 +2771,16 @@ def create_autospec(spec, spec_set=False, 
instance=False, _parent=None,
         raise InvalidSpecError(f'Cannot autospec a Mock object. '
                                f'[object={spec!r}]')
     is_async_func = _is_async_func(spec)
+    _kwargs = {'spec': spec}
 
     entries = [(entry, _missing) for entry in dir(spec)]
     if is_type and instance and is_dataclass(spec):
+        is_dataclass_spec = True
         dataclass_fields = fields(spec)
         entries.extend((f.name, f.type) for f in dataclass_fields)
-        _kwargs = {'spec': [f.name for f in dataclass_fields]}
+        dataclass_spec_list = [f.name for f in dataclass_fields]
     else:
-        _kwargs = {'spec': spec}
+        is_dataclass_spec = False
 
     if spec_set:
         _kwargs = {'spec_set': spec}
@@ -2810,6 +2817,8 @@ def create_autospec(spec, spec_set=False, instance=False, 
_parent=None,
 
     mock = Klass(parent=_parent, _new_parent=_parent, _new_name=_new_name,
                  name=_name, **_kwargs)
+    if is_dataclass_spec:
+        mock._mock_extend_spec_methods(dataclass_spec_list)
 
     if isinstance(spec, FunctionTypes):
         # should only happen at the top level because we don't
diff --git 
a/Misc/NEWS.d/next/Library/2025-06-12-10-45-02.gh-issue-135368.OjWVHL.rst 
b/Misc/NEWS.d/next/Library/2025-06-12-10-45-02.gh-issue-135368.OjWVHL.rst
new file mode 100644
index 00000000000000..b9973d88a859c6
--- /dev/null
+++ b/Misc/NEWS.d/next/Library/2025-06-12-10-45-02.gh-issue-135368.OjWVHL.rst
@@ -0,0 +1,2 @@
+Fix :class:`unittest.mock.Mock` generation on :func:`dataclasses.dataclass`
+objects. Now all special attributes are set as it was before :gh:`124429`.

_______________________________________________
Python-checkins mailing list -- python-checkins@python.org
To unsubscribe send an email to python-checkins-le...@python.org
https://mail.python.org/mailman3//lists/python-checkins.python.org
Member address: arch...@mail-archive.com

Reply via email to