Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

class decorator syntax seems to ignore dataclasses' constructor signatures #12971

Open
glyph opened this issue Jun 11, 2022 · 2 comments
Open
Labels
bug mypy got something wrong topic-dataclasses

Comments

@glyph
Copy link

glyph commented Jun 11, 2022

Bug Report

This code sample gives errors on the presence of f in X's constructor but not on DX's

from __future__ import annotations
from typing import Callable, Type
from dataclasses import dataclass

def clsdec(x: Callable[[int, str, object], object]) -> 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

It 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 version used: mypy 0.961 (compiled: yes)
  • Mypy command-line flags: none
  • Mypy configuration options from mypy.ini (and other config files): none
  • Python version used: Python 3.10.4 (python.org)
  • Operating system and version:
ProductName:	macOS
ProductVersion:	12.4
BuildVersion:	21F79
@glyph glyph added the bug mypy got something wrong label Jun 11, 2022
@glyph
Copy link
Author

glyph commented Jun 11, 2022

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)

@erictraut
Copy link

erictraut commented Aug 13, 2023

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)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug mypy got something wrong topic-dataclasses
Projects
None yet
Development

No branches or pull requests

3 participants