Skip to content

fix: allow Final[T] instance fields in dataclasses to depend on type variables#21643

Open
adhavan18 wants to merge 1 commit into
python:masterfrom
adhavan18:fix/final-typevar-in-dataclass-instance-field
Open

fix: allow Final[T] instance fields in dataclasses to depend on type variables#21643
adhavan18 wants to merge 1 commit into
python:masterfrom
adhavan18:fix/final-typevar-in-dataclass-instance-field

Conversation

@adhavan18

Copy link
Copy Markdown

Summary

Fixes #21637.

Final[T] fields in a @dataclass body are instance attributes, not class attributes (PEP 591 / dataclass spec: "A Final dataclass field initialized in the class body is not a class attribute unless explicitly annotated with ClassVar."). The checker was unconditionally raising Final name declared in class body cannot depend on type variables for any Final[T] in a generic class body.

Before:

from dataclasses import dataclass
from typing import Final, Generic, TypeVar

T = TypeVar("T")

@dataclass(frozen=True)
class A(Generic[T]):
    value: Final[T]  # error: Final name declared in class body cannot depend on type variables

After: no error (same behaviour as value: T without Final).

Change

In mypy/checker.py, the condition that raises DEPENDENT_FINAL_IN_CLASS_BODY now skips the error when:

  1. the active class has dataclass_tag in its metadata (i.e. it is a dataclass or dataclass-transform class), and
  2. the lvalue is a Var that is not marked is_classvar.

Plain (non-dataclass) classes and explicit ClassVar fields are unaffected.

Tests

Two new test cases in test-data/unit/check-final.test:

  • testFinalDefiningTypevarsDataclassInstanceFieldFinal[T] instance field in a frozen generic dataclass must not produce an error.
  • testFinalDefiningTypevarsNonDataclassStillErrorsFinal[T] in a plain generic class must still produce the error.

All 83 existing check-final tests pass with no regressions.

…variables

PEP 591 and the dataclass spec say that a Final field declared in a
dataclass body is an *instance attribute*, not a class attribute, unless
explicitly annotated with ClassVar. The checker was unconditionally
raising 'Final name declared in class body cannot depend on type
variables' for any Final[T] in a generic class body, which is correct
for plain classes but wrong for dataclasses.

fix: when the active class has 'dataclass_tag' in its metadata and the
lvalue is a Var that is not marked is_classvar, skip the error.
non-dataclass classes and explicit ClassVar fields are unaffected.

fixes python#21637
@github-actions

Copy link
Copy Markdown
Contributor

According to mypy_primer, this change doesn't affect type check results on a corpus of open source code. ✅

@adhavan18

Copy link
Copy Markdown
Author

A quick note on scope, since the discussion in #21637 clarified it:

This PR intentionally handles only the dataclass case. Per @A5rocks and @gschaffner in the issue, the non-dataclass Final[T] class-body case is a separate matter (tracked under #8982), while the dataclass case "needs extra code to support" — which is what this change adds.

Behavior after the fix:

  • value: Final[T] in a @dataclass generic → allowed (it is an instance field, not a class attribute, per the dataclass spec).
  • x: Final[T] = ... in a plain generic class body → still errors (unchanged).
  • ClassVar[Final[T]] → still rejected earlier by the "Final must be the outermost qualifier" check, so the guard here is limited to non-ClassVar dataclass fields.

Both cases are covered by tests in check-final.test, CI is green, and mypy_primer reports no changes across the OSS corpus. Happy to adjust the scoping if a different split is preferred.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

False positive for Final attributes using typevars in dataclasses

1 participant