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

Check override compatibility for dynamically typed methods #17274

Closed
wants to merge 3 commits into from

Conversation

stroxler
Copy link
Contributor

This fixes #9618 in a fairly aggressive way, by turning on all override checks for all methods. This means that with this change, MyPy will complain in cases where just inspecting type signatures involving Any is enough to establish that an override is a type error:

The tests illustrate this, I added a test case to check-functions.test (is this a good place for it?) for the @final check specifically but also updated a few existing tests of dynamic logic.

The resulting code change is very simple (since all override checks happen in the same place), and I think conceptually it is right to enable all these checks because the contract is that MyPy won't generally type check bodies of functions, but the function signatures are actually part of the class type definition which is (in other cases) sanity-checked even when there are no annotations.

If we think that the arity checks are too big a change, I can work on a refactor that would let us validate only @final but not other properties. I think we should give it a shot and see if mypy_primer indicates a big blast radius.

This fixes python#9618 in a fairly aggressive way, by turning on all override
checks for all methods. This means that with this change, MyPy will
complain in cases where just inspecting type signatures involving `Any`
is enough to establish that an override is a type error:
- This includes the case of overriding `@final` as listed in python#9618
- But it also includes arity mismatches, for example when an untyped
  subclass method adds a required attribute not in the parent class
  method.

The tests illustrate this, I added a test case to `check-functions.test`
(is this a good place for it?) for the `@final` check specifically but
also updated a few existing tests of dynamic logic.

The resulting code change is very simple (since all override checks
happen in the same place), and I think conceptually it is right to
enable all these checks because the contract is that MyPy won't
generally type check *bodies* of functions, but the function signatures
are actually part of the *class* type definition which is (in other
cases) sanity-checked even when there are no annotations.

If we think that the arity checks are too big a change, I can work
on a refactor that would let us validate *only* `@final` but not other
properties. Maybe waiting for `mypy_primer` output is the way to go?
@stroxler stroxler force-pushed the stroxler-fixes-9618-plus branch from 323cbd7 to a463090 Compare May 21, 2024 18:03

This comment has been minimized.

@stroxler
Copy link
Contributor Author

The mypy primer results suggest that there are a lot of packages relying on the ability to unsoundly change arity of function signatures. I'm not completely convinced this is a good reason a type checker should in principle allow incompatible overrides with untyped definitions, since these library authors are going to be stuck anyway if they ever try to add type annotations, but it does look like a big enough backward incompatibility that we might not want it in mypy by default.

I'll put up an alternative PR where we only enable override checks when validating untyped defs, and see what the mypy_primer results show in that case.

Copy link
Contributor

Diff from mypy_primer, showing the effect of this PR on open source code:

comtypes (https://github.com/enthought/comtypes)
+ comtypes/test/__init__.py:159: error: Signature of "run" incompatible with supertype "TextTestRunner"  [override]
+ comtypes/test/__init__.py:159: note:      Superclass:
+ comtypes/test/__init__.py:159: note:          def run(self, test: TestSuite | TestCase) -> TestResult
+ comtypes/test/__init__.py:159: note:      Subclass:
+ comtypes/test/__init__.py:159: note:          def run(self, test: Any, skipped: Any) -> Any
+ comtypes/server/localserver.py:37: error: Signature of "IUnknown_AddRef" incompatible with supertype "COMObject"  [override]
+ comtypes/server/localserver.py:37: note:      Superclass:
+ comtypes/server/localserver.py:37: note:          def IUnknown_AddRef(self, this: Any, Any = ..., /, _debug: Any = ...) -> Any
+ comtypes/server/localserver.py:37: note:      Subclass:
+ comtypes/server/localserver.py:37: note:          def IUnknown_AddRef(self, this: Any) -> Any
+ comtypes/server/localserver.py:40: error: Signature of "IUnknown_Release" incompatible with supertype "COMObject"  [override]
+ comtypes/server/localserver.py:40: note:      Superclass:
+ comtypes/server/localserver.py:40: note:          def IUnknown_Release(self, this: Any, Any = ..., /, _debug: Any = ...) -> Any
+ comtypes/server/localserver.py:40: note:      Subclass:
+ comtypes/server/localserver.py:40: note:          def IUnknown_Release(self, this: Any) -> Any

sockeye (https://github.com/awslabs/sockeye)
+ sockeye/lexicon.py:260: error: Signature of "get_blocked_trg_ids" incompatible with supertype "RestrictLexicon"  [override]
+ sockeye/lexicon.py:260: note:      Superclass:
+ sockeye/lexicon.py:260: note:          def get_blocked_trg_ids(self, src_ids: Any | None = ...) -> Any
+ sockeye/lexicon.py:260: note:      Subclass:
+ sockeye/lexicon.py:260: note:          def get_blocked_trg_ids(self, src_ids: Any) -> Any
+ sockeye/lexicon.py:307: error: Signature of "get_allowed_trg_ids" incompatible with supertype "RestrictLexicon"  [override]
+ sockeye/lexicon.py:307: note:      Superclass:
+ sockeye/lexicon.py:307: note:          def get_allowed_trg_ids(self, src_ids: Any | None = ...) -> Any
+ sockeye/lexicon.py:307: note:      Subclass:
+ sockeye/lexicon.py:307: note:          def get_allowed_trg_ids(self, src_ids: Any) -> Any

pandas (https://github.com/pandas-dev/pandas)
+ pandas/core/arrays/boolean.py:359: error: Signature of "_logical_method" incompatible with supertype "BaseMaskedArray"  [override]
+ pandas/core/arrays/boolean.py:359: note:      Superclass:
+ pandas/core/arrays/boolean.py:359: note:          def _arith_method(self: BaseMaskedArray, other: Any, op: Any) -> Any
+ pandas/core/arrays/boolean.py:359: note:      Subclass:
+ pandas/core/arrays/boolean.py:359: note:          def _logical_method(self, other: Any, op: Any) -> Any
+ pandas/core/arrays/boolean.py:359: note:      Superclass:
+ pandas/core/arrays/boolean.py:359: note:          def _arith_method(self: BaseMaskedArray, other: Any, op: Any) -> Any
+ pandas/core/arrays/boolean.py:359: note:      Subclass:
+ pandas/core/arrays/boolean.py:359: note:          def _logical_method(self, other: Any, op: Any) -> Any
+ pandas/core/indexes/interval.py:993: error: Signature of "_intersection" incompatible with supertype "Index"  [override]
+ pandas/core/indexes/interval.py:993: note:      Superclass:
+ pandas/core/indexes/interval.py:993: note:          def _intersection(self, other: Index, sort: bool = ...) -> Any
+ pandas/core/indexes/interval.py:993: note:      Subclass:
+ pandas/core/indexes/interval.py:993: note:          def _intersection(self, other: Any, sort: Any) -> Any
+ pandas/core/resample.py:1624: error: Signature of "_downsample" incompatible with supertype "Resampler"  [override]
+ pandas/core/resample.py:1624: note:      Superclass:
+ pandas/core/resample.py:1624: note:          def _downsample(self, f: Any, **kwargs: Any) -> Any
+ pandas/core/resample.py:1624: note:      Subclass:
+ pandas/core/resample.py:1624: note:          def _downsample(self, how: Any, **kwargs: Any) -> Any
+ pandas/core/resample.py:1783: error: Signature of "_downsample" incompatible with supertype "Resampler"  [override]
+ pandas/core/resample.py:1783: note:      Superclass:
+ pandas/core/resample.py:1783: note:          def _downsample(self, f: Any, **kwargs: Any) -> Any
+ pandas/core/resample.py:1783: note:      Subclass:
+ pandas/core/resample.py:1783: note:          def _downsample(self, how: Any, **kwargs: Any) -> Any
+ pandas/tests/window/test_win_type.py:159: error: Signature of "get_window_bounds" incompatible with supertype "BaseIndexer"  [override]
+ pandas/tests/window/test_win_type.py:159: note:      Superclass:
+ pandas/tests/window/test_win_type.py:159: note:          def get_window_bounds(self, num_values: int = ..., min_periods: int | None = ..., center: bool | None = ..., closed: str | None = ..., step: int | None = ...) -> tuple[ndarray[Any, Any], ndarray[Any, Any]]
+ pandas/tests/window/test_win_type.py:159: note:      Subclass:
+ pandas/tests/window/test_win_type.py:159: note:          def get_window_bounds(self, num_values: Any, min_periods: Any, center: Any, closed: Any, step: Any) -> Any
+ pandas/tests/window/test_rolling.py:1293: error: Signature of "get_window_bounds" incompatible with supertype "BaseIndexer"  [override]
+ pandas/tests/window/test_rolling.py:1293: note:      Superclass:
+ pandas/tests/window/test_rolling.py:1293: note:          def get_window_bounds(self, num_values: int = ..., min_periods: int | None = ..., center: bool | None = ..., closed: str | None = ..., step: int | None = ...) -> tuple[ndarray[Any, Any], ndarray[Any, Any]]
+ pandas/tests/window/test_rolling.py:1293: note:      Subclass:
+ pandas/tests/window/test_rolling.py:1293: note:          def get_window_bounds(self, num_values: Any, min_periods: Any, center: Any, closed: Any, step: Any) -> Any
+ pandas/tests/window/test_base_indexer.py:27: error: Signature of "get_window_bounds" incompatible with supertype "BaseIndexer"  [override]
+ pandas/tests/window/test_base_indexer.py:27: note:      Superclass:
+ pandas/tests/window/test_base_indexer.py:27: note:          def get_window_bounds(self, num_values: int = ..., min_periods: int | None = ..., center: bool | None = ..., closed: str | None = ..., step: int | None = ...) -> tuple[ndarray[Any, Any], ndarray[Any, Any]]
+ pandas/tests/window/test_base_indexer.py:27: note:      Subclass:
+ pandas/tests/window/test_base_indexer.py:27: note:          def get_window_bounds(self) -> Any
+ pandas/tests/window/test_base_indexer.py:49: error: Signature of "get_window_bounds" incompatible with supertype "BaseIndexer"  [override]
+ pandas/tests/window/test_base_indexer.py:49: note:      Superclass:
+ pandas/tests/window/test_base_indexer.py:49: note:          def get_window_bounds(self, num_values: int = ..., min_periods: int | None = ..., center: bool | None = ..., closed: str | None = ..., step: int | None = ...) -> tuple[ndarray[Any, Any], ndarray[Any, Any]]
+ pandas/tests/window/test_base_indexer.py:49: note:      Subclass:
+ pandas/tests/window/test_base_indexer.py:49: note:          def get_window_bounds(self, num_values: Any, min_periods: Any, center: Any, closed: Any, step: Any) -> Any
+ pandas/tests/window/test_base_indexer.py:71: error: Signature of "get_window_bounds" incompatible with supertype "BaseIndexer"  [override]
+ pandas/tests/window/test_base_indexer.py:71: note:      Superclass:
+ pandas/tests/window/test_base_indexer.py:71: note:          def get_window_bounds(self, num_values: int = ..., min_periods: int | None = ..., center: bool | None = ..., closed: str | None = ..., step: int | None = ...) -> tuple[ndarray[Any, Any], ndarray[Any, Any]]
+ pandas/tests/window/test_base_indexer.py:71: note:      Subclass:
+ pandas/tests/window/test_base_indexer.py:71: note:          def get_window_bounds(self, num_values: Any, min_periods: Any, center: Any, closed: Any, step: Any) -> Any
+ pandas/tests/window/test_base_indexer.py:300: error: Signature of "get_window_bounds" incompatible with supertype "BaseIndexer"  [override]
+ pandas/tests/window/test_base_indexer.py:300: note:      Superclass:
+ pandas/tests/window/test_base_indexer.py:300: note:          def get_window_bounds(self, num_values: int = ..., min_periods: int | None = ..., center: bool | None = ..., closed: str | None = ..., step: int | None = ...) -> tuple[ndarray[Any, Any], ndarray[Any, Any]]
+ pandas/tests/window/test_base_indexer.py:300: note:      Subclass:
+ pandas/tests/window/test_base_indexer.py:300: note:          def get_window_bounds(self, num_values: Any, min_periods: Any, center: Any, closed: Any, step: Any) -> Any
+ pandas/tests/window/test_base_indexer.py:481: error: Signature of "get_window_bounds" incompatible with supertype "BaseIndexer"  [override]
+ pandas/tests/window/test_base_indexer.py:481: note:      Superclass:
+ pandas/tests/window/test_base_indexer.py:481: note:          def get_window_bounds(self, num_values: int = ..., min_periods: int | None = ..., center: bool | None = ..., closed: str | None = ..., step: int | None = ...) -> tuple[ndarray[Any, Any], ndarray[Any, Any]]
+ pandas/tests/window/test_base_indexer.py:481: note:      Subclass:
+ pandas/tests/window/test_base_indexer.py:481: note:          def get_window_bounds(self, num_values: Any, min_periods: Any, center: Any, closed: Any, step: Any) -> Any
+ pandas/tests/window/test_base_indexer.py:503: error: Signature of "get_window_bounds" incompatible with supertype "BaseIndexer"  [override]
+ pandas/tests/window/test_base_indexer.py:503: note:      Superclass:
+ pandas/tests/window/test_base_indexer.py:503: note:          def get_window_bounds(self, num_values: int = ..., min_periods: int | None = ..., center: bool | None = ..., closed: str | None = ..., step: int | None = ...) -> tuple[ndarray[Any, Any], ndarray[Any, Any]]
+ pandas/tests/window/test_base_indexer.py:503: note:      Subclass:
+ pandas/tests/window/test_base_indexer.py:503: note:          def get_window_bounds(self, num_values: Any, min_periods: Any, center: Any, closed: Any, step: Any) -> Any
+ pandas/tests/extension/json/array.py:241: error: Signature of "_pad_or_backfill" incompatible with supertype "ExtensionArray"  [override]
+ pandas/tests/extension/json/array.py:241: note:      Superclass:
+ pandas/tests/extension/json/array.py:241: note:          def _pad_or_backfill(self, *, method: Literal['backfill', 'bfill', 'ffill', 'pad'], limit: int | None = ..., limit_area: Literal['inside', 'outside'] | None = ..., copy: bool = ...) -> JSONArray
+ pandas/tests/extension/json/array.py:241: note:      Subclass:
+ pandas/tests/extension/json/array.py:241: note:          def _pad_or_backfill(self, *, method: Any, limit: Any = ..., copy: Any = ...) -> Any
+ pandas/tests/extension/decimal/array.py:292: error: Signature of "fillna" incompatible with supertype "ExtensionArray"  [override]
+ pandas/tests/extension/decimal/array.py:292: note:      Superclass:
+ pandas/tests/extension/decimal/array.py:292: note:          def fillna(self, value: object, limit: int | None = ..., copy: bool = ...) -> DecimalArray
+ pandas/tests/extension/decimal/array.py:292: note:      Subclass:
+ pandas/tests/extension/decimal/array.py:292: note:          def fillna(self, value: Any = ..., limit: Any = ...) -> Any
+ pandas/tests/extension/test_string.py:107: error: Signature of "test_view" incompatible with supertype "BaseInterfaceTests"  [override]
+ pandas/tests/extension/test_string.py:107: note:      Superclass:
+ pandas/tests/extension/test_string.py:107: note:          def test_view(self, data: Any) -> Any
+ pandas/tests/extension/test_string.py:107: note:      Subclass:
+ pandas/tests/extension/test_string.py:107: note:          def test_view(self, data: Any, request: Any, arrow_string_storage: Any) -> Any
+ pandas/tests/extension/test_string.py:116: error: Signature of "test_transpose" incompatible with supertype "BaseReshapingTests"  [override]
+ pandas/tests/extension/test_string.py:116: note:      Superclass:
+ pandas/tests/extension/test_string.py:116: note:          def test_transpose(self, data: Any) -> Any
+ pandas/tests/extension/test_string.py:116: note:      Subclass:

... (truncated 146 lines) ...

sympy (https://github.com/sympy/sympy)
+ sympy/core/numbers.py:349: error: Signature of "invert" incompatible with supertype "Expr"  [override]
+ sympy/core/numbers.py:349: note:      Superclass:
+ sympy/core/numbers.py:349: note:          def invert(self, g: Any, *gens: Any, **args: Any) -> Any
+ sympy/core/numbers.py:349: note:      Subclass:
+ sympy/core/numbers.py:349: note:          def invert(self, other: Any, *gens: Any, **args: Any) -> Any
+ sympy/core/numbers.py:3020: error: Signature of "evalf" incompatible with supertype "EvalfMixin"  [override]
+ sympy/core/numbers.py:3020: note:      Superclass:
+ sympy/core/numbers.py:3020: note:          def evalf(self, n: Any = ..., subs: Any = ..., maxn: Any = ..., chop: Any = ..., strict: Any = ..., quad: Any = ..., verbose: Any = ...) -> Any
+ sympy/core/numbers.py:3020: note:      Subclass:
+ sympy/core/numbers.py:3020: note:          def evalf(self, prec: Any = ..., **options: Any) -> Any
+ sympy/core/numbers.py:3181: error: Signature of "evalf" incompatible with supertype "EvalfMixin"  [override]
+ sympy/core/numbers.py:3181: note:      Superclass:
+ sympy/core/numbers.py:3181: note:          def evalf(self, n: Any = ..., subs: Any = ..., maxn: Any = ..., chop: Any = ..., strict: Any = ..., quad: Any = ..., verbose: Any = ...) -> Any
+ sympy/core/numbers.py:3181: note:      Subclass:
+ sympy/core/numbers.py:3181: note:          def evalf(self, prec: Any = ..., **options: Any) -> Any
+ sympy/core/function.py:1775: error: Signature of "_eval_lseries" incompatible with supertype "Expr"  [override]
+ sympy/core/function.py:1775: note:      Superclass:
+ sympy/core/function.py:1775: note:          def _eval_lseries(self, x: Any, logx: Any = ..., cdir: Any = ...) -> Any
+ sympy/core/function.py:1775: note:      Subclass:
+ sympy/core/function.py:1775: note:          def _eval_lseries(self, x: Any, logx: Any, cdir: Any = ...) -> Any
+ sympy/core/function.py:2281: error: Signature of "evalf" incompatible with supertype "EvalfMixin"  [override]
+ sympy/core/function.py:2281: note:      Superclass:
+ sympy/core/function.py:2281: note:          def evalf(self, n: Any = ..., subs: Any = ..., maxn: Any = ..., chop: Any = ..., strict: Any = ..., quad: Any = ..., verbose: Any = ...) -> Any
+ sympy/core/function.py:2281: note:      Subclass:
+ sympy/core/function.py:2281: note:          def evalf(self, prec: Any = ..., **options: Any) -> Any
+ sympy/physics/units/prefixes.py:73: error: Signature of "__repr__" incompatible with supertype "Printable"  [override]
+ sympy/physics/units/prefixes.py:73: note:      Superclass:
+ sympy/physics/units/prefixes.py:73: note:          def __str__(Printable, /) -> Any
+ sympy/physics/units/prefixes.py:73: note:      Subclass:
+ sympy/physics/units/prefixes.py:73: note:          def __repr__(self) -> Any
+ sympy/functions/elementary/miscellaneous.py:649: error: Signature of "evalf" incompatible with supertype "EvalfMixin"  [override]
+ sympy/functions/elementary/miscellaneous.py:649: note:      Superclass:
+ sympy/functions/elementary/miscellaneous.py:649: note:          def evalf(self, n: Any = ..., subs: Any = ..., maxn: Any = ..., chop: Any = ..., strict: Any = ..., quad: Any = ..., verbose: Any = ...) -> Any
+ sympy/functions/elementary/miscellaneous.py:649: note:      Subclass:
+ sympy/functions/elementary/miscellaneous.py:649: note:          def evalf(self, n: Any = ..., **options: Any) -> Any
+ sympy/functions/elementary/miscellaneous.py:791: error: Signature of "_eval_is_positive" incompatible with supertype "MinMaxBase"  [override]
+ sympy/functions/elementary/miscellaneous.py:791: note:      Superclass:
+ sympy/functions/elementary/miscellaneous.py:791: note:          def (s: Any) -> Any
+ sympy/functions/elementary/miscellaneous.py:791: note:      Subclass:
+ sympy/functions/elementary/miscellaneous.py:791: note:          def _eval_is_positive(self) -> Any
+ sympy/functions/elementary/miscellaneous.py:794: error: Signature of "_eval_is_nonnegative" incompatible with supertype "MinMaxBase"  [override]
+ sympy/functions/elementary/miscellaneous.py:794: note:      Superclass:
+ sympy/functions/elementary/miscellaneous.py:794: note:          def (s: Any) -> Any
+ sympy/functions/elementary/miscellaneous.py:794: note:      Subclass:
+ sympy/functions/elementary/miscellaneous.py:794: note:          def _eval_is_nonnegative(self) -> Any
+ sympy/functions/elementary/miscellaneous.py:797: error: Signature of "_eval_is_negative" incompatible with supertype "MinMaxBase"  [override]
+ sympy/functions/elementary/miscellaneous.py:797: note:      Superclass:
+ sympy/functions/elementary/miscellaneous.py:797: note:          def (s: Any) -> Any
+ sympy/functions/elementary/miscellaneous.py:797: note:      Subclass:
+ sympy/functions/elementary/miscellaneous.py:797: note:          def _eval_is_negative(self) -> Any
+ sympy/functions/elementary/miscellaneous.py:854: error: Signature of "_eval_is_positive" incompatible with supertype "MinMaxBase"  [override]
+ sympy/functions/elementary/miscellaneous.py:854: note:      Superclass:
+ sympy/functions/elementary/miscellaneous.py:854: note:          def (s: Any) -> Any
+ sympy/functions/elementary/miscellaneous.py:854: note:      Subclass:
+ sympy/functions/elementary/miscellaneous.py:854: note:          def _eval_is_positive(self) -> Any
+ sympy/functions/elementary/miscellaneous.py:857: error: Signature of "_eval_is_nonnegative" incompatible with supertype "MinMaxBase"  [override]
+ sympy/functions/elementary/miscellaneous.py:857: note:      Superclass:
+ sympy/functions/elementary/miscellaneous.py:857: note:          def (s: Any) -> Any
+ sympy/functions/elementary/miscellaneous.py:857: note:      Subclass:
+ sympy/functions/elementary/miscellaneous.py:857: note:          def _eval_is_nonnegative(self) -> Any
+ sympy/functions/elementary/miscellaneous.py:860: error: Signature of "_eval_is_negative" incompatible with supertype "MinMaxBase"  [override]
+ sympy/functions/elementary/miscellaneous.py:860: note:      Superclass:
+ sympy/functions/elementary/miscellaneous.py:860: note:          def (s: Any) -> Any
+ sympy/functions/elementary/miscellaneous.py:860: note:      Subclass:
+ sympy/functions/elementary/miscellaneous.py:860: note:          def _eval_is_negative(self) -> Any
+ sympy/combinatorics/free_groups.py:442: error: Signature of "index" incompatible with supertype "tuple"  [override]
+ sympy/combinatorics/free_groups.py:442: note:      Superclass:
+ sympy/combinatorics/free_groups.py:442: note:          def index(self, Any, SupportsIndex = ..., SupportsIndex = ..., /) -> int
+ sympy/combinatorics/free_groups.py:442: note:      Subclass:
+ sympy/combinatorics/free_groups.py:442: note:          def index(self, gen: Any) -> Any
+ sympy/combinatorics/free_groups.py:442: error: Signature of "index" incompatible with supertype "Sequence"  [override]
+ sympy/combinatorics/free_groups.py:442: note:      Superclass:
+ sympy/combinatorics/free_groups.py:442: note:          def index(self, value: Any, start: int = ..., stop: int = ...) -> int
+ sympy/combinatorics/free_groups.py:442: note:      Subclass:
+ sympy/combinatorics/free_groups.py:442: note:          def index(self, gen: Any) -> Any
+ sympy/codegen/ast.py:318: error: Signature of "__repr__" incompatible with supertype "Printable"  [override]
+ sympy/codegen/ast.py:318: note:      Superclass:
+ sympy/codegen/ast.py:318: note:          def __str__(Printable, /) -> Any
+ sympy/codegen/ast.py:318: note:      Subclass:
+ sympy/codegen/ast.py:318: note:          def __repr__(self) -> Any
+ sympy/codegen/ast.py:907: error: Signature of "_sympystr" incompatible with supertype "Token"  [override]
+ sympy/codegen/ast.py:907: note:      Superclass:
+ sympy/codegen/ast.py:907: note:          def _sympyrepr(self: Token, printer: Any, *args: Any, joiner: Any = ..., **kwargs: Any) -> Any
+ sympy/codegen/ast.py:907: note:      Subclass:
+ sympy/codegen/ast.py:907: note:          def _sympystr(self, printer: Any, *args: Any, **kwargs: Any) -> Any
+ sympy/codegen/ast.py:1023: error: Signature of "_sympystr" incompatible with supertype "Token"  [override]
+ sympy/codegen/ast.py:1023: note:      Superclass:
+ sympy/codegen/ast.py:1023: note:          def _sympyrepr(self: Token, printer: Any, *args: Any, joiner: Any = ..., **kwargs: Any) -> Any
+ sympy/codegen/ast.py:1023: note:      Subclass:
+ sympy/codegen/ast.py:1023: note:          def _sympystr(self, printer: Any, *args: Any, **kwargs: Any) -> Any
+ sympy/codegen/ast.py:1298: error: Signature of "cast_nocheck" incompatible with supertype "FloatBaseType"  [override]
+ sympy/codegen/ast.py:1298: note:      Superclass:
+ sympy/codegen/ast.py:1298: note:          @staticmethod
+ sympy/codegen/ast.py:1298: note:          def __new__(cls, num: Any, dps: Any = ..., precision: Any = ...) -> Float
+ sympy/codegen/ast.py:1298: note:      Subclass:
+ sympy/codegen/ast.py:1298: note:          def cast_nocheck(self, value: Any) -> Any
+ sympy/codegen/ast.py:1318: error: Signature of "cast_nocheck" incompatible with supertype "FloatBaseType"  [override]
+ sympy/codegen/ast.py:1318: note:      Superclass:
+ sympy/codegen/ast.py:1318: note:          @staticmethod
+ sympy/codegen/ast.py:1318: note:          def __new__(cls, num: Any, dps: Any = ..., precision: Any = ...) -> Float
+ sympy/codegen/ast.py:1318: note:      Subclass:
+ sympy/codegen/ast.py:1318: note:          def cast_nocheck(self, value: Any) -> Any
+ sympy/codegen/ast.py:1399: error: Signature of "_sympystr" incompatible with supertype "Token"  [override]
+ sympy/codegen/ast.py:1399: note:      Superclass:
+ sympy/codegen/ast.py:1399: note:          def _sympyrepr(self: Token, printer: Any, *args: Any, joiner: Any = ..., **kwargs: Any) -> Any
+ sympy/codegen/ast.py:1399: note:      Subclass:
+ sympy/codegen/ast.py:1399: note:          def _sympystr(self, printer: Any, *args: Any, **kwargs: Any) -> Any

... (truncated 1170 lines) ...

manticore (https://github.com/trailofbits/manticore)
+ manticore/core/smtlib/visitors.py:199: error: Signature of "visit" incompatible with supertype "Visitor"  [override]
+ manticore/core/smtlib/visitors.py:199: note:      Superclass:
+ manticore/core/smtlib/visitors.py:199: note:          def visit(self, node: Any, use_fixed_point: Any = ...) -> Any
+ manticore/core/smtlib/visitors.py:199: note:      Subclass:
+ manticore/core/smtlib/visitors.py:199: note:          def visit(self, expression: Any) -> Any
+ manticore/core/workspace.py:400: error: Signature of "load_value" incompatible with supertype "Store"  [override]
+ manticore/core/workspace.py:400: note:      Superclass:
+ manticore/core/workspace.py:400: note:          def load_value(self, key: str, binary: bool = ...) -> Any
+ manticore/core/workspace.py:400: note:      Subclass:
+ manticore/core/workspace.py:400: note:          def load_value(self, key: Any) -> Any
+ manticore/ethereum/manticore.py:1572: error: Signature of "generate_testcase" incompatible with supertype "ManticoreBase"  [override]
+ manticore/ethereum/manticore.py:1572: note:      Superclass:
+ manticore/ethereum/manticore.py:1572: note:          def generate_testcase(self, state: Any, message: str = ..., name: str = ...) -> Testcase
+ manticore/ethereum/manticore.py:1572: note:      Subclass:
+ manticore/ethereum/manticore.py:1572: note:          def generate_testcase(self, state: Any, message: Any = ..., only_if: Any = ..., name: Any = ...) -> Any
+ tests/native/test_x86.py:73: error: Signature of "assertEqual" incompatible with supertype "TestCase"  [override]
+ tests/native/test_x86.py:73: note:      Superclass:
+ tests/native/test_x86.py:73: note:          def assertEqual(self, first: Any, second: Any, msg: Any = ...) -> None
+ tests/native/test_x86.py:73: note:      Subclass:
+ tests/native/test_x86.py:73: note:          def assertEqual(self, a: Any, b: Any) -> Any
+ tests/native/test_aarch64cpu.py:14071: error: Signature of "assertEqual" incompatible with supertype "TestCase"  [override]
+ tests/native/test_aarch64cpu.py:14071: note:      Superclass:
+ tests/native/test_aarch64cpu.py:14071: note:          def assertEqual(self, first: Any, second: Any, msg: Any = ...) -> None
+ tests/native/test_aarch64cpu.py:14071: note:      Subclass:
+ tests/native/test_aarch64cpu.py:14071: note:          def assertEqual(self, actual: Any, expected: Any, *args: Any, **kwargs: Any) -> Any
+ manticore/native/manticore.py:27: error: Signature of "generate_testcase" incompatible with supertype "ManticoreBase"  [override]
+ manticore/native/manticore.py:27: note:      Superclass:
+ manticore/native/manticore.py:27: note:          def generate_testcase(self, state: Any, message: str = ..., name: str = ...) -> Testcase
+ manticore/native/manticore.py:27: note:      Subclass:
+ manticore/native/manticore.py:27: note:          def generate_testcase(self, state: Any, message: Any = ...) -> Any

cloud-init (https://github.com/canonical/cloud-init)
+ cloudinit/safeyaml.py:165: error: Signature of "construct_mapping" incompatible with supertype "SafeConstructor"  [override]
+ cloudinit/safeyaml.py:165: note:      Superclass:
+ cloudinit/safeyaml.py:165: note:          def construct_mapping(self, node: MappingNode, deep: bool = ...) -> dict[Hashable, Any]
+ cloudinit/safeyaml.py:165: note:      Subclass:
+ cloudinit/safeyaml.py:165: note:          def construct_mapping(self, node: Any) -> Any
+ cloudinit/safeyaml.py:165: error: Signature of "construct_mapping" incompatible with supertype "BaseConstructor"  [override]
+ cloudinit/safeyaml.py:165: note:      Superclass:
+ cloudinit/safeyaml.py:165: note:          def construct_mapping(self, node: MappingNode, deep: bool = ...) -> dict[Hashable, Any]
+ cloudinit/safeyaml.py:165: note:      Subclass:
+ cloudinit/safeyaml.py:165: note:          def construct_mapping(self, node: Any) -> Any
+ cloudinit/helpers.py:442: error: Signature of "get" incompatible with supertype "RawConfigParser"  [override]
+ cloudinit/helpers.py:442: note:      Superclass:
+ cloudinit/helpers.py:442: note:          @overload
+ cloudinit/helpers.py:442: note:          def get(self, section: str, option: str, *, raw: bool = ..., vars: Mapping[str, str] | None = ...) -> str | Any
+ cloudinit/helpers.py:442: note:          @overload
+ cloudinit/helpers.py:442: note:          def [_T] get(self, section: str, option: str, *, raw: bool = ..., vars: Mapping[str, str] | None = ..., fallback: _T) -> str | _T | Any
+ cloudinit/helpers.py:442: note:      Subclass:
+ cloudinit/helpers.py:442: note:          def get(self, section: Any, option: Any) -> Any
+ cloudinit/helpers.py:442: error: Signature of "get" incompatible with supertype "Mapping"  [override]
+ cloudinit/helpers.py:442: note:      Superclass:
+ cloudinit/helpers.py:442: note:          @overload
+ cloudinit/helpers.py:442: note:          def get(self, str, /) -> Mapping[str, str] | None
+ cloudinit/helpers.py:442: note:          @overload
+ cloudinit/helpers.py:442: note:          def [_T] get(self, str, /, default: Mapping[str, str] | _T) -> Mapping[str, str] | _T
+ cloudinit/helpers.py:442: note:      Subclass:
+ cloudinit/helpers.py:442: note:          def get(self, section: Any, option: Any) -> Any
+ cloudinit/helpers.py:461: error: Signature of "getboolean" incompatible with supertype "RawConfigParser"  [override]
+ cloudinit/helpers.py:461: note:      Superclass:
+ cloudinit/helpers.py:461: note:          @overload
+ cloudinit/helpers.py:461: note:          def getboolean(self, section: str, option: str, *, raw: bool = ..., vars: Mapping[str, str] | None = ...) -> bool
+ cloudinit/helpers.py:461: note:          @overload
+ cloudinit/helpers.py:461: note:          def [_T] getboolean(self, section: str, option: str, *, raw: bool = ..., vars: Mapping[str, str] | None = ..., fallback: _T = ...) -> bool | _T
+ cloudinit/helpers.py:461: note:      Subclass:
+ cloudinit/helpers.py:461: note:          def getboolean(self, section: Any, option: Any) -> Any
+ cloudinit/helpers.py:466: error: Signature of "getfloat" incompatible with supertype "RawConfigParser"  [override]
+ cloudinit/helpers.py:466: note:      Superclass:
+ cloudinit/helpers.py:466: note:          @overload
+ cloudinit/helpers.py:466: note:          def getfloat(self, section: str, option: str, *, raw: bool = ..., vars: Mapping[str, str] | None = ...) -> float
+ cloudinit/helpers.py:466: note:          @overload
+ cloudinit/helpers.py:466: note:          def [_T] getfloat(self, section: str, option: str, *, raw: bool = ..., vars: Mapping[str, str] | None = ..., fallback: _T = ...) -> float | _T
+ cloudinit/helpers.py:466: note:      Subclass:
+ cloudinit/helpers.py:466: note:          def getfloat(self, section: Any, option: Any) -> Any
+ cloudinit/helpers.py:471: error: Signature of "getint" incompatible with supertype "RawConfigParser"  [override]
+ cloudinit/helpers.py:471: note:      Superclass:
+ cloudinit/helpers.py:471: note:          @overload
+ cloudinit/helpers.py:471: note:          def getint(self, section: str, option: str, *, raw: bool = ..., vars: Mapping[str, str] | None = ...) -> int
+ cloudinit/helpers.py:471: note:          @overload
+ cloudinit/helpers.py:471: note:          def [_T] getint(self, section: str, option: str, *, raw: bool = ..., vars: Mapping[str, str] | None = ..., fallback: _T = ...) -> int | _T
+ cloudinit/helpers.py:471: note:      Subclass:
+ cloudinit/helpers.py:471: note:          def getint(self, section: Any, option: Any) -> Any
+ tests/integration_tests/clouds.py:362: error: Signature of "_get_or_set_profile_list" incompatible with supertype "_LxdIntegrationCloud"  [override]

... (truncated 771 lines) ...```

stroxler added a commit to stroxler/mypy that referenced this pull request May 21, 2024
This commit fixes python#9618 by making MyPy always complain if a method
overrides a base class method marked as `@final`.

In the process, it also adds a few additional validations:
- Always verify the `@override` decorator, which ought to be pretty
  backward-compatible for most projects assuming that strict override
  checks aren't enabled by default (and it appears to me that
  `--enable-error-code explicit-override` is off by default)
- Verify that the method signature is compatible (which in practice
  means only arity and argument name checks) *if* the
  `--check-untyped-defs` flag is set; it seems unlikely that a user
  would want mypy to validate the bodies of untyped functions but
  wouldn't want to be alerted about incompatible overrides.

Note: I did also explore enabling the signature compatibility check
for all code, which in principle makes sense. But the mypy_primer
results indicated that there would be backward compability issues
because too many libraries rely on us not validating this:
python#17274
@stroxler
Copy link
Contributor Author

The mypy_primer tests suggest this is too noisy.

Closing this PR in favor of more relaxed checks in #17276

@stroxler stroxler closed this May 21, 2024
hauntsaninja pushed a commit that referenced this pull request May 23, 2024
This commit fixes #9618 by making MyPy always complain if a method
overrides a base class method marked as `@final`.

In the process, it also adds a few additional validations:
- Always verify the `@override` decorator, which ought to be pretty
backward-compatible for most projects assuming that strict override
checks aren't enabled by default (and it appears to me that
`--enable-error-code explicit-override` is off by default)
- Verify that the method signature is compatible (which in practice
means only arity and argument name checks) *if* the
`--check-untyped-defs` flag is set; it seems unlikely that a user would
want mypy to validate the bodies of untyped functions but wouldn't want
to be alerted about incompatible overrides.

Note: I did also explore enabling the signature compatibility check for
all code, which in principle makes sense. But the mypy_primer results
indicated that there would be backward compability issues because too
many libraries rely on us not validating this:
#17274
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

Successfully merging this pull request may close these issues.

@final decorator is not honored on override with --check-untyped-defs
1 participant