8000 Fix crash when overriding with unpacked TypedDict (#17359) · python/mypy@b8a0260 · GitHub
[go: up one dir, main page]

Skip to content

Commit b8a0260

Browse files
Fix crash when overriding with unpacked TypedDict (#17359)
Fixes #17208 While writing the fix (that is trivial), I could not notice that the relevant code simply assumes functions can have nothing but positional parameters. This could lead really misleading error messages, so I decided to fix this as well. --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
1 parent 317533c commit b8a0260

File tree

2 files changed

+67
-12
lines changed

2 files changed

+67
-12
lines changed

mypy/checker.py

Lines changed: 19 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2255,6 +2255,7 @@ def check_override(
22552255
if fail:
22562256
emitted_msg = False
22572257

2258+
offset_arguments = isinstance(override, CallableType) and override.unpack_kwargs
22582259
# Normalize signatures, so we get better diagnostics.
22592260
if isinstance(override, (CallableType, Overloaded)):
22602261
override = override.with_unpacked_kwargs()
@@ -2285,12 +2286,23 @@ def check_override(
22852286
def erase_override(t: Type) -> Type:
22862287
return erase_typevars(t, ids_to_erase=override_ids)
22872288

2288-
for i in range(len(override.arg_types)):
2289-
if not is_subtype(
2290-
original.arg_types[i], erase_override(override.arg_types[i])
2291-
):
2292-
arg_type_in_super = original.arg_types[i]
2293-
2289+
for i, (sub_kind, super_kind) in enumerate(
2290+
zip(override.arg_kinds, original.arg_kinds)
2291+
):
2292+
if sub_kind.is_positional() and super_kind.is_positional():
2293+
override_arg_type = override.arg_types[i]
2294+
original_arg_type = original.arg_types[i]
2295+
elif sub_kind.is_named() and super_kind.is_named() and not offset_arguments:
2296+
arg_name = override.arg_names[i]
2297+
if arg_name in original.arg_names:
2298+
override_arg_type = override.arg_types[i]
2299+
original_i = original.arg_names.index(arg_name)
2300+
original_arg_type = original.arg_types[original_i]
2301+
else:
2302+
continue
2303+
else:
2304+
continue
2305+
if not is_subtype(original_arg_type, erase_override(override_arg_type)):
22942306
if isinstance(node, FuncDef) and not node.is_property:
22952307
context: Context = node.arguments[i + len(override.bound_args)]
22962308
else:
@@ -2300,7 +2312,7 @@ def erase_override(t: Type) -> Type:
23002312
name,
23012313
type_name,
23022314
name_in_super,
2303-
arg_type_in_super,
2315+
original_arg_type,
23042316
supertype,
23052317
context,
23062318
secondary_context=node,

test-data/unit/check-functions.test

Lines changed: 48 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -40,11 +40,10 @@ class B(A):
4040
class C(A):
4141
def f(self, *, b: int, a: str) -> None: pass # Fail
4242
[out]
43-
main:10: error: Signature of "f" incompatible with supertype "A"
44-
main:10: note: Superclass:
45-
main:10: note: def f(self, *, a: int, b: str) -> None
46-
main:10: note: Subclass:
47-
main:10: note: def f(self, *, b: int, a: str) -> None
43+
main:10: error: Argument 1 of "f" is incompatible with supertype "A"; supertype defines the argument type as "str"
44+
main:10: note: This violates the Liskov substitution principle
45+
main:10: note: See https://mypy.readthedocs.io/en/stable/common_issues.html#incompatible-overrides
46+
main:10: error: Argument 2 of "f" is incompatible with supertype "A"; supertype defines the argument type as "int"
4847

4948
[case testPositionalOverridingArgumentNameInsensitivity]
5049
import typing
@@ -3324,3 +3323,47 @@ class Bar(Foo):
33243323
# N: See https://mypy.readthedocs.io/en/stable/common_issues.html#incompatible-overrides
33253324
...
33263325
[builtins fixtures/property.pyi]
3326+
3327+
[case testNoCrashOnUnpackOverride]
3328+
from typing import Unpack
3329+
from typing_extensions import TypedDict
3330+
3331+
class Params(TypedDict):
3332+
x: int
3333+
y: str
3334+
3335+
class Other(TypedDict):
3336+
x: int
3337+
y: int
3338+
3339+
class B:
3340+
def meth(self, **kwargs: Unpack[Params]) -&g D567 t; None:
3341+
...
3342+
class C(B):
3343+
def meth(self, **kwargs: Unpack[Other]) -> None: # E: Signature of "meth" incompatible with supertype "B" \
3344+
# N: Superclass: \
3345+
# N: def meth(*, x: int, y: str) -> None \
3346+
# N: Subclass: \
3347+
# N: def meth(*, x: int, y: int) -> None
3348+
3349+
...
3350+
[builtins fixtures/tuple.pyi]
3351+
3352+
[case testOverrideErrorLocationNamed]
3353+
class B:
3354+
def meth(
3355+
self, *,
3356+
x: int,
3357+
y: str,
3358+
) -> None:
3359+
...
3360+
class C(B):
3361+
def meth(
3362+
self, *,
3363+
y: int, # E: Argument 1 of "meth" is incompatible with supertype "B"; supertype defines the argument type as "str" \
3364+
# N: This violates the Liskov substitution principle \
3365+
# N: See https://mypy.readthedocs.io/en/stable/common_issues.html#incompatible-overrides
3366+
x: int,
3367+
) -> None:
3368+
...
3369+
[builtins fixtures/tuple.pyi]

0 commit comments

Comments
 (0)
0