Skip to content
/ mypy Public
  • Sponsor python/mypy

  • Notifications You must be signed in to change notification settings
  • Fork 2.9k

Commit a9bc366

Browse files
authoredAug 27, 2022
Fix crash on bare Final in dataclass (#13528)
Fixes #10090 Unfortunately we cannot fully support this use case. Mypy requires explicit type annotations to generate methods for dataclasses before type checking. While type inference for bare `Final` happens during type checking. I still try to infer type if the default is a literal, otherwise give an error, and use `Any` for generated methods.
1 parent 49983fc commit a9bc366

File tree

3 files changed

+40
-1
lines changed

3 files changed

+40
-1
lines changed
 

‎mypy/plugin.py

+4
Original file line numberDiff line numberDiff line change
@@ -407,6 +407,10 @@ def final_iteration(self) -> bool:
407407
def is_stub_file(self) -> bool:
408408
raise NotImplementedError
409409

410+
@abstractmethod
411+
def analyze_simple_literal_type(self, rvalue: Expression, is_final: bool) -> Type | None:
412+
raise NotImplementedError
413+
410414

411415
# A context for querying for configuration data about a module for
412416
# cache invalidation purposes.

‎mypy/plugins/dataclasses.py

+18-1
Original file line numberDiff line numberDiff line change
@@ -368,7 +368,7 @@ def collect_attributes(self) -> list[DataclassAttribute] | None:
368368

369369
if isinstance(node, TypeAlias):
370370
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"),
372372
node,
373373
)
374374
# Skip processing this node. This doesn't match the runtime behaviour,
@@ -426,6 +426,23 @@ def collect_attributes(self) -> list[DataclassAttribute] | None:
426426
is_kw_only = bool(ctx.api.parse_bool(field_kw_only_param))
427427

428428
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+
429446
attrs.append(
430447
DataclassAttribute(
431448
name=lhs.name,

‎test-data/unit/check-dataclasses.test

+18
Original file line numberDiff line numberDiff line change
@@ -1796,3 +1796,21 @@ t: Two
17961796
reveal_type(t.__match_args__) # E: "Two" has no attribute "__match_args__" \
17971797
# N: Revealed type is "Any"
17981798
[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]

0 commit comments

Comments
 (0)
Please sign in to comment.