Open glyph opened 2 years ago
Even weirder: if I use a callable protocol, which I thought was supposed to be roughly the same, the errors are backwards from this: error reported for DX with a decorator, but not for DX with a plain call syntax or X with a decorator:
from __future__ import annotations
from typing import Type, Protocol
from dataclasses import dataclass
class P(Protocol):
def __call__(self, x: int, y: str, z: object) -> object:
...
def clsdec(x: P) -> Type:
return x # type: ignore
@clsdec
class X(object):
def __init__(self, x: int, y: str, z: object, f: float) -> None:
pass
@clsdec
@dataclass
class DX(object):
x: int
y: str
z: object
f: float
clsdec(DX)
This is explained by the fact that dataclass fields can (and sometimes do) reference the dataclass itself, as in:
@dataclass
class DC:
child: "DC"
This sort of circularity requires that type checkers first evaluate the type of the dataclass symbol (DC
) prior to synthesizing the __init__
method associated with the dataclass. In this example, the symbol DC
depends on any decorators that are applied, so this creates an unresolvable cycle if a decorator is applied to the dataclass. If mypy were to synthesize the __init__
prior to evaluating the decorator, it would catch the bug in the code above, but it would break many other legitimate use cases. I don't see a way to fix both. Since the application of a class decorator to a dataclass that depends on the synthesized __init__
method is relatively rare (compared to the use of the dataclass symbol in a field type annotation), I think mypy is making the right tradeoff here. FWIW, pyright exhibits the same behavior here.
You can work around the problem by applying the decorator manually:
@dataclass
class _DX:
x: int
y: str
z: object
f: float
DX = clsdec(_DX)
Bug Report
This code sample gives errors on the presence of
f
inX
's constructor but not onDX
'sIt seems like it can't "see" the dataclass's fields somehow, despite otherwise reporting sensible errors at the call site.
In fact,
clsdec(DX)
gives the same error; it's only a problem when it's used in the decorator position.Your Environment
mypy.ini
(and other config files): none