-
-
Notifications
You must be signed in to change notification settings - Fork 2.9k
MethodType incorrectly rejected as subtype of Callable #8869
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
Comments
Methods always have at least one arg (self) - would that justify mypy concluding that a nullary callable would never unify with MethodType? |
In Python 3 (not sure about 2), an instance of
The instance that a method is bound to is stored in the
As you can see, it's correct to call a bound method that only takes |
That makes sense. In the interest of narrowing things down, are you able to clear the error if you tweak the callable type? Either specifically to take a C or Any or some other option? |
Changing the argument types of the |
Mypy has no special understanding of A potential fix would be to special case |
class MethodType:
__func__: _StaticFunctionType
__self__: object
__name__: str
__qualname__: str
def __init__(self, func: Callable[..., Any], obj: object) -> None: ...
def __call__(self, *args: Any, **kwargs: Any) -> Any: ...
|
Here is a test case that doesn't use from typing import Any, Callable
class ProtoAny:
def __call__(self) -> Any: ...
class ProtoNone:
def __call__(self) -> None: ...
def describe(func: Callable[[], None]) -> str:
if isinstance(func, ProtoAny):
return 'ProtoAny'
elif isinstance(func, ProtoNone):
return 'ProtoNone'
else:
return 'other callable'
class C(ProtoAny):
def __call__(self) -> None: ...
def f():
pass
print(describe(ProtoAny()))
print(describe(C()))
print(describe(ProtoNone()))
print(describe(f)) mypy reports:
Which is the |
A test case without from typing import Callable
class A:
pass
class B(A):
pass
class ProtoA:
def __call__(self) -> A: ...
class ProtoB:
def __call__(self) -> B: ...
def describe(func: Callable[[], A]) -> str:
if isinstance(func, ProtoA):
return 'ProtoA'
elif isinstance(func, ProtoB):
return 'ProtoB'
else:
return 'other callable'
class C(ProtoA):
def __call__(self) -> B: ...
class D(ProtoB):
def __call__(self) -> A: ...
def f():
pass
print(describe(C()))
print(describe(D()))
print(describe(f)) mypy reports:
Line 27 is the body of |
One more case: from types import MethodType
class C:
def m(self):
pass
def f(meth: MethodType):
meth.__self__
f(C().m) The output: |
This is now also reproducible with from typing import Any, Callable, reveal_type
from typing_extensions import TypeIs
class FunctionType:
def __call__(self, *args: Any, **kwargs: Any) -> Any: ...
def isfunction(x: object) -> TypeIs[FunctionType]:
raise NotImplementedError
def decorate(wrapped: Callable[..., object]) -> Any:
if isfunction(wrapped):
reveal_type(wrapped)
if isinstance(wrapped, FunctionType):
reveal_type(wrapped)
|
When I run mypy on the following code:
It outputs:
This statement is
return 'bound method'
.If you run the test case, you will see "bound method" being printed, so the code is reachable in practice.
For some reason the problem disappears if the annotation
func: Callable[[], None]
is changed to justfunc: Callable
.I'm using mypy 0.770 on Python 3.8.2, with the
--warn-unreachable
option.The text was updated successfully, but these errors were encountered: