Skip to content
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

Narrowing types to Literal using in syntax #12535

Open
tusharsadhwani opened this issue Apr 6, 2022 · 6 comments
Open

Narrowing types to Literal using in syntax #12535

tusharsadhwani opened this issue Apr 6, 2022 · 6 comments
Labels

Comments

@tusharsadhwani
Copy link
Contributor

tusharsadhwani commented Apr 6, 2022

Take this example code:

x = "a"
reveal_type(x)
assert x in ("a", "b", "c")
reveal_type(x)

y = 20
reveal_type(y)
assert y == 10 or y == 20 or y == 50
reveal_type(y)

The current output of this is:

$ mypy x.py 
x.py:2: note: Revealed type is "builtins.str"
x.py:4: note: Revealed type is "builtins.str"
x.py:7: note: Revealed type is "builtins.int"
x.py:9: note: Revealed type is "builtins.int"

If mypy were to support type narrowing using chained or statements and in statements, the output would look like:

$ mypy x.py 
x.py:4: note: Revealed type is "builtins.str"
x.py:6: note: Revealed type is "Union[Literal['a'], Literal['b'], Literal['c']]"
x.py:9: note: Revealed type is "builtins.int"
x.py:11: note: Revealed type is "Union[Literal[10], Literal[20], Literal[50]]"

I think doing simple narrowing here would be relatively easy and very useful.

@JelleZijlstra JelleZijlstra added the topic-type-narrowing Conditional type narrowing / binder label Apr 6, 2022
@tmke8
Copy link
Contributor

tmke8 commented Apr 7, 2022

Related to (but maybe not quite a duplicate of) #9718.

@tusharsadhwani
Copy link
Contributor Author

It's a superset of #9718. I'll be looking into implementing this then, starting with the literal narrowing case.

@AlexWaygood
Copy link
Member

A superset of #9718, and a subset of #10977 :)

@bluenote10
Copy link
Contributor

This issue seems to address narrowing both for in but also for ==. Should these two features be tracked independently (if their implementations would be rather decoupled) or is tracking them together preferred (if implementing one would likely solve the other)?

I couldn't find an issue specifically for narrowing literals in == equality comparisons, and I would have expected this to be implemented first, because it even feels more basic than the in operator:

from typing import Literal

def takes_foo(s: Literal["foo"]) -> None:
    ...

def f(s: str) -> None:
    if s == "foo":
        takes_foo(s)
error: Argument 1 to "takes_foo" has incompatible type "str"; expected "Literal['foo']"  [arg-type]

Pyright seems to understand this kind of narrowing well.

@asottile-sentry
Copy link

I opted for a clunky workaround using TypeIs though it would be nice if this were automatic

MyLiteral = Literal["a", "b"]

def is_my_literal(s: str) -> TypeIs[MyLiteral]:
    return s in ("a", "b")

@JelleZijlstra
Copy link
Member

This is similar to #3229 though that one doesn't ask for narrowing to Literal, possibly because the issue predates literal types.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

6 participants