https://github.com/python/cpython/commit/00cf7d9fc21569fcf23c87ddf4b70fd79e547fdc
commit: 00cf7d9fc21569fcf23c87ddf4b70fd79e547fdc
branch: 3.15
author: Miss Islington (bot) <[email protected]>
committer: JelleZijlstra <[email protected]>
date: 2026-10-09T12:14:47-07:00
summary:

[3.15] gh-158936: Resolve lazy imports consistently for dataclass annotations 
(GH-158939) (#158946)

With string annotations, dataclasses looks up the first name of each
annotation to detect ClassVar, InitVar and KW_ONLY. If that name was a
lazy import that could not be resolved, the lookup raised and the class
could not be created, while the same class works with non-string
annotations.

This resolves lazy imports the way annotationlib does: try to resolve
them, and treat a failure like a missing name.
(cherry picked from commit 0f1d4a79f61d6a2e848b6b9c42f0f5f14033832d)

Co-authored-by: Brittany Reynoso <[email protected]>

files:
A Lib/test/test_dataclasses/dataclass_lazy_broken.py
A Lib/test/test_dataclasses/dataclass_lazy_str.py
A Misc/NEWS.d/next/Library/2026-10-06-20-33-52.gh-issue-158936.flCm_m.rst
M Lib/dataclasses.py
M Lib/test/test_dataclasses/__init__.py

diff --git a/Lib/dataclasses.py b/Lib/dataclasses.py
index 863e4101f98bd6..ac5ec129b4bc06 100644
--- a/Lib/dataclasses.py
+++ b/Lib/dataclasses.py
@@ -806,7 +806,11 @@ def _get_type_from_annotation(annotation, cls):
     path = match[1].split(".")
     root = sys.modules.get(cls.__module__)
     for path_item in path:
-        root = getattr(root, path_item.strip(), None)
+        try:
+            root = getattr(root, path_item.strip(), None)
+        except Exception:
+            # Like annotationlib, ignore lazy imports that cannot be resolved.
+            return None
         if root is None:
             return None
 
diff --git a/Lib/test/test_dataclasses/__init__.py 
b/Lib/test/test_dataclasses/__init__.py
index 17dee87599135b..2309ad296f86ac 100644
--- a/Lib/test/test_dataclasses/__init__.py
+++ b/Lib/test/test_dataclasses/__init__.py
@@ -4525,6 +4525,11 @@ def test_classvar_module_level_import(self):
                     # won't exist on the instance.
                     self.assertNotIn('not_iv4', c.__dict__)
 
+    def test_lazy_import_annotations(self):
+        from test.test_dataclasses import dataclass_lazy_str as m
+        self.assertEqual([f.name for f in fields(m.C)], ['x', 'y'])
+        self.assertNotIsInstance(vars(m)['t'], types.LazyImportType)
+
     def test_text_annotations(self):
         from test.test_dataclasses import dataclass_textanno
 
diff --git a/Lib/test/test_dataclasses/dataclass_lazy_broken.py 
b/Lib/test/test_dataclasses/dataclass_lazy_broken.py
new file mode 100644
index 00000000000000..d9d29834fe1276
--- /dev/null
+++ b/Lib/test/test_dataclasses/dataclass_lazy_broken.py
@@ -0,0 +1 @@
+raise RuntimeError("module fails to import")
diff --git a/Lib/test/test_dataclasses/dataclass_lazy_str.py 
b/Lib/test/test_dataclasses/dataclass_lazy_str.py
new file mode 100644
index 00000000000000..cd600fb6f38071
--- /dev/null
+++ b/Lib/test/test_dataclasses/dataclass_lazy_str.py
@@ -0,0 +1,12 @@
+from __future__ import annotations
+
+import dataclasses
+lazy import typing as t
+lazy from _dataclass_test_missing_module import Missing
+lazy from test.test_dataclasses.dataclass_lazy_broken import Broken
+
[email protected]
+class C:
+    x: Missing | None = None
+    y: Broken | None = None
+    cv: t.ClassVar[int] = 0
diff --git 
a/Misc/NEWS.d/next/Library/2026-10-06-20-33-52.gh-issue-158936.flCm_m.rst 
b/Misc/NEWS.d/next/Library/2026-10-06-20-33-52.gh-issue-158936.flCm_m.rst
new file mode 100644
index 00000000000000..5b023af8b1b91b
--- /dev/null
+++ b/Misc/NEWS.d/next/Library/2026-10-06-20-33-52.gh-issue-158936.flCm_m.rst
@@ -0,0 +1,3 @@
+:func:`~dataclasses.dataclass` no longer fails when a string annotation names a
+:ref:`lazy import <lazy-imports>` that cannot be resolved, matching the
+behavior for non-string annotations.

_______________________________________________
Python-checkins mailing list -- [email protected]
To unsubscribe send an email to [email protected]
https://mail.python.org/mailman3//lists/python-checkins.python.org
Member address: [email protected]

Reply via email to