0

At runtime, the following code is perfectly valid:

class Base():
    @abstractmethod
    def get(self, arg: str, **kwargs: Any):
        pass

class Derived(Base):
    def get(self, arg: str, optional_arg: bool = False, **kwargs: Any):
        pass

What's nice about this is that during static analysis, if you happen to know you have a variable of type Derived you get the extra bonus of discovering about the optional named argument optional_arg and it's type.

I thought that Derived is overriding get here in a compatible manner, however, PyRight says that the override is incompatible:

Method "get" overrides class "Base" in an incompatible manner
  Parameter 3 type mismatch: base parameter is type "Any", override parameter is type "bool"
PylancereportIncompatibleMethodOverride

An attempt using the overload decorator fails for me as well so I believe I'm not using it correctly either:

class Derived(Base):
    @overload
    def get(self, path: Path, local_path: Path, is_directory: bool = False):
        ...

    def get(self, path: Path, local_path: Path, **kwargs: Any):
        pass

Giving:

"get" is marked as overload, but additional overloads are missing
PylancereportGeneralTypeIssues

and

Overloaded function implementation is not consistent with signature of overload 1
  Type "(self: Derived, arg: str, **kwargs: Any) -> None" cannot be assigned to type "(self: Derived, arg: str, optional_arg: bool = False) -> None"
    Function accepts too many positional parameters; expected 2 but received 3
PylancereportGeneralTypeIssues

Is there a proper way to type annotate what I'm trying to achieve? i.e. base class abstract method known nothing about the names and types of optional arguments of derived classes, but derived classes can expose specific named arguments and their types.

stav
  • 1,497
  • 2
  • 15
  • 40
  • "i.e. base class abstract method known nothing about the names and types of optional arguments of derived classes," no there isn't, what you are trying to do violates the Liskov substitution principle, for starters, but I believe what is actually going on here is that callables must be contravariant in their argument types. – juanpa.arrivillaga Jul 14 '21 at 14:27
  • How you tried having both `*args` and `**kwargs` as parameters (e.g. `get(self, arg: str, *args, **kwargs)`? That should prevent the LSP violation and may satisfy the checkers. It may not be the call semantics you were hoping for though. By the way, it usually best not to type annotate `args` and `kwargs`. – Kemp Jul 14 '21 at 14:32

0 Answers0