-
Notifications
You must be signed in to change notification settings - Fork 219
Open
0 / 10 of 1 issue completedOpen
0 / 10 of 1 issue completed
Copy link
Milestone
Description
Summary
ty incorrectly rejects a valid method override of MutableMapping.update that mypy accepts. The override uses a Protocol-based type alias that is structurally compatible with the base class signature.
Reproduction
from __future__ import annotations
from collections.abc import MutableMapping, Iterator, Iterable
import typing as t
KT = t.TypeVar('KT')
VT = t.TypeVar('VT')
VT_co = t.TypeVar('VT_co', covariant=True)
@t.runtime_checkable
class Maplike(t.Protocol[KT, VT_co]):
"""Equivalent to typeshed's SupportsKeysAndGetItem."""
def keys(self) -> Iterable[KT]: ...
def __getitem__(self, key: KT, /) -> VT_co: ...
MapOrItems = Maplike[KT, VT] | Iterable[tuple[KT, VT]]
class MyMapping(MutableMapping[KT, VT]):
_data: dict[KT, VT]
def __init__(self) -> None:
self._data = {}
def __getitem__(self, key: KT) -> VT:
return self._data[key]
def __setitem__(self, key: KT, value: VT) -> None:
self._data[key] = value
def __delitem__(self, key: KT) -> None:
del self._data[key]
def __iter__(self) -> Iterator[KT]:
return iter(self._data)
def __len__(self) -> int:
return len(self._data)
# This signature accepts the same input types as MutableMapping.update
def update(self, arg: MapOrItems[KT, VT] = (), /, **kw: VT) -> None:
passExpected Behavior
The update override should be accepted because:
Maplike[KT, VT]is a Protocol withkeys()and__getitem__- structurally equivalent toSupportsKeysAndGetItemMapOrItems = Maplike[KT, VT] | Iterable[tuple[KT, VT]]covers all the argument types thatMutableMapping.updateaccepts- The
**kw: VTparameter mirrors the kwargs overloads
mypy 1.19.1 accepts this with --strict.
Actual Behavior
error[invalid-method-override]: Invalid override of method `update`
--> repro.py:39:9
|
37 | return len(self._data)
38 |
39 | def update(self, arg: MapOrItems[KT, VT] = (), /, **kw: VT) -> None:
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Definition is incompatible with `MutableMapping.update`
40 | pass
|
info: This violates the Liskov Substitution Principle
Analysis
The MutableMapping.update signature in typeshed is:
@overload
def update(self, m: SupportsKeysAndGetItem[_KT, _VT], /) -> None: ...
@overload
def update(self: SupportsGetItem[str, _VT], m: SupportsKeysAndGetItem[str, _VT], /, **kwargs: _VT) -> None: ...
@overload
def update(self, m: Iterable[tuple[_KT, _VT]], /) -> None: ...
@overload
def update(self: SupportsGetItem[str, _VT], m: Iterable[tuple[str, _VT]], /, **kwargs: _VT) -> None: ...
@overload
def update(self: SupportsGetItem[str, _VT], /, **kwargs: _VT) -> None: ...The child signature (self, arg: Maplike[KT, VT] | Iterable[tuple[KT, VT]] = (), /, **kw: VT) -> None is a valid override because:
Maplikeis structurally equivalent toSupportsKeysAndGetItem- The union covers both main overload variants
- Default
()is compatible withIterable[tuple[KT, VT]] - The kwargs match
ty should recognize this structural Protocol equivalence when checking method overrides.
Environment
- ty 0.0.14
- Python 3.13
- mypy 1.19.1 (for comparison - accepts this code)
Note: This issue affects my bidict library, which passes mypy strict mode but fails ty checks on this signature.
Version
ty 0.0.14 (16597f5 2026-01-26)
Reactions are currently unavailable
Sub-issues
Metadata
Metadata
Assignees
Labels
No labels