Parameter 2 type mismatch: base parameter is type "Any", override parameter is type "Any" with interface methods
Closed this issue · 4 comments
This is a somewhat niche problem, but I have come across it in practice twice already. It seems like the issue is caused by unimplemented methods in the base class which have type annotations being implemented in sub-classes. The resulting error looks like this:
Method "foo" overrides class "A" in an incompatible manner
Parameter 2 type mismatch: base parameter is type "Any", override parameter is type "Any"
Adding the "Any" annotation doesn't seem to help either.
Sample code which causes the issue
from typing import Any, Callable
class A(object):
def _foo_unimplemented(self, input: Any) -> None:
raise NotImplementedError
foo: Callable[..., Any] = _foo_unimplemented
class B(A):
def foo(self, obj: Any):
pass
# Result: Method "foo" overrides class "A" in an incompatible manner. Parameter 2 type mismatch: base parameter is type "Any", override parameter is type "Any"
# Real world examples
import tornado.web
class RunsHandler(tornado.web.RequestHandler):
def initialize(self, con: Any):
self._con = con
# Result: Method "initialize" overrides class "RequestHandler" in an incompatible manner. Parameter 2 type mismatch: base parameter is type "Any", override parameter is type "Any"
import torch.nn
class Model(torch.nn.Module):
def forward(self, data: Any):
return data
# Result: Method "forward" overrides class "Module" in an incompatible manner. Parameter 2 type mismatch: base parameter is type "Any", override parameter is type "Any"
class Model(torch.nn.Module):
def forward(self, data):
return data
# Result: Method "forward" overrides class "Module" in an incompatible manner. Parameter 2 type mismatch: base parameter is type "Any", override parameter is type "Unknown"
The current behavior is correct, or at least as intended. The intention behind the "reportIncompatibleMethodOverride" check is to detect cases where a base class is making assumptions that a subclass is violating. In your example above, A.foo
is annotated as a callable type that accepts an arbitrary number of parameters (both positional and keyword) and returns any type. That implies that class A
may call this function with any number of parameters. But B.foo
is attempting to override it with a method that accepts only one positional parameter (obj
) and no keyword parameters. If A
calls it with any other combination of parameters, the program will crash at runtime. So this is exactly the sort of inconsistency that "reportIncompatibleMethodOverride" was designed to detect.
I think you have a few options here:
- Change the signature of
foo
in the base class so it reflects the fact that it accepts only one positional parameter. - Change the signature of
foo
in the subclass so it accepts*args
and**kwargs
. - If you don't want this check to be performed, you can disable it globally for your project. Or you can disable it for a specific file by using a comment (
# pyright: reportIncompatibleMethodOverride=false
).
Hello. I also encountered this issue and tried to address it, but option 2 is not working. What should I do?
from typing import Any, Callable
class A(object):
def _foo_unimplemented(self, input: Any) -> None:
raise NotImplementedError
foo: Callable[..., Any] = _foo_unimplemented
class B(A):
def foo(self):
pass
# Result:
# Method "foo" overrides class "A" in an incompatible manner
# Positional parameter count mismatch; base method has 2, but override has 1
# Parameter "args" is missing in override (reportIncompatibleMethodOverride)
class C(A):
def foo(self, *args: Any):
pass
# Result:
# Method "foo" overrides class "A" in an incompatible manner
# Positional parameter count mismatch; base method has 2, but override has 2 (reportIncompatibleMethodOverride)
class D(A):
def foo(self, *args: Any, **kwargs: Any):
pass
# Result:
# Method "foo" overrides class "A" in an incompatible manner
# Positional parameter count mismatch; base method has 2, but override has 3 (reportIncompatibleMethodOverride)
Yeah, I would expect option 2 to work in this case. It doesn't because of the self
parameter that's present in the override but is not in the base. However, the ...
should be treated specially in this case.
I've made this change, and it will be included in the next release.
This is addressed in pyright 1.1.306, which I just published. It will also be included in a future release of pylance.