python / python/mypy

Allow unsafe overrides in sub-subclasses that are compatible with subclass

Open
#12,372 3 comments 6 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

feature topic-inheritance
Dominant language
Python
Stars
20.6k
Forks
3.3k
PR merge metrics
PR metrics pending

Description

Feature

When a subclass is implemented with unsafe method override, error can be suppressed with # type: ignore[override] comment. However, further subclassing requires this comment to be present in subclasses too.

Pitch

Suppose we have the following structure:

class A:
    def foo(self) -> int: pass

class B(A):
    def foo(self) -> str: pass  # type: ignore[override]

class C(B):
    def foo(self) -> str: pass

Currently mypy complains:

example.py:8: error: Return type "str" of "foo" incompatible with return type "int" in supertype "A"

But in reality we do expect that all B subclasses should implement foo with signature () -> str. For instance, it is useful when B is replacement for A with slightly different usage (when we don't want to replicate all internal logic - e.g. if it comes from third-party package). It takes care to override all methods that became incompatible after this change, and now B is consistent. Then we want to add some feature to B via subclassing while preserving its interface. All signatures are preserved, but mypy complains that we are incompatible with old base (A). I think that when unsafe override is explicitly ignored once, mypy should check new implementation against definitions in B, not in A, ignoring the fact that B has unsafe overrides. Actually now the checker reports errors that are explicitly ignored: we already took care to ignore that errors inside B.

Real world example: ModelChoiceField in Django inherits from ChoiceField (yeah, that's bad design decision, but I'm working on stubs and can't change the source). Incompatible part:

class ChoiceField(Field):
    def to_python(self, data: Optional[Any]) -> str: ...

class ModelChoiceField(ChoiceField):
    def to_python(self, data: Optional[Any]) -> Optional[Model]: ...  # type: ignore[override]

When end Django user tries to subclass ModelChoiceField and implements to_python with same signature, user receives mypy error. However, this implementation is perfectly valid as long as direct superclass already defines that incompatible change.

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

The issue names no source files or tests. Start by reproducing the A/B/C example and trace mypy's override checking for subclass methods; done means C's compatible override is accepted based on B while B's explicitly ignored incompatibility remains suppressed.

Written by the indexing model from the issue text.

Assessment

Tech stack
python
Domain
devtools
Issue type
Feature
Difficulty
5/5
Estimated time
Over a week
Activity status
Stale
Clarity
Mostly clear
Newbie friendliness
35/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.