Skip to content

Dataclass __init__ annotation eagerly evaluated #128184

Closed
@nickdrozd

Description

@nickdrozd

Bug report

Bug description:

In 3.14, evaluation of annotations is deferred. Except, annotations for the __init__ method of a dataclass are eagerly evaluated.

from dataclasses import dataclass
from typing import TYPE_CHECKING

if TYPE_CHECKING:
    Z = int

@dataclass
class X:
    def __init__(self, _: Z):  # <-- NameError: name 'Z' is not defined
        pass

    def f(self, _: Z):  # <-- no __init__, no problem
        pass

class Y:
    def __init__(self, _: Z):  # <-- no dataclass, no problem
        pass

Running this blows up with a scary stack trace:

Traceback (most recent call last):
  File "/home/nick/test/annotation.py", line 7, in <module>
    @dataclass
     ^^^^^^^^^
  File "/usr/local/lib/python3.14/dataclasses.py", line 1365, in dataclass
    return wrap(cls)
  File "/usr/local/lib/python3.14/dataclasses.py", line 1355, in wrap
    return _process_class(cls, init, repr, eq, order, unsafe_hash,
                          frozen, match_args, kw_only, slots,
                          weakref_slot)
  File "/usr/local/lib/python3.14/dataclasses.py", line 1166, in _process_class
    text_sig = str(inspect.signature(cls)).replace(' -> None', '')
                   ~~~~~~~~~~~~~~~~~^^^^^
  File "/usr/local/lib/python3.14/inspect.py", line 3281, in signature
    return Signature.from_callable(obj, follow_wrapped=follow_wrapped,
           ~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
                                   globals=globals, locals=locals, eval_str=eval_str,
                                   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
                                   annotation_format=annotation_format)
                                   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.14/inspect.py", line 2999, in from_callable
    return _signature_from_callable(obj, sigcls=cls,
                                    follow_wrapper_chains=follow_wrapped,
                                    globals=globals, locals=locals, eval_str=eval_str,
                                    annotation_format=annotation_format)
  File "/usr/local/lib/python3.14/inspect.py", line 2524, in _signature_from_callable
    return _get_signature_of(init)
  File "/usr/local/lib/python3.14/inspect.py", line 2421, in _signature_from_callable
    sig = _get_signature_of(obj.__func__)
  File "/usr/local/lib/python3.14/inspect.py", line 2492, in _signature_from_callable
    return _signature_from_function(sigcls, obj,
                                    skip_bound_arg=skip_bound_arg,
                                    globals=globals, locals=locals, eval_str=eval_str,
                                    annotation_format=annotation_format)
  File "/usr/local/lib/python3.14/inspect.py", line 2315, in _signature_from_function
    annotations = get_annotations(func, globals=globals, locals=locals, eval_str=eval_str,
                                  format=annotation_format)
  File "/usr/local/lib/python3.14/annotationlib.py", line 707, in get_annotations
    ann = _get_dunder_annotations(obj)
  File "/usr/local/lib/python3.14/annotationlib.py", line 847, in _get_dunder_annotations
    ann = getattr(obj, "__annotations__", None)
  File "/home/nick/test/annotation.py", line 9, in __annotate__
    def __init__(self, _: Z):  # <-- NameError: name 'Z' is not defined
                          ^
NameError: name 'Z' is not defined

AFAICT, this behavior is specific to __init__ with dataclass. I don't know what's intended, but as a user it feels like a bug.

Adding from __future__ import annotations causes the problem to go away. But that shouldn't be required in 3.14.

CPython versions tested on:

3.14, CPython main branch

Operating systems tested on:

Linux

Linked PRs

Metadata

Metadata

Assignees

Labels

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions