File tree 3 files changed +40
-1
lines changed
3 files changed +40
-1
lines changed Original file line number Diff line number Diff line change @@ -407,6 +407,10 @@ def final_iteration(self) -> bool:
407
407
def is_stub_file (self ) -> bool :
408
408
raise NotImplementedError
409
409
410
+ @abstractmethod
411
+ def analyze_simple_literal_type (self , rvalue : Expression , is_final : bool ) -> Type | None :
412
+ raise NotImplementedError
413
+
410
414
411
415
# A context for querying for configuration data about a module for
412
416
# cache invalidation purposes.
Original file line number Diff line number Diff line change @@ -368,7 +368,7 @@ def collect_attributes(self) -> list[DataclassAttribute] | None:
368
368
369
369
if isinstance (node , TypeAlias ):
370
370
ctx .api .fail (
371
- ("Type aliases inside dataclass definitions " " are not supported at runtime" ),
371
+ ("Type aliases inside dataclass definitions are not supported at runtime" ),
372
372
node ,
373
373
)
374
374
# Skip processing this node. This doesn't match the runtime behaviour,
@@ -426,6 +426,23 @@ def collect_attributes(self) -> list[DataclassAttribute] | None:
426
426
is_kw_only = bool (ctx .api .parse_bool (field_kw_only_param ))
427
427
428
428
known_attrs .add (lhs .name )
429
+
430
+ if sym .type is None and node .is_final and node .is_inferred :
431
+ # This is a special case, assignment like x: Final = 42 is classified
432
+ # annotated above, but mypy strips the `Final` turning it into x = 42.
433
+ # We do not support inferred types in dataclasses, so we can try inferring
434
+ # type for simple literals, and otherwise require an explicit type
435
+ # argument for Final[...].
436
+ typ = ctx .api .analyze_simple_literal_type (stmt .rvalue , is_final = True )
437
+ if typ :
438
+ node .type = typ
439
+ else :
440
+ ctx .api .fail (
441
+ "Need type argument for Final[...] with non-literal default in dataclass" ,
442
+ stmt ,
443
+ )
444
+ node .type = AnyType (TypeOfAny .from_error )
445
+
429
446
attrs .append (
430
447
DataclassAttribute (
431
448
name = lhs .name ,
Original file line number Diff line number Diff line change @@ -1796,3 +1796,21 @@ t: Two
1796
1796
reveal_type(t.__match_args__) # E: "Two" has no attribute "__match_args__" \
1797
1797
# N: Revealed type is "Any"
1798
1798
[builtins fixtures/dataclasses.pyi]
1799
+
1800
+ [case testFinalInDataclass]
1801
+ from dataclasses import dataclass
1802
+ from typing import Final
1803
+
1804
+ @dataclass
1805
+ class FirstClass:
1806
+ FIRST_CONST: Final = 3 # OK
1807
+
1808
+ @dataclass
1809
+ class SecondClass:
1810
+ SECOND_CONST: Final = FirstClass.FIRST_CONST # E: Need type argument for Final[...] with non-literal default in dataclass
1811
+
1812
+ reveal_type(FirstClass().FIRST_CONST) # N: Revealed type is "Literal[3]?"
1813
+ FirstClass().FIRST_CONST = 42 # E: Cannot assign to final attribute "FIRST_CONST"
1814
+ reveal_type(SecondClass().SECOND_CONST) # N: Revealed type is "Literal[3]?"
1815
+ SecondClass().SECOND_CONST = 42 # E: Cannot assign to final attribute "SECOND_CONST"
1816
+ [builtins fixtures/dataclasses.pyi]
You can’t perform that action at this time.
0 commit comments