at runtime dataclass special-cases FunctionType, but at type-time mypy special-cases Callable, leading to mismatches
Nobody has claimed this yet.
- Dominant language
- Python
- Stars
- 20.6k
- Forks
- 3.3k
- PR merge metrics
- PR metrics pending
Description
Bug Report
If you declare an attribute on a dataclass to be Callable, Mypy assumes that its __get__ will not be called, even on a default attribute. But this is only true if the default is a FunctionType, not some other type of callable.
Similarly, the inverse is true: an abstract type (such as a callable Protocol) will tell mypy that __get__ will be called, even though if the concrete default value is a FunctionType, it won't be.
Specifically, the following program contains 2 type errors, but MyPy does not think so:
To Reproduce
from __future__ import annotations
from typing import Callable, Protocol, ParamSpec, TypeVar, Generic
from dataclasses import dataclass
def f() -> None:
print("hello world")
def f2(self: object) -> None:
print("bound self", self)
P = ParamSpec("P")
T = TypeVar("T")
R = TypeVar("R", covariant=True)
T_con = TypeVar("T_con", contravariant=True)
class BindableMethod(Protocol[T_con, R]):
def __get__(self, instance: T_con, owner: None | type[object]) -> Callable[[], R]:
...
def __call__(me, self: T_con) -> R:
...
class UnboundNonFunction(Generic[P, R]):
def __call__(self) -> int:
print("unbound")
return 3
def __get__(self, instance: object, owner: type | None) -> BoundNonFunction:
print("binding", instance, owner)
return BoundNonFunction(instance)
@dataclass(frozen=True)
class BoundNonFunction:
instance: object
def __call__(self) -> str:
print("bound", self.instance)
return "wat"
@dataclass
class FuncHolder():
func: Callable[[], None] = f
func2: BindableMethod[FuncHolder, None] = f2
func3: Callable[[], int] = UnboundNonFunction()
FuncHolder().func()
try:
FuncHolder().func2()
except Exception as e:
print("whoops", str(e))
try:
print(FuncHolder().func3() + 4)
except Exception as e:
print("whoops 2", str(e))
https://mypy-play.net/?mypy=latest&python=3.11&gist=1ac4e7c090774489207dfbc737de2d61
Expected Behavior
I would expect runtime and type-time behavior to be consistent here. Specifically I just don't expect __get__ to be called on things with a Callable type.
Actual Behavior
Mypy succeeds, rather than reporting the type errors.
Your Environment
- Mypy version used: mypy 1.1.1 (compiled: yes)
- Mypy command-line flags: none
- Mypy configuration options from
mypy.ini(and other config files): none - Python version used: 3.11.2 (python.org, macOS, aarch64)
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start with the self-contained Python reproduction in the issue and run it through mypy-play using the linked configuration. Compare the dataclass descriptor behavior at runtime with mypy's treatment of Callable and protocol-typed defaults. Done means mypy reports the two type errors while preserving the valid FunctionType case.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- python
- Domain
- compilers, devtools
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Stale
- Clarity
- Mostly clear
- Newbie friendliness
- 25/100