Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions Lib/dataclasses.py
Original file line number Diff line number Diff line change
Expand Up @@ -854,6 +854,9 @@ def _get_field(cls, a_name, a_type, default_kw_only):
# For the complete discussion, see https://bugs.python.org/issue33453
if isinstance(a_type, str):
a_type_annotation = _get_type_from_annotation(a_type, cls)
elif isinstance(a_type, annotationlib.ForwardRef):
# An unresolved lazy import can defer the whole annotation.
a_type_annotation = _get_type_from_annotation(a_type.__forward_arg__, cls)
else:
a_type_annotation = a_type

Expand Down
26 changes: 26 additions & 0 deletions Lib/test/test_dataclasses/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -4530,6 +4530,32 @@
self.assertEqual([f.name for f in fields(m.C)], ['x', 'y'])
self.assertNotIsInstance(vars(m)['t'], types.LazyImportType)

def test_lazy_import_forwardref_annotations(self):
m = import_helper.import_fresh_module(
'test.test_dataclasses.dataclass_lazy_initvar',
fresh=['test.test_dataclasses.dataclass_lazy_initvar_other'],
)
self.assertIsNotNone(m)
value = m.A()
for cls in (m.DirectInitVar, m.QualifiedInitVar, m.AliasedInitVar):
with self.subTest(cls=cls):
instance = cls(value)
self.assertIs(instance.received, value)
self.assertEqual(fields(cls), ())
self.assertNotIn('value', vars(instance))
self.assertIsInstance(cls.__dataclass_fields__['value'].type,

Check failure on line 4546 in Lib/test/test_dataclasses/__init__.py

View workflow job for this annotation

GitHub Actions / Lazy imports enabled / Run Tests with lazy_imports=all

test_lazy_import_forwardref_annotations (test.test_dataclasses.TestStringAnnotations.test_lazy_import_forwardref_annotations) (cls=<class 'test.test_dataclasses.dataclass_lazy_initvar_other.AliasedInitVar'>)

AssertionError: dataclasses.InitVar[A] is not an instance of <class 'annotationlib.ForwardRef'>

Check failure on line 4546 in Lib/test/test_dataclasses/__init__.py

View workflow job for this annotation

GitHub Actions / Lazy imports enabled / Run Tests with lazy_imports=all

test_lazy_import_forwardref_annotations (test.test_dataclasses.TestStringAnnotations.test_lazy_import_forwardref_annotations) (cls=<class 'test.test_dataclasses.dataclass_lazy_initvar_other.QualifiedInitVar'>)

AssertionError: dataclasses.InitVar[A] is not an instance of <class 'annotationlib.ForwardRef'>

Check failure on line 4546 in Lib/test/test_dataclasses/__init__.py

View workflow job for this annotation

GitHub Actions / Lazy imports enabled / Run Tests with lazy_imports=all

test_lazy_import_forwardref_annotations (test.test_dataclasses.TestStringAnnotations.test_lazy_import_forwardref_annotations) (cls=<class 'test.test_dataclasses.dataclass_lazy_initvar_other.DirectInitVar'>)

AssertionError: dataclasses.InitVar[A] is not an instance of <class 'annotationlib.ForwardRef'>
annotationlib.ForwardRef)

with self.subTest(cls=m.DeferredClassVar):
self.assertEqual(fields(m.DeferredClassVar), ())
self.assertEqual(vars(m.DeferredClassVar()), {})

with self.subTest(cls=m.DeferredField):
self.assertEqual([f.name for f in fields(m.DeferredField)], ['value'])
self.assertIs(m.DeferredField(value).value, value)
self.assertIsInstance(fields(m.DeferredField)[0].type,

Check failure on line 4556 in Lib/test/test_dataclasses/__init__.py

View workflow job for this annotation

GitHub Actions / Lazy imports enabled / Run Tests with lazy_imports=all

test_lazy_import_forwardref_annotations (test.test_dataclasses.TestStringAnnotations.test_lazy_import_forwardref_annotations) (cls=<class 'test.test_dataclasses.dataclass_lazy_initvar_other.DeferredField'>)

AssertionError: <class 'test.test_dataclasses.dataclass_lazy_initvar.A'> is not an instance of <class 'annotationlib.ForwardRef'>
annotationlib.ForwardRef)

def test_text_annotations(self):
from test.test_dataclasses import dataclass_textanno

Expand Down
16 changes: 16 additions & 0 deletions Lib/test/test_dataclasses/dataclass_lazy_initvar.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
from dataclasses import dataclass

from test.test_dataclasses.dataclass_lazy_initvar_other import (
AliasedInitVar, DeferredClassVar, DeferredField, DirectInitVar,
QualifiedInitVar,
)

__all__ = [
'A', 'AliasedInitVar', 'DeferredClassVar', 'DeferredField',
'DirectInitVar', 'QualifiedInitVar',
]


@dataclass
class A:
pass
40 changes: 40 additions & 0 deletions Lib/test/test_dataclasses/dataclass_lazy_initvar_other.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
from dataclasses import dataclass, InitVar
from dataclasses import InitVar as IV
from typing import ClassVar
import dataclasses

lazy from test.test_dataclasses.dataclass_lazy_initvar import A


@dataclass
class DirectInitVar:
value: InitVar[A]

def __post_init__(self, value):
self.received = value


@dataclass
class QualifiedInitVar:
value: dataclasses.InitVar[A]

def __post_init__(self, value):
self.received = value


@dataclass
class AliasedInitVar:
value: IV[A]

def __post_init__(self, value):
self.received = value


@dataclass
class DeferredClassVar:
value: ClassVar[A] = None


@dataclass
class DeferredField:
value: A
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
Fix :func:`dataclasses.dataclass` handling of :class:`~dataclasses.InitVar` and
:data:`~typing.ClassVar` annotations when a lazy import cannot yet be resolved
during a circular import.
Loading