Skip to content

Lazy imports break dataclass InitVars in extremely rare scenarios #160665

Description

@lor1113

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:
    pass

testing_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

Linked PRs

Activity

  1. DavidCEllis commented on Oct 26, 2026

    @DavidCEllis
    Contributor

    I think this is more of a direct annotations issue than a dataclasses one.

    Dataclasses is failing to pick up the InitVar because 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 current main:

    from annotationlib import get_annotations, Format
    from pprint import pp
    from typing import TYPE_CHECKING
    
    lazy from failing_import import LazyFail
    
    if TYPE_CHECKING:
        from fake_import import FakeSucceed
    
    class Example:
        a: list[LazyFail]
        b: list[FakeSucceed]
    
    pp(get_annotations(Example, format=Format.FORWARDREF))
    {'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, but list[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.

  2. added a commit that references this issue on Oct 26, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    stdlibStandard Library Python modules in the Lib/ directorytopic-lazy-importstype-bugAn unexpected behavior, bug, or error

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions