https://github.com/python/cpython/commit/0f1d4a79f61d6a2e848b6b9c42f0f5f14033832d
commit: 0f1d4a79f61d6a2e848b6b9c42f0f5f14033832d
branch: main
author: Brittany Reynoso <[email protected]>
committer: JelleZijlstra <[email protected]>
date: 2026-10-06T19:19:55-07:00
summary:
gh-158936: Resolve lazy imports consistently for dataclass annotations (#158939)
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.
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 bd317aa9b0f2f37..5ec91cddaa3e109 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 a89999bb97938c0..2b4defe35f8a33d 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 000000000000000..d9d29834fe12764
--- /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 000000000000000..cd600fb6f38071d
--- /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 000000000000000..5b023af8b1b91be
--- /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]