Skip to content
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

Dealing with error: Signature of "func" incompatible with supertype "Base" #9174

Closed
dsuch opened this issue Jul 19, 2020 · 2 comments
Closed

Comments

@dsuch
Copy link

dsuch commented Jul 19, 2020

Hello,

this is a user question.

Software versions are:

  • Python 3.6.9
  • mypy 0.782
  • mypy-0.790+dev.28829fbb684d7535680934b401c05698db5b4d85 (from GitHub)

I have a question related to mypy checks of overridden methods, as documented here.

From ticket #1237 (this comment in particular), I understand that this is to be expected and that there are reasons behind the behaviour below.

Please consider the classes here.

# ######################################################################

class Base:
    def __init__(self):
        self.a = 0
        self.zxc = 'qwerty'

    def func(self, a:int):
        self.a = a

class ChildOne(Base):
    def __init__(self):
        self.b = 0

    def func(self, a:int, b:int):
        self.b = b
        super().func(a)

class ChildTwo(Base):
    def __init__(self):
        self.b = 0
        self.c = 0

    def func(self, a:int, b:int, c:int):
        self.b = b
        self.c = c
        super().func(a)

# ######################################################################

class Base2:
    def do_something(self, *args, **kwargs):
        raise NotImplementedError('Should be overridden by subclasses')

class ChildOne2(Base2):
    def do_something(self, arg1:int, arg2:int):
        pass

class ChildTwo2(Base2):
    def do_something(self, arg1:int, arg2:str, arg3:list, arg4:dict):
        pass

# ######################################################################

What mypy reports:

$ mypy mypy1.py
mypy1.py:14: error: Signature of "func" incompatible with supertype "Base"
mypy1.py:23: error: Signature of "func" incompatible with supertype "Base"
mypy1.py:35: error: Signature of "do_something" incompatible with supertype "Base2"
mypy1.py:39: error: Signature of "do_something" incompatible with supertype "Base2"
Found 4 errors in 1 file (checked 1 source file)
$
  • The first case are sub-classes that have functionality on top of what the base class offers. Having performed what they are supposed to do, they forward the common parameter(s) to a parent class implementing some kind of a common functionality.

    In addition to the common functionality, it is often desirable to treat instances of ChildOne and ChildTwo as though they were instances of Base because I am only interested in the fact that the 'zxc' attribute is available, no matter what particular subclass of Base it comes from.

  • The other situation is considered the same type of an error by mypy in the sense that it is a case of differing signatures, which is why I am providing it here in the same ticket but conceptually it is somewhat different - I find myself writing this kind of code with plugins-like architectures where there is a set of methods to implement but sub-classes know better what their actual arguments and return types are.

In either case, this is not really about the child classes arguments that are wider or narrower in the mypy's type checking sense which I recognise is yet another situation.

I find mypy a great tool and I am very thankful for your effort. However, I am just not sure how to tackle the two scenarios above, how to specify the types here.

In the latter scenario, I think it would suffice if I could just skip type checking in Base2's do_something method, after all it is not doing anything with its input, but using # type: ignore still leads to the same error.

    def do_something(self, *args, **kwargs):
        # type: ignore
        raise NotImplementedError('Should be overridden by subclasses')

In the former case, I would truly like for the right types to be checked. In such a simple case, I could possibly simply rename func to base_func and call that in sub-classes but with a hierarchy that was deeper it could become difficult to know which base method to call thus being able to do simply super().func(a) and have it bubble up wherever the receiver is, that would be most convenient.

Just to confirm it, I realise why mypy behaves in that way by default as this code looks like; I am just looking for a way to configure it to type check it anyway somehow because it seems a reasonable use-case, especially because I read comments in #1237 as confirming my belief to a degree (the idea of @covariant_args).

Thank you for your assistance.

@ilevkivskyi
Copy link
Member

Duplicate of #5876

@NeilGirdhar
Copy link
Contributor

NeilGirdhar commented Jun 17, 2022

I suggest you give the methods different names since they can't be used polymorphically anyway. This will avoid the LSP violation.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants