mypy icon indicating copy to clipboard operation
mypy copied to clipboard

Dataclasses: `No overload variant of "asdict" matches argument type "Self" [call-overload]` when deciding decorator conditionally

Open abravalheri opened this issue 1 year ago • 1 comments

Bug Report

Mypy raises a false positive for dataclass.asdict (No overload variant of "asdict" matches argument type "Self" [call-overload]) on the very specific circumstances:

  1. The specific decorator for dataclass is decided based on a condition (e.g. Python version)
  2. The datalcasses.asdict method is called from a method that uses PEP 673's Self Type.

I also noted a similar problem occurs for dataclasses.replace: Value of type variable "_DataclassT" of "replace" cannot be "Self" [type-var].

To Reproduce

import sys
import dataclasses
import json
from typing import Self

if sys.version_info >= (3, 10):
    dataclass = dataclasses.dataclass(kw_only=True)
else:
    dataclass = dataclasses.dataclass


@dataclass
class HelloWorld:
    a: int = 1
    b: int = 2
    c: int = 3

    def double(self) -> Self:
        fields = dataclasses.asdict(self)
        return self.__class__(**{k: v*2 for k, v in fields.items()})

    def json_overwrite(self, text: str) -> Self:
        new = json.loads(text)
        return dataclasses.replace(self, **new)

x = HelloWorld().double()
y = HelloWorld().json_overwrite('{"c": 42}')
assert x.b == 4
assert y.c == 42
print(f"{x=}")
print(f"{y=}")

Gist URL: https://gist.github.com/mypy-play/70b801fbe15391f0750be8f6c403fdf4 Playground URL: https://mypy-play.net/?mypy=latest&python=3.12&flags=show-error-context%2Cstrict&gist=70b801fbe15391f0750be8f6c403fdf4

Expected Behavior

There should be no false negative. The program works fine in runtime:

$ python3.12 main.py
x=HelloWorld(a=2, b=4, c=6)
y=HelloWorld(a=1, b=2, c=42)

Actual Behavior

main.py: note: In member "double" of class "HelloWorld":
main.py:19: error: No overload variant of "asdict" matches argument type "Self"  [call-overload]
main.py:19: note: Possible overload variants:
main.py:19: note:     def asdict(obj: DataclassInstance) -> dict[str, Any]
main.py:19: note:     def [_T] asdict(obj: DataclassInstance, *, dict_factory: Callable[[list[tuple[str, Any]]], _T]) -> _T
main.py: note: In member "json_overwrite" of class "HelloWorld":
main.py:24: error: Value of type variable "_DataclassT" of "replace" cannot be "Self"  [type-var]
Found 2 errors in 1 file (checked 1 source file)

Your Environment

  • Mypy version used: 1.10.0
  • Mypy command-line flags: --show-error-context, --strict
  • Mypy configuration options from mypy.ini (and other config files):
  • Python version used: 3.12

abravalheri avatar May 14 '24 12:05 abravalheri

I too have found this when working with pydantic dataclasses

OWissett avatar Oct 07 '24 14:10 OWissett

just discovered this when working with pydantic dataclasses

jbcpollak avatar Dec 06 '24 18:12 jbcpollak

mypy simply does not support aliased dataclass at all.

from dataclasses import dataclass

dataclass_alias = dataclass

@dataclass
class Ok:
    a: int = 1
    
@dataclass_alias
class Bad:
    a: int = 1

reveal_type(Ok.__init__)  # N: Revealed type is "def (self: __main__.Ok, a: builtins.int =)"
reveal_type(Bad.__init__)  # N: Revealed type is "def (self: builtins.object)"

sterliakov avatar Aug 31 '25 23:08 sterliakov