Mypy: Class with function fields incorrectly thinks first argument is self

Created on 15 Aug 2018  路  8Comments  路  Source: python/mypy

This is a bug report.

Minimal repro

from dataclasses import dataclass
from typing import Callable

@dataclass
class Repro:
    fn: Callable[[int], str]

r = Repro(fn=repr)
assert "1" == r.fn(1)

Works correctly when dataclass is not used, i.e. when Repro is

class Repro:
    def __init__(self, fn: Callable[[int], str]) -> None:
        self.fn = fn

Actual behavior

repro.py:9: error: Invalid self argument "Repro" to attribute function "fn" with type "Callable[[int], str]"
repro.py:9: error: Too many arguments

When running with Python, works correctly. Doesn't throw any exceptions.

Expected behavior

Successful type check

Environment

Versions

Tested with two versions, 0.620 and current version from GitHub.

  • Python 3.7.0
  • mypy 0.620
  • mypy 0.630+dev-a2ffde90635baf05e264f8d899f5705c52b27c2a

Mypy config

[mypy]
warn_redundant_casts = True
warn_unused_ignores = True
incremental = True

follow_imports = normal
ignore_missing_imports = True
disallow_untyped_calls = True
disallow_untyped_defs = True
check_untyped_defs = True
warn_return_any = True
warn_no_return = True
no_implicit_optional = True
strict_optional = True
bug false-positive priority-1-normal topic-plugins

Most helpful comment

I've found that another workaround to this is to use a callback protocol instead of Callable

All 8 comments

This is not something specific to dataclasses. There is a simpler repro that gives same errors:

class Repro:
    fn: Callable[[int], str]

r = Repro()
r.fn(1)

The problem is the same as in https://github.com/python/mypy/issues/708. I would however _not_ close this as a duplicate, because this issue highlights another aspect of the problem.

I just came across the same issue. I guess a lot of people are going to face this issue very soon, as it's very easy to reproduce it using dataclasses.

I guess this should be not hard to fix, mypy cares whether a callable was assigned in class (to match what Python does), so we should just tweak the Vars in plugin to pretend they were assigned on self. But we still need a volunteer who will fix this.

If someone needs a workaround, this works:

from dataclasses import dataclass
from typing import *

T = TypeVar("T")


@dataclass
class Box(Generic[T]):
    inner: T

    @property
    def unboxed(self) -> T:
        return self.inner


@dataclass
class Repro:
    fn: Box[Callable[[int], str]]


Repro(fn=Box(repr)).fn.unboxed(1)

I've found that another workaround to this is to use a callback protocol instead of Callable

When using NamedTuple instead of dataclass this problem does not arise.

I volunteer to fix this and any guidances will be greatly helpful @ilevkivskyi

@TH3CHARLie Fixing this in general may be tricky, but fixing just the dataclass case, should be easy. In any case, please go ahead!

Was this page helpful?
0 / 5 - 0 ratings