-
-
Notifications
You must be signed in to change notification settings - Fork 2k
Description
MutableMapping.update is currently defined like this:
@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: ...Because UserDict inherits from MutableMapping and because the last overload doesn't specify self as a positional-only parameter, this override makes it impossible to override update. I can either try to do this:
class MyDict[K, V](UserDict[K, V]):
class __EmptyArg:
pass
__emptyarg = EmptyArg()
@overload
def update(self, m: SupportsKeysAndGetItem[K, V], /) -> None: ...
@overload
def update(self: SupportsGetItem[str, V], m: SupportsKeysAndGetItem[str, V], /, **kwargs: V) -> None: ...
@overload
def update(self, m: Iterable[tuple[K, V]], /) -> None: ...
@overload
def update(self: SupportsGetItem[str, V], m: Iterable[tuple[str, V]], /, **kwargs: V) -> None: ...
@overload
def update(self: SupportsGetItem[str, V], **kwargs: V) -> None: ...
def update(
self,
m: SupportsKeysAndGetItem[K, V] | Iterable[tuple[K, V] | __EmptyArg = __emptyarg,
/,
**kwargs: V,
):
if isinstance(m, MyDict.__EmptyArg):
return super().update(**kwargs)
return super().update(m, **kwargs)Which pyright faults with the following message:
Overloaded implementation is not consistent with signature of overload 5
Type "(self: Self@MyDict[K@MyDict, V@MyDict], m: SupportsKeysAndGetItem[K@MyDict, V@MyDict] | Iterable[tuple[K@MyDict, V@MyDict]] | __EmptyArg = __emptyarg, /, **kwargs: V@MyDict) -> None" is not assignable to type "(self: Self@MyDict[K@MyDict, V@MyDict], **kwargs: V@MyDict) -> None"
Position-only parameter mismatch; parameter "self" is not position-only
Position-only parameter mismatch; expected 1 but received 0
To "fix this", I would have to update the last overload in my class to this:
@overload
def update(self: SupportsGetItem[str, V], /, **kwargs: V) -> None: ...But that's also incompatible with the definition in typeshed, so of course I get the following error:
Method "update" overrides class "MutableMapping" in an incompatible manner
Override does not handle all overloads of base method
Potential solution
I updated this line in my local checkout of typeshed to make self positional-only in all cases:
def update(self: SupportsGetItem[str, _VT], /, **kwargs: _VT) -> None: ...This resolved the issue, but I might be missing some important detail that makes this not feasible.
In general it seems to me like having multiple overloads in a function where the same parameter is sometimes marked positional-only and sometimes not should be illegal outright, but that's something for the type checkers to validate.