Repository navigation
bpo-43766: Implement PEP 647 (User-Defined Type Guards) in typing.py #25282
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
Changes from 2 commits
afbd42b
8c994a5
493b18d
eb8dc6b
49964fa
fa736a7
5e4c5f7
481b618
7f3a113
d2aaf76
f015c82
6731b24
39e8d8e
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -933,6 +933,55 @@ These can be used as types in annotations using ``[]``, each having a unique syn | |
|
|
||
| .. versionadded:: 3.9 | ||
|
|
||
|
|
||
| .. data:: TypeGuard | ||
|
|
||
| Special typing form used to annotate the return type of a user-defined | ||
| type guard function. ``TypeGuard`` only accepts a single type argument. | ||
|
|
||
| ``TypeGuard`` aims to benefit *type narrowing* - a technique used by static | ||
| type checkers to determine a more precise type of an expression within a | ||
| program's code flow. Usually type narrowing is done by analyzing | ||
| conditional code flow and applying the narrowing to a block of code. The | ||
| conditional expression here is sometimes referred to as a "type guard". | ||
|
Fidget-Spinner marked this conversation as resolved.
Resolved
|
||
|
|
||
| Sometimes, a type guard can be a complex checking function. | ||
|
Fidget-Spinner marked this conversation as resolved.
Resolved
|
||
| These type guard functions require ``TypeGuard`` to narrow their input types | ||
|
Fidget-Spinner marked this conversation as resolved.
Resolved
|
||
| as the static type checker usually does not have enough information to | ||
| statically infer them. | ||
|
|
||
| A ``TypeGuard`` tells the static type checker that for a given function: | ||
|
Fidget-Spinner marked this conversation as resolved.
Resolved
|
||
| 1. The return value is a boolean. | ||
|
Fidget-Spinner marked this conversation as resolved.
Resolved
|
||
| 2. If the return value was "truthy", the type of the input to the | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Let's not get into truthiness here -- let's just say Separately, I wonder if we need to somehow distinguish more carefully between what happens in the static checker and at runtime. The checker makes the assumption that a
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Do you mean that for the current PEP, things like: def is_fake_str_list(val: List[object]) -> TypeGuard[List[str]]:
'''Liar!'''
return True
def func1(val: List[object]):
if is_str_list(val):
# Type of ``val`` is narrowed to List[str]
print(" ".join(val))
else:
# Type of ``val`` remains as List[object]
print("Not a list of strings!")
# Kaboom!
func([1,2,3])Fail at runtime instead of being caught at "compile" time? If so, I'm not sure if there's an easy way to fix that. One way would be for the static type checker to verify Here's some comments from the PEP that I agree with (though it's talking about why there isn't strict narrowing, not runtime vs static checkers' responsibilities):
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes, that's what I meant. And of course there's no easy fix for that. I just want the docs to not mislead the reader into thinking that that is somehow checked. |
||
| function is specified by the type inside ``TypeGuard``. | ||
|
Fidget-Spinner marked this conversation as resolved.
Resolved
|
||
|
|
||
| For example:: | ||
|
|
||
| def is_str_list(val: List[object]) -> TypeGuard[List[str]]: | ||
| '''Determines whether all objects in the list are strings''' | ||
| return all(isinstance(x, str) for x in val) | ||
|
|
||
| def func1(val: List[object]): | ||
| if is_str_list(val): | ||
| # Type of ``val`` is narrowed to List[str] | ||
| print(" ".join(val)) | ||
| else: | ||
| # Type of ``val`` remains as List[object] | ||
| print("Not a list of strings!") | ||
|
|
||
| In short, the form ``def foo(arg: TypeA) -> TypeGuard[TypeB]: ...``, | ||
| Means that if ``foo(arg)`` returned true, then ``arg`` narrows from | ||
|
Fidget-Spinner marked this conversation as resolved.
Resolved
|
||
| ``TypeA`` to ``TypeB``. | ||
|
Fidget-Spinner marked this conversation as resolved.
Resolved
|
||
|
|
||
| Return statements within a type guard function should return ``bool`` | ||
| values. | ||
|
Fidget-Spinner marked this conversation as resolved.
Resolved
|
||
|
|
||
| ``TypeGuard`` also works with type variables. For more information, see | ||
| :pep:`647` (User-Defined Type Guards). | ||
|
|
||
| .. versionadded:: 3.10 | ||
|
|
||
|
|
||
| Building generic types | ||
| """""""""""""""""""""" | ||
|
|
||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,2 @@ | ||
| Implement :pep:`647` in the :mod:`typing` module by adding | ||
| :data:`TypeGuard`. |
Uh oh!
There was an error while loading. Please reload this page.