Skip to content

Commit cfb0993

Browse files
Benitexjakezychrchristof
committed
feat: error for Final fields with init=False (#13119)
Test cases from PR #14285, thanks to @jakezych. Co-Authored-By: Jake Zych <[email protected]> Co-Authored-By: Rafael Christof <[email protected]>
1 parent f2db975 commit cfb0993

File tree

2 files changed

+64
-0
lines changed

2 files changed

+64
-0
lines changed

mypy/plugins/dataclasses.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -651,6 +651,14 @@ def collect_attributes(self) -> list[DataclassAttribute] | None:
651651
elif not isinstance(stmt.rvalue, TempNode):
652652
has_default = True
653653

654+
if node.is_final and not is_in_init and not has_default:
655+
has_post_init = cls.info.get("__post_init__") is not None
656+
if not has_post_init:
657+
self._api.fail(
658+
f'Final field with init=False must have a default value',
659+
stmt.rvalue
660+
)
661+
654662
if not has_default and self._spec is _TRANSFORM_SPEC_FOR_DATACLASSES:
655663
# Make all non-default dataclass attributes implicit because they are de-facto
656664
# set on self in the generated __init__(), not in the class body. On the other

test-data/unit/check-dataclasses.test

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2737,3 +2737,59 @@ class ClassB(ClassA):
27372737
def value(self) -> int:
27382738
return 0
27392739
[builtins fixtures/dict.pyi]
2740+
2741+
[case testErrorFinalFieldNoInitNoArgumentPassed]
2742+
from typing import Final
2743+
from dataclasses import dataclass, field
2744+
@dataclass
2745+
class Foo:
2746+
a: Final[int] = field(init=False) # E: Final field with init=False must have a default value
2747+
Foo().a
2748+
[builtins fixtures/dataclasses.pyi]
2749+
2750+
[case testErrorFinalFieldInitNoArgumentPassed]
2751+
from typing import Final
2752+
from dataclasses import dataclass, field
2753+
2754+
@dataclass
2755+
class Foo:
2756+
a: Final[int] = field()
2757+
2758+
Foo().a # E: Missing positional argument "a" in call to "Foo"
2759+
[builtins fixtures/dataclasses.pyi]
2760+
2761+
[case testFinalFieldGeneratedInitArgumentPassed]
2762+
from typing import Final
2763+
from dataclasses import dataclass, field
2764+
2765+
@dataclass
2766+
class Foo:
2767+
a: Final[int] = field()
2768+
2769+
Foo(1).a
2770+
[builtins fixtures/dataclasses.pyi]
2771+
2772+
[case testFinalFieldPostInit]
2773+
from typing import Final
2774+
from dataclasses import dataclass, field
2775+
2776+
@dataclass
2777+
class Foo:
2778+
a: Final[int] = field(init=False)
2779+
2780+
def __post_init__(self):
2781+
self.a = 1
2782+
2783+
Foo().a
2784+
[builtins fixtures/dataclasses.pyi]
2785+
2786+
[case testFinalFieldInitFalseWithDefault]
2787+
from typing import Final
2788+
from dataclasses import dataclass, field
2789+
2790+
@dataclass
2791+
class Foo:
2792+
a: Final[int] = field(init=False, default=1)
2793+
2794+
Foo().a
2795+
[builtins fixtures/dataclasses.pyi]

0 commit comments

Comments
 (0)