-
-
Notifications
You must be signed in to change notification settings - Fork 2.9k
TypeVar is not resolved correctly with None optional argument #8708
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
Comments
When running under I recommend using
|
Thank you. |
I don't see how I can construct an
|
im hitting this one as well even for type vars specific types i'd really like to b able to use the resolved type of a argument in the output expression as opposed to overloads an example where this would really be nice is for example instead of spelling 4 overloads, i'd like to refer to resolved types more nicely def get(
self,
key: str,
default: _D | None = None,
convert: Callable[[str], _T] | None = None,
) -> _D | _T | str | None:
return self.config.get(self.name, key, convert=convert, default=default) it would be really great if something like def get(
self,
key: str,
default: _D | None = None,
convert: Callable[[str], _T] | None = None,
) -> ResolveOr[_T, str] | ResolveOr[ _D, None]:
return self.config.get(self.name, key, convert=convert, default=default) completely avoiding the overload in that case |
I see there are two bugs considering default arguments and TypeVars and especially TypeVars allowing None. Considering the example below in which a TypeVar can be either a Logger (Protocol) or None. It has two issues:
from typing import TypeVar, Generic, Self, Protocol, reveal_type
class LoggerProtocol(Protocol):
def bind(self) -> Self: ...
class Logger:
def bind(self) -> Self:
return self
TLoggerOrNone = TypeVar("TLoggerOrNone", None, Logger)
class Bar(Generic[TLoggerOrNone]):
def __init__(self, val: TLoggerOrNone = None): # ❌ error: Incompatible default for argument "val" (default has type "None", argument has type "Logger") [assignment]
self._val: TLoggerOrNone = val
@property
def val(self) -> TLoggerOrNone:
if self._val is None:
return self._val
return self._val.bind()
reveal_type(Bar().val) # ❌ Revealed type is "__main__.Logger"
reveal_type(Bar(Logger()).val) # ✅ Revealed type is "__main__.Logger"
reveal_type(Bar(None).val) # ✅ Revealed type is "None"
reveal_type(Bar("aaa").val) # ✅ error: Value of type variable "TLoggerOrNone" of "Bar" cannot be "str" [type-var] If we modify the example to use from typing import TypeVar, Generic, Self, Protocol, reveal_type
class LoggerProtocol(Protocol):
def bind(self) -> Self: ...
class Logger:
def bind(self) -> Self:
return self
TLoggerOrStr = TypeVar("TLoggerOrStr", Logger, str)
class Bar(Generic[TLoggerOrStr]):
def __init__(self, val: TLoggerOrStr = "abc"): # ❌ Incompatible default for argument "val" (default has type "str", argument has type "Logger") [assignment]
self._val: TLoggerOrStr = val
@property
def val(self) -> TLoggerOrStr:
if isinstance(self._val, str):
return self._val
return self._val.bind()
reveal_type(Bar().val) # ❌ Revealed type is "__main__.Logger"
reveal_type(Bar(Logger()).val) # ✅ Revealed type is "__main__.Logger"
reveal_type(Bar(None).val) # ✅ error: Value of type variable "TLoggerOrStr" of "Bar" cannot be "None" [type-var]
reveal_type(Bar("aaa").val) # ✅ revealed type is "builtins.str" Strange enough, if the order of the TypeVars are changed, i.e. I can't use overloads to define my So long story short: I think the issue is not special to |
Handling default values for parameters annotated with a TypeVar is quite complicated — both from an implementation and a type soundness perspective. Refer to this issue for more details. |
Thanks for pointing this out @erictraut . |
Running
mypy --no-implicit-optional test.py
with the code below gives the errorIncompatible default for argument "default" (default has type "None", argument has type "D")
I would expect mypy doesn't emit any error. The type of
default
should be resolved toNone
when the function is called without the optional argument, e.g.get_first([])
, so that the return type isUnion[E, None]
, which is equivalent toOptional[E]
.I am using mypy 0.770 and python 3.7
The text was updated successfully, but these errors were encountered: