-
-
Notifications
You must be signed in to change notification settings - Fork 3k
Phase 2 of async/await #1946
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
Phase 2 of async/await #1946
Changes from 1 commit
407503a
04e6e83
2834f73
31d0362
1340034
9f35170
4aa2541
662f38d
8be06db
637eaf4
a97872d
319d403
17bed84
712d1a4
dcf011b
6ffe63b
5052065
fc2fd88
a9e5911
c725a29
49e5a6f
637d381
839dc7d
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
Upgrade AwaitableGenerator to four parameters -- the last one preserves the original type, which we sometimes need.
- Loading branch information
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -314,7 +314,7 @@ def is_generator_return_type(self, typ: Type, is_coroutine: bool) -> bool: | |
except KeyError: | ||
return False | ||
agt = self.named_generic_type('typing.AwaitableGenerator', | ||
[AnyType(), AnyType(), AnyType()]) | ||
[AnyType(), AnyType(), AnyType(), AnyType()]) | ||
return is_equivalent(typ, agt) | ||
|
||
def get_generator_yield_type(self, return_type: Type, is_coroutine: bool) -> Type: | ||
|
@@ -360,7 +360,7 @@ def get_generator_receive_type(self, return_type: Type, is_coroutine: bool) -> T | |
# Awaitable, AwaitableGenerator: tc is Any. | ||
return AnyType() | ||
elif (return_type.type.fullname() in ('typing.Generator', 'typing.AwaitableGenerator') | ||
and len(return_type.args) == 3): | ||
and len(return_type.args) >= 3): | ||
# Generator: tc is args[1]. | ||
return return_type.args[1] | ||
else: | ||
|
@@ -385,7 +385,7 @@ def get_generator_return_type(self, return_type: Type, is_coroutine: bool) -> Ty | |
# Awaitable: tr is args[0]. | ||
return return_type.args[0] | ||
elif (return_type.type.fullname() in ('typing.Generator', 'typing.AwaitableGenerator') | ||
and len(return_type.args) == 3): | ||
and len(return_type.args) >= 3): | ||
# AwaitableGenerator, Generator: tr is args[2]. | ||
return return_type.args[2] | ||
else: | ||
|
@@ -555,7 +555,8 @@ def is_implicit_any(t: Type) -> bool: | |
ty = self.get_generator_yield_type(t, c) | ||
tc = self.get_generator_receive_type(t, c) | ||
tr = self.get_generator_return_type(t, c) | ||
ret_type = self.named_generic_type('typing.AwaitableGenerator', [ty, tc, tr]) | ||
ret_type = self.named_generic_type('typing.AwaitableGenerator', | ||
[ty, tc, tr, t]) | ||
typ = typ.copy_modified(ret_type=ret_type) | ||
defn.type = typ | ||
|
||
|
@@ -1891,6 +1892,11 @@ def visit_call_expr(self, e: CallExpr) -> Type: | |
return self.expr_checker.visit_call_expr(e) | ||
|
||
def visit_yield_from_expr(self, e: YieldFromExpr) -> Type: | ||
# NOTE: Whether `yield from` accepts an `async def` decorated | ||
# with `@types.coroutine` (or `@asyncio.coroutine`) depends on | ||
# whether the generator containing the `yield from` is itself | ||
# thus decorated. But it accepts a generator regardless of | ||
# how it's decorated. | ||
return_type = self.return_types[-1] | ||
subexpr_type = self.accept(e.expr, return_type) | ||
iter_type = None # type: Type | ||
|
@@ -1901,6 +1907,8 @@ def visit_yield_from_expr(self, e: YieldFromExpr) -> Type: | |
iter_type = AnyType() | ||
elif (isinstance(subexpr_type, Instance) and | ||
is_subtype(subexpr_type, self.named_type('typing.Iterable'))): | ||
if self.is_async_def(subexpr_type) and not self.has_coroutine_decorator(return_type): | ||
self.msg.yield_from_invalid_operand_type(subexpr_type, e) | ||
iter_method_type = self.expr_checker.analyze_external_member_access( | ||
'__iter__', | ||
subexpr_type, | ||
|
@@ -1911,7 +1919,8 @@ def visit_yield_from_expr(self, e: YieldFromExpr) -> Type: | |
iter_type, _ = self.expr_checker.check_call(iter_method_type, [], [], | ||
context=generic_generator_type) | ||
else: | ||
self.msg.yield_from_invalid_operand_type(subexpr_type, e) | ||
if not (self.is_async_def(subexpr_type) and self.has_coroutine_decorator(return_type)): | ||
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. Why is |
||
self.msg.yield_from_invalid_operand_type(subexpr_type, e) | ||
iter_type = AnyType() | ||
|
||
# Check that the iterator's item type matches the type yielded by the Generator function | ||
|
@@ -1938,6 +1947,16 @@ def visit_yield_from_expr(self, e: YieldFromExpr) -> Type: | |
else: | ||
return Void() | ||
|
||
def has_coroutine_decorator(self, t: Type) -> bool: | ||
"""Whether t came from a function decorated with `@coroutine`.""" | ||
return isinstance(t, Instance) and t.type.fullname() == 'typing.AwaitableGenerator' | ||
|
||
def is_async_def(self, t: Type) -> bool: | ||
"""Whether t came from a function defined using `async def`.""" | ||
if self.has_coroutine_decorator(t) and len(t.args) >= 4: | ||
t = t.args[3] | ||
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. Could you add a comment about what's going on here? 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. Responded to the code review and pushed a new version.
|
||
return isinstance(t, Instance) and t.type.fullname() == 'typing.Awaitable' | ||
|
||
def visit_member_expr(self, e: MemberExpr) -> Type: | ||
return self.expr_checker.visit_member_expr(e) | ||
|
||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -26,6 +26,7 @@ | |
|
||
U = TypeVar('U') | ||
V = TypeVar('V') | ||
S = TypeVar('S') | ||
|
||
class Container(Generic[T]): | ||
@abstractmethod | ||
|
@@ -61,7 +62,7 @@ class Awaitable(Generic[T]): | |
@abstractmethod | ||
def __await__(self) -> Generator[Any, Any, T]: pass | ||
|
||
class AwaitableGenerator(Generator[T, U, V], Awaitable[V], Generic[T, U, V]): | ||
class AwaitableGenerator(Generator[T, U, V], Awaitable[V], Generic[T, U, V, S]): | ||
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. Why is there an 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. Because this stub (despite its extension) is actually a stub. It matches the real 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. Oh okay. That makes sense. |
||
pass | ||
|
||
class AsyncIterable(Generic[T]): | ||
|
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.
Wouldn't it be simpler to just look at the fullname instead, like we do elsewhere?