mypy
mypy copied to clipboard
@overload interacts badly with @property even with the `type: ignore` workaround
Assuming a issue.py
module:
$ mypy issue.py
issue.py:86: error: Invalid self argument "MyContainerStr" to attribute function "bounds" with type "Callable[[MyContainerBase[int]], BoundsInt]"
issue.py:86: error: Argument 1 to "check_to_str_bounds" has incompatible type "BoundsInt"; expected "BoundsStr"
Found 2 errors in 1 file (checked 1 source file)
This occurs when I attempt to overload a property. It however does not occurs for the same overload on a plain method.
issue.py
is:
from typing import (ClassVar, Generic, NamedTuple, Optional, Type, TypeVar,
Union, overload)
BoundsInt = NamedTuple("BoundsInt", [
("begin", int), ("end", int)])
BoundsStr = NamedTuple("BoundsStr", [
("begin", str), ("end", str)])
_T = TypeVar("_T", int, str)
class MyContainerBase(Generic[_T]):
BOUND_TYPE = None # type: ClassVar[Optional[Type[Union[BoundsInt, BoundsStr]]]] # noqa
def __init__(self, begin: _T, end: _T) -> None:
assert self.BOUND_TYPE is not None
if isinstance(begin, int) and isinstance(end, int):
self._bounds = BoundsInt(begin, end) # type: Union[BoundsInt, BoundsStr] # noqa
assert isinstance(begin, str) and isinstance(end, str)
self._bounds = BoundsStr(begin, end)
@overload # type: ignore
@property
def bounds(
self: 'MyContainerBase[int]'
) -> BoundsInt:
...
@overload # type: ignore
@property
def bounds(
self: 'MyContainerBase[str]'
) -> BoundsStr:
...
@property
def bounds(self) -> Union[BoundsInt, BoundsStr]:
"""Return the recurrence period's bounds."""
return self._bounds
@overload
def get_bounds(
self: 'MyContainerBase[int]'
) -> BoundsInt:
...
@overload
def get_bounds(
self: 'MyContainerBase[str]'
) -> BoundsStr:
...
def get_bounds(self) -> Union[BoundsInt, BoundsStr]:
"""Return the recurrence period's bounds."""
return self._bounds
class MyContainerInt(MyContainerBase[int]):
BOUND_TYPE = BoundsInt
class MyContainerStr(MyContainerBase[str]):
BOUND_TYPE = BoundsStr
def test() -> None:
def check_to_int_bounds(bounds: BoundsInt) -> None:
pass
def check_to_str_bounds(bounds: BoundsStr) -> None:
pass
# Typechecks as expected:
check_to_int_bounds(MyContainerInt(1, 1).bounds)
# As expected, if uncommented, fails with:
# Argument 1 to "check_to_str_bounds" has incompatible type "BoundsInt";
# expected "BoundsStr"
# check_to_str_bounds(MyContainerInt(1, 1).bounds)
# Unexpectedly, if uncommented both of those fail with:
# Invalid self argument "MyContainerStr" to attribute
# function "bounds" with type "Callable[[MyContainerBase[int]],
# BoundsInt]"
check_to_str_bounds(MyContainerStr("a", "b").bounds)
# check_to_int_bounds(MyContainerStr(1.0, 1.0).bounds)
# We do not have the above issue with the `get_bounds` method:
# Typechecks as expected:
check_to_int_bounds(MyContainerInt(1, 1).get_bounds())
# As expected, if uncommented, fails.
# check_to_str_bounds(MyContainerInt(1, 1).get_bounds())
# Typechecks as expected:
check_to_str_bounds(MyContainerStr("a", "b").get_bounds())
# As expected, if uncommented, fails.
# check_to_int_bounds(MyContainerStr(1.0, 1.0).get_bounds())
Your Environment
- Mypy version used: 0.790
- Mypy command-line flags: none
- Mypy configuration options from
mypy.ini
(and other config files):
[mypy]
warn_unused_ignores = True
mypy_path = distropmc
disallow_any_generics = True
- Python version used: 3.5.9
- Operating system and version:
$ uname -a
Linux rgauthier-precision 5.4.79 #1-NixOS SMP Sun Nov 22 09:14:12 UTC 2020 x86_64 GNU/Linux
Slightly shorter repro:
from __future__ import annotations
from typing import overload, Generic, TypeVar
T = TypeVar('T')
class C(Generic[T]):
@overload # type:ignore[misc]
@property
def f(self: C[int]) -> int: raise NotImplementedError
@overload # type:ignore[misc]
@property
def f(self: C[str]) -> str: raise NotImplementedError
@property # type:ignore[misc]
def f(self): raise NotImplementedError
c: C[str] = C()
_ = c.f # E: Invalid self argument "C[str]" to attribute function "f" with type "Callable[[C[int]], int]"
Playground: https://mypy-play.net/?mypy=latest&python=3.11&gist=15958eb7a2388321bc5561aeebffefd7
Here's a workaround as suggested here: https://github.com/microsoft/pyright/issues/3071#issuecomment-1043978070
from __future__ import annotations
from typing import overload, Generic, TypeVar, Any
T = TypeVar("T")
class _fDescriptor:
@overload
def __get__(self, instance: C[int], owner: Any) -> int:
...
@overload
def __get__(self, instance: C[str], owner: Any) -> str:
...
def __get__(self, instance: object, owner: Any) -> int | str:
raise NotImplementedError
class C(Generic[T]):
f: _fDescriptor
c: C[str] = C()
_ = c.f
reveal_type(c.f)
ci: C[int] = C()
reveal_type(ci.f)
This will reveal the types correctly.
Bump! I've just encountered the same issue. I'm not really into creating a custom descriptor.