-
-
Notifications
You must be signed in to change notification settings - Fork 367
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
Use TypeGuard
for has
in Python 3.10 and above
#997
Conversation
I don't see a direct use-case for this (since attrs classes should be statically known now) but I don't see the harm either. |
So what exactly does this do? |
It essentially makes Let's say your function takes a class (not an instance, but an actual class). You will type your argument as If we annotate Code example: def needs_attrs_class(cls: type[AttrsInstance]) -> None:
pass
def my_function(cls: type) -> None:
if has(cls):
needs_attrs_class(cls) # Mypy's gonna be unhappy, unless it knows `has` narrows the type of is argument |
It tells the type checker that if the condition is true then def is_dict_no_typeguard(value: object) -> bool:
return isinstance(value, dict)
def is_dict_typeguard(value: object) -> TypeGuard[dict]:
return isinstance(value, dict)
might_be_a_dict = ...
if is_dict_no_typeguard(might_be_a_dict):
"type of might_be_a_dict is object"
if is_dict_typeguard(might_be_a_dict):
"type of might_be_a_dict is dict"
|
Sounds cool! Any reason against it? If not: make it happen Tin. ;) |
src/attr/__init__.pyi
Outdated
@@ -470,7 +470,15 @@ def astuple( | |||
tuple_factory: Type[Sequence[Any]] = ..., | |||
retain_collection_types: bool = ..., | |||
) -> Tuple[Any, ...]: ... | |||
def has(cls: type) -> bool: ... | |||
|
|||
if sys.version_info >= (3, 10): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Would it be feasible to add a conditional import from typing-extensions?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You can only use one of a small number of constants like TYPE_CHECKING
and sys.version_info
to define types conditionally, try/except won't work, so I don't think so.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Right, but typing_extensions is not a dependency of attrs, so that'll error if typing_extensions is not installed.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Okay, it seems typeshed ships typing_extensions as part of the stdlib, so I guess we can actually do that.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done in fd2c819.
Would you mind coming up with a way to verify this works in https://github.com/python-attrs/attrs/blob/main/tests/typing_example.py ? |
There's a test in |
No, both would be great, since they work somewhat differently. Maybe |
Just so I understand this correctly: the import from typing_extensions only works, because we have the stubs in pyi files that are only touched by mypy which in turn brings in typing_extensions? IOW: if we'd use inline type hints, this would fall apart? |
That's right. The typeshed has import typing
if typing.TYPE_CHECKING:
from typing_extensions import TypeGuard
# Use TypeGuard either in quotes or with `from __future__ import annotations` |
Thanks! |
Since python-attrs/attrs#890 (≥ 22.1.0) `attrs.fields` is typed to accept a protocol. Since python-attrs/attrs#997 (≥ 22.2.0) `attrs.has` is a type-guard. Support both by removing the explicit error reporting and letting it fall through to the type stub. Fixes #15980.
Summary
Conditionally defines
has
usingTypeGuard
in Python 3.10+ in the stub file.xref: #987
Pull Request Check List
Our CI fails if coverage is not 100%.
.pyi
).tests/typing_example.py
.attr/__init__.pyi
, they've also been re-imported inattrs/__init__.pyi
.docs/api.rst
by hand.@attr.s()
have to be added by hand too.versionadded
,versionchanged
, ordeprecated
directives.Find the appropriate next version in our
__init__.py
file..rst
files is written using semantic newlines.changelog.d
.