This repository has been archived by the owner on Apr 26, 2024. It is now read-only.
-
-
Notifications
You must be signed in to change notification settings - Fork 2.1k
Improve type hints for attrs classes #16276
Merged
Merged
Changes from 1 commit
Commits
Show all changes
6 commits
Select commit
Hold shift + click to select a range
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -30,6 +30,7 @@ | |
Collection, | ||
Coroutine, | ||
Dict, | ||
Generator, | ||
Generic, | ||
Hashable, | ||
Iterable, | ||
|
@@ -718,30 +719,25 @@ def failure_cb(val: Failure) -> None: | |
return new_d | ||
|
||
|
||
# This class can't be generic because it uses slots with attrs. | ||
# See: https://github.com/python-attrs/attrs/issues/313 | ||
@attr.s(slots=True, frozen=True, auto_attribs=True) | ||
class DoneAwaitable: # should be: Generic[R] | ||
class DoneAwaitable(Awaitable[R]): | ||
"""Simple awaitable that returns the provided value.""" | ||
|
||
value: Any # should be: R | ||
value: R | ||
|
||
def __await__(self) -> Any: | ||
return self | ||
|
||
def __iter__(self) -> "DoneAwaitable": | ||
return self | ||
|
||
def __next__(self) -> None: | ||
raise StopIteration(self.value) | ||
def __await__(self) -> Generator[Any, None, R]: | ||
yield None | ||
return self.value | ||
Comment on lines
+723
to
+730
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. I couldn't resist trying to simplify this. Needs careful checking though, I find all this confusing. C.f. a0aef0b |
||
|
||
|
||
def maybe_awaitable(value: Union[Awaitable[R], R]) -> Awaitable[R]: | ||
"""Convert a value to an awaitable if not already an awaitable.""" | ||
if inspect.isawaitable(value): | ||
assert isinstance(value, Awaitable) | ||
return value | ||
|
||
# For some reason mypy doesn't deduce that value is not Awaitable here, even though | ||
# inspect.isawaitable returns a TypeGuard. | ||
assert not isinstance(value, Awaitable) | ||
return DoneAwaitable(value) | ||
|
||
|
||
|
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
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.
Not sure it matters, but is the yield less efficient?
I'm not 100% sure I understand removing
__iter__
and__next__
.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.
Err, being honest it's because I couldn't work out how to annotate the
__iter__
and__next__
in a way that mypy was happy with. Can dig up the errors if you'd like.I think this is equivalent though:
__await__
is supposed to return an iterator__next__
__iter__
redundant? Might have needed it before theawait
syntax added in Py 3.5?)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.
Oh, I think it wasn't redundant because we were returning ourself, now
__await__
itself is an iterator?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.
This explains why
__next__
was needed before my change. But I assert that__iter__
is redundant these days:AFAICS you used to need
__iter__
so that you couldyield from
, back in the days whenasync
wasn't a keyword (a = yield from b
instead ofa = await b
.) If I try to use my class in an old-style coroutine:But if we define
__iter__
: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.
(Tbh we could probably use
asyncio.Future
instead of writing our own)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.
Alas,
Future
docs say: