-
Notifications
You must be signed in to change notification settings - Fork 1.5k
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
Exhaustiveness checks for patten matching #2569
Comments
Structural pattern matching has many use cases that don't involve exhaustive checks, so I don't think it makes sense to assume that every use of a The same is true for if/elif/else chains. In that case, the recommended approach when you want to validate exhaustive checks is to use an from typing import NoReturn
def assert_never(x: NoReturn) -> NoReturn:
assert False, "Unhandled type: {}".format(type(x).__name__)
IntOrStr = int | str
def handle(x: IntOrStr) -> None:
if isinstance(x, str):
print(f"Handled {x}")
else:
assert_never(x) # Error: "int" is incompatible with "NoReturn" This works because If you want a static type checker enforce exhaustiveness within a def handle_not_exhaustive(x : IntOrStr) -> None:
match x:
case str(s):
print(f"Handled {s}")
case _:
assert_never(x) # Error: "int" is incompatible with "NoReturn"
def handle_exhaustive(x: IntOrStr) -> None:
match x:
case str(s):
print(f"Handled {s}")
case int(s):
print(f"Handled {s}")
case _:
assert_never(x) # No error |
That's fair. The issue I have with that approach is that it requires users to remember to add a catch-all case to every |
Yeah, as I said, there are many legitimate uses for |
Where is match useful in place of if in that case? If there is no way to have implicit exhaustiveness checks for match statements then I'm curious to know where they help versus using if with the same pattern. |
Match statements provide a different (more concise) syntax that some developers prefer over if/elif/else statements. You can generally use them interchangeably and for the same purposes. |
Thanks - and thank you generally for your prompt responses to issues and feature requests on here. It's very appreciated! |
PEP 622 seems to suggest that type checkers should enforce exhaustiveness checks with pattern matching: https://www.python.org/dev/peps/pep-0622/#exhaustiveness-checks. Are there no plans for pyright to support this beyond the |
Pyright does provide the type narrowing support required for enforcing exhaustiveness checks, but you need to specify whether exhaustiveness is what you intend. There are many legitimate uses of match that do not involve exhaustive pattern matching, so it wouldn't be appropriate to assume that exhaustive matching is intended in every case. There are a few ways to specify that exhaustive matching is intended. The first example in PEP 622 that you linked to is already covered by the return type check in pyright. In other cases (such as in the second example), you can use the |
I made a counter-argument in the same thread: python/mypy#12010 (comment) (Btw, I love PyRight - thank you! ❤️ ) |
There seems to be sufficient interest in enforcing exhaustive matching, so I've added a new diagnostic check ("reportMatchNotExhaustive") that emits a diagnostic in cases where exhaustion cannot be proven through static analysis. The check is off by default with "basic" type checking mode but on by default with "strict" typing checking. This will be included in the next release of pyright. |
Thank you @erictraut! |
Thank you! |
This is included in pyright 1.1.215, which I just published. It will also be included in the next release of pylance. |
Hi @erictraut , I wanted to follow up and thank you for implementing this. I use the 3.10 |
|
Is it possible to enable this check for individual match statements? If not, would it be useful to add? Something like: # pyright: exhaustive-match
match foo:
... For those that don't want to enable this check globally, this seems much nicer than using |
FYI, from typing_extensions import assert_never |
Noticed this while documenting another project; enums need the value to be NoReturn to create type exhaustion. Variants still need to be specialcased upstream for this to work, though. Reference microsoft/pyright#2569
I wrote up my positive experience with this check and made a repo with some demo cases to play with: |
Is your feature request related to a problem? Please describe.
I would like to be able to be able to enable an error when a
match
does not cover all cases.Describe the solution you'd like
I would like an option to generate an error when a
match
statement is not exhaustive. For example, thismatch
statement would error with such an option enabled because theint
case is not handled.The text was updated successfully, but these errors were encountered: