-
-
Notifications
You must be signed in to change notification settings - Fork 2.8k
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
Mypy does not correctly narrow indexing operations when using Literal or Final keys #7905
Labels
priority-2-low
topic-final
PEP 591
topic-literal-types
topic-type-narrowing
Conditional type narrowing / binder
topic-typed-dict
Comments
Does this happen often? If not, I would propose to not worry about this for now (mostly because I am not sure what to do either). |
Related #7339 (even stronger version of this). |
Michael0x2a
added a commit
to Michael0x2a/mypy
that referenced
this issue
Nov 9, 2019
This diff adds support for the following pattern: ```python from typing import Enum, List from enum import Enum class Key(Enum): A = 1 B = 2 class Foo: key: Literal[Key.A] blah: List[int] class Bar: key: Literal[Key.B] something: List[str] x: Union[Foo, Bar] if x.key is Key.A: reveal_type(x) # Revealed type is 'Foo' else: reveal_type(x) # Revealed type is 'Bar' ``` In short, when we do `x.key is Key.A`, we "propagate" the information we discovered about `x.key` up one level to refine the type of `x`. We perform this propagation only when `x` is a Union and only when we are doing member or index lookups into instances, typeddicts, namedtuples, and tuples. For indexing operations, we have one additional limitation: we *must* use a literal expression in order for narrowing to work at all. Using Literal types or Final instances won't work; See python#7905 for more details. To put it another way, this adds support for tagged unions, I guess. This more or less resolves python#7344. We currently don't have support for narrowing based on string or int literals, but that's a separate issue and should be resolved by python#7169 (which I resumed work on earlier this week).
Michael0x2a
added a commit
to Michael0x2a/mypy
that referenced
this issue
Nov 9, 2019
This diff adds support for the following pattern: ```python from typing import Enum, List from enum import Enum class Key(Enum): A = 1 B = 2 class Foo: key: Literal[Key.A] blah: List[int] class Bar: key: Literal[Key.B] something: List[str] x: Union[Foo, Bar] if x.key is Key.A: reveal_type(x) # Revealed type is 'Foo' else: reveal_type(x) # Revealed type is 'Bar' ``` In short, when we do `x.key is Key.A`, we "propagate" the information we discovered about `x.key` up one level to refine the type of `x`. We perform this propagation only when `x` is a Union and only when we are doing member or index lookups into instances, typeddicts, namedtuples, and tuples. For indexing operations, we have one additional limitation: we *must* use a literal expression in order for narrowing to work at all. Using Literal types or Final instances won't work; See python#7905 for more details. To put it another way, this adds support for tagged unions, I guess. This more or less resolves python#7344. We currently don't have support for narrowing based on string or int literals, but that's a separate issue and should be resolved by python#7169 (which I resumed work on earlier this week).
Michael0x2a
added a commit
to Michael0x2a/mypy
that referenced
this issue
Nov 9, 2019
This diff adds support for the following pattern: ```python from typing import Enum, List from enum import Enum class Key(Enum): A = 1 B = 2 class Foo: key: Literal[Key.A] blah: List[int] class Bar: key: Literal[Key.B] something: List[str] x: Union[Foo, Bar] if x.key is Key.A: reveal_type(x) # Revealed type is 'Foo' else: reveal_type(x) # Revealed type is 'Bar' ``` In short, when we do `x.key is Key.A`, we "propagate" the information we discovered about `x.key` up one level to refine the type of `x`. We perform this propagation only when `x` is a Union and only when we are doing member or index lookups into instances, typeddicts, namedtuples, and tuples. For indexing operations, we have one additional limitation: we *must* use a literal expression in order for narrowing to work at all. Using Literal types or Final instances won't work; See python#7905 for more details. To put it another way, this adds support for tagged unions, I guess. This more or less resolves python#7344. We currently don't have support for narrowing based on string or int literals, but that's a separate issue and should be resolved by python#7169 (which I resumed work on earlier this week).
Yeah, it's somewhat low priority. I mostly added this issue because it came up while I was working on another PR and wanted something to link to. |
Michael0x2a
added a commit
that referenced
this issue
Nov 13, 2019
This diff adds support for the following pattern: ```python from typing import Enum, List from enum import Enum class Key(Enum): A = 1 B = 2 class Foo: key: Literal[Key.A] blah: List[int] class Bar: key: Literal[Key.B] something: List[str] x: Union[Foo, Bar] if x.key is Key.A: reveal_type(x) # Revealed type is 'Foo' else: reveal_type(x) # Revealed type is 'Bar' ``` In short, when we do `x.key is Key.A`, we "propagate" the information we discovered about `x.key` up to refine the type of `x`. We perform this propagation only when `x` is a Union and only when we are doing member or index lookups into instances, typeddicts, namedtuples, and tuples. For indexing operations, we have one additional limitation: we *must* use a literal expression in order for narrowing to work at all. Using Literal types or Final instances won't work; See #7905 for more details. To put it another way, this adds support for tagged unions, I guess. This more or less resolves #7344. We currently don't have support for narrowing based on string or int literals, but that's a separate issue and should be resolved by #7169 (which I resumed work on earlier this week).
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Labels
priority-2-low
topic-final
PEP 591
topic-literal-types
topic-type-narrowing
Conditional type narrowing / binder
topic-typed-dict
Consider the following program:
Mypy is currently capable of narrowing expressions like
d["key"]
, which we can see in the first expression.So, it's natural to assume that mypy would be able to do the same for the second since the two programs are theoretically identical -- but we can't.
The root cause has to do with the "literal" subsystem (which is not to be confused with the Literal types subsystem) here: https://github.com/python/mypy/blob/master/mypy/literals.py#L65
The index in the second example is a NameExpr, which causes the if statement to evaluate to false and return a
LITERAL_NO
. This then makes the narrowing logic rule outd[KEY]
as a candidate for narrowing in https://github.com/python/mypy/blob/master/mypy/checker.py#L3775.I'm not really sure what the best way of fixing this would be. The natural solution would be to also pass along the expression type into the
literal(...)
function, but that seems very annoying to do. I'm also not entirely sure whether this is even a sound narrowing: I'm not very familiar with the "literals" subsystem, or how it's meant to interact with Literal types.The text was updated successfully, but these errors were encountered: