Lazy imports break dataclass InitVars in extremely rare scenarios #160665
Description
Bug report
3 files are required to get this bug to happen:
testing.py
from testing_2 import A
from testing_3 import B
a = A()
b = B(a)testing_2.py
from dataclasses import dataclass
from testing_3 import B
@dataclass
class A:
passtesting_3.py
from dataclasses import InitVar, dataclass
lazy from testing_2 import A
@dataclass
class B:
a: InitVar[A]
def __post_init__(self, a: A) -> None:
print(a)If you run testing.py with python 3.15 you will get TypeError: B.__post_init__() missing 1 required positional argument: 'a'
The key thing that causes the error seems to be the presence of the import cycle combined with the lazy import. It's worth noting that if you use a TYPE_CHECKING guard instead as described below, it does also work on 3.15.
I found this issue while migrating a bunch of my TYPE_CHECKING guards to lazy imports - infact as PEP 810 says "Lazy imports eliminate the common need for TYPE_CHECKING guards". The version of python that breaks is cpython-3.15.0a7-linux-x86_64-gnu. The code instead works on cpython-3.14.3-linux-x86_64-gnu, if of course you replace the lazy import in testing_3.py with a if TYPE_CHECKING: block
CPython versions tested on:
3.15
Operating systems tested on:
Linux
Activity
DavidCEllis commentedon Oct 26, 2026
I think this is more of a direct annotations issue than a dataclasses one.
Dataclasses is failing to pick up the
InitVarbecause the failing lazy import causes the whole annotation to fail to evaluate, while if only the name doesn't exist, then__missing__is triggered in the stringifier dict and the name eventually gets replaced with a ForwardRef.Demonstrating the same idea with
list[...]on the currentmain:{'a': ForwardRef('list[LazyFail]', is_class=True, owner=<class '__main__.Example'>), 'b': list[ForwardRef('FakeSucceed', is_class=True, owner=<class '__main__.Example'>)]}Here we get
ForwardRef('list[...]')where a lazy import fails, butlist[ForwardRef('...')]for a standard unresolvable name.I think the fix is making annotationlib handle unresolvable lazy imports better, rather than making dataclasses handle strings again like in the current PR.
pythongh-160665: Resolve dataclass fixture imports before defining A
DavidCEllis commentedon Oct 27, 2026
This also fails even under 3.14 without any lazy imports if you use a module import for
testing_2:testing3.py
The solution for this is a little more complicated as it requires handling
AttributeErrors better in general than we currently do as the import resolves but the attribute doesn't exist yet.pythongh-160665: Preserve annotation structure for unresolved lazy im…