You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
False-positive on TypeVar bound class return value #10817
I recently added support for this case in pyright. It took me a while to work out a good solution, so I'll share it here in case it might be of use for someone attempting to fix this in mypy.
When a symbol whose type is defined by a type variable (such as in the example above, where t is of type QBT) is narrowed using an isinstance or issubclass test, the type checker needs to remember that 1) it is now narrowed to the specified type (in the example above, B) and 2) it originated from the type variable. The second part is important to avoid a false positive error like the one reported in this example.
I solved the problem in pyright by introducing the notion of a "conditional type". A conditional type is like a normal type except that it's conditioned on one or more type variables from which it derives. In the case of constrained type variables, a type is conditioned on a particular constraint of a type variable. The example above uses a bound type variable, so there is no specific constraint associated with this condition. Pyright reports conditional types with a * after them to indicate that there is one or more type variable conditions associated with it.
deffn(t: QBT) ->QBT:
reveal_type(t) # Type of "t" is "QBT@fn"ifnotisinstance(t, B):
raiseNotImplementedErrorreveal_type(t) # Type of "t" is "B*"returnt
This same approach allows constrained type variables to be analyzed in a single pass, versus the multi-pass approach currently used in mypy.
FloatOrStr=TypeVar("FloatOrStr", float, str)
defadd(v1: FloatOrStr, v2: FloatOrStr) ->FloatOrStr:
reveal_type(v1) # Type of "v1" is "FloatOrStr@add"reveal_type(v2) # Type of "v2" is "FloatOrStr@add"v3=v1+v2reveal_type(v3) # Type of "v3" is "float* | str*"ifisinstance(v3, float):
reveal_type(v3) # Type of "v3" is "float*"returnv3+1else:
reveal_type(v3) # Type of "v3" is "str*"returnv3+"1"
Reacted by Jelle Zijlstra, Kaleb Barrett, Iwan Aucamp, Neil Girdhar, Shantanu, Phillip Verheyden, Tom Hanks Noname, Illviljan and Felix F Zimmermann
Reacted by Salomon Smeke, Iwan Aucamp, Avasam and adam cushner
May be relevant to this bug. In the following code, mypy seems to ignore the type guard and requests me to return an object that is both tuple and dict.
@LutingWang were you ever able to work around your issue in any way? I'm trying to do the same thing where I essentially am saying 'lets make up this new type that can actually be one of these 5 classes'. If I return an instance of any of those classes, mypy is complaining.
@LutingWang were you ever able to work around your issue in any way? I'm trying to do the same thing where I essentially am saying 'lets make up this new type that can actually be one of these 5 classes'. If I return an instance of any of those classes, mypy is complaining.
Activity
[-]False-positive on TypeVar bound class[/-][+]False-positive on TypeVar bound class return value[/+]erictraut commentedon Jul 13, 2021
I recently added support for this case in pyright. It took me a while to work out a good solution, so I'll share it here in case it might be of use for someone attempting to fix this in mypy.
When a symbol whose type is defined by a type variable (such as in the example above, where
tis of typeQBT) is narrowed using anisinstanceorissubclasstest, the type checker needs to remember that 1) it is now narrowed to the specified type (in the example above,B) and 2) it originated from the type variable. The second part is important to avoid a false positive error like the one reported in this example.I solved the problem in pyright by introducing the notion of a "conditional type". A conditional type is like a normal type except that it's conditioned on one or more type variables from which it derives. In the case of constrained type variables, a type is conditioned on a particular constraint of a type variable. The example above uses a bound type variable, so there is no specific constraint associated with this condition. Pyright reports conditional types with a
*after them to indicate that there is one or more type variable conditions associated with it.This same approach allows constrained type variables to be analyzed in a single pass, versus the multi-pass approach currently used in mypy.
LutingWang commentedon Jun 3, 2022
May be relevant to this bug. In the following code, mypy seems to ignore the type guard and requests me to return an object that is both tuple and dict.
mypy 0.960 (compiled: yes)
Python 3.8.9
shawnwall commentedon Oct 17, 2022
@LutingWang were you ever able to work around your issue in any way? I'm trying to do the same thing where I essentially am saying 'lets make up this new type that can actually be one of these 5 classes'. If I return an instance of any of those classes, mypy is complaining.
LutingWang commentedon Oct 19, 2022
For now I'm using
overload, similar to this post. @shawnwallstarhel commentedon May 15, 2023
I've probably encountered same bug when using dataclasses. Reproduction:
For now I have no idea how to workaround this case,
overloadis not an option for me.Narrow type variable bounds in binder (python#19183)