Skip to content

Commit 3ae4cd6

Browse files
committed
gh-160665: Recognize dataclass markers deferred by lazy imports
1 parent dc28c3a commit 3ae4cd6

5 files changed

Lines changed: 88 additions & 0 deletions

File tree

‎Lib/dataclasses.py‎

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -854,6 +854,9 @@ def _get_field(cls, a_name, a_type, default_kw_only):
854854
# For the complete discussion, see https://bugs.python.org/issue33453
855855
if isinstance(a_type, str):
856856
a_type_annotation = _get_type_from_annotation(a_type, cls)
857+
elif isinstance(a_type, annotationlib.ForwardRef):
858+
# An unresolved lazy import can defer the whole annotation.
859+
a_type_annotation = _get_type_from_annotation(a_type.__forward_arg__, cls)
857860
else:
858861
a_type_annotation = a_type
859862

‎Lib/test/test_dataclasses/__init__.py‎

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4530,6 +4530,32 @@ def test_lazy_import_annotations(self):
45304530
self.assertEqual([f.name for f in fields(m.C)], ['x', 'y'])
45314531
self.assertNotIsInstance(vars(m)['t'], types.LazyImportType)
45324532

4533+
def test_lazy_import_forwardref_annotations(self):
4534+
m = import_helper.import_fresh_module(
4535+
'test.test_dataclasses.dataclass_lazy_initvar',
4536+
fresh=['test.test_dataclasses.dataclass_lazy_initvar_other'],
4537+
)
4538+
self.assertIsNotNone(m)
4539+
value = m.A()
4540+
for cls in (m.DirectInitVar, m.QualifiedInitVar, m.AliasedInitVar):
4541+
with self.subTest(cls=cls):
4542+
instance = cls(value)
4543+
self.assertIs(instance.received, value)
4544+
self.assertEqual(fields(cls), ())
4545+
self.assertNotIn('value', vars(instance))
4546+
self.assertIsInstance(cls.__dataclass_fields__['value'].type,
4547+
annotationlib.ForwardRef)
4548+
4549+
with self.subTest(cls=m.DeferredClassVar):
4550+
self.assertEqual(fields(m.DeferredClassVar), ())
4551+
self.assertEqual(vars(m.DeferredClassVar()), {})
4552+
4553+
with self.subTest(cls=m.DeferredField):
4554+
self.assertEqual([f.name for f in fields(m.DeferredField)], ['value'])
4555+
self.assertIs(m.DeferredField(value).value, value)
4556+
self.assertIsInstance(fields(m.DeferredField)[0].type,
4557+
annotationlib.ForwardRef)
4558+
45334559
def test_text_annotations(self):
45344560
from test.test_dataclasses import dataclass_textanno
45354561

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
from dataclasses import dataclass
2+
3+
from test.test_dataclasses.dataclass_lazy_initvar_other import (
4+
AliasedInitVar, DeferredClassVar, DeferredField, DirectInitVar,
5+
QualifiedInitVar,
6+
)
7+
8+
__all__ = [
9+
'A', 'AliasedInitVar', 'DeferredClassVar', 'DeferredField',
10+
'DirectInitVar', 'QualifiedInitVar',
11+
]
12+
13+
14+
@dataclass
15+
class A:
16+
pass
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
from dataclasses import dataclass, InitVar
2+
from dataclasses import InitVar as IV
3+
from typing import ClassVar
4+
import dataclasses
5+
6+
lazy from test.test_dataclasses.dataclass_lazy_initvar import A
7+
8+
9+
@dataclass
10+
class DirectInitVar:
11+
value: InitVar[A]
12+
13+
def __post_init__(self, value):
14+
self.received = value
15+
16+
17+
@dataclass
18+
class QualifiedInitVar:
19+
value: dataclasses.InitVar[A]
20+
21+
def __post_init__(self, value):
22+
self.received = value
23+
24+
25+
@dataclass
26+
class AliasedInitVar:
27+
value: IV[A]
28+
29+
def __post_init__(self, value):
30+
self.received = value
31+
32+
33+
@dataclass
34+
class DeferredClassVar:
35+
value: ClassVar[A] = None
36+
37+
38+
@dataclass
39+
class DeferredField:
40+
value: A
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
Fix :func:`dataclasses.dataclass` handling of :class:`~dataclasses.InitVar` and
2+
:data:`~typing.ClassVar` annotations when a lazy import cannot yet be resolved
3+
during a circular import.

0 commit comments

Comments
 (0)