8000 Phase 2 of async/await by gvanrossum · Pull Request #1946 · python/mypy · GitHub
[go: up one dir, main page]

Skip to content

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

Merged
merged 23 commits into from
Aug 3, 2016
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
Rewrite the get_generator_*_type() functions (again).
Mostly streamlined the comments, but also decided that return in a
generator not explicitly declared as returning Any or Generator should
be None.
  • Loading branch information
Guido van Rossum committed Aug 3, 2016
commit 2834f730af005df0d61aeb2ebc8ffe68a3b10f7a
67 changes: 30 additions & 37 deletions mypy/checker.py
Original file line number Diff line number Diff line change
Expand Up @@ -278,17 +278,22 @@ def check_overlapping_overloads(self, defn: OverloadedFuncDef) -> None:
# other two are unconstrained. The "external" return type (seen
# by the caller) is Awaitable[tr].
#
# In addition, there's the synthetic type AwaitableGenerator: it
# inherits from both Awaitable and Generator and can be used both
# in `yield from` and in `await`. This type is set automatically
# for functions decorated with `@types.coroutine` or
# `@asyncio.coroutine`. Its single parameter corresponds to tr.
#
# There are several useful methods, each taking a type t and a
# flag c indicating whether it's for a generator or coroutine:
#
# - is_generator_return_type(t, c) returns whether t is a Generator,
# Iterator, Iterable (if not c), or Awaitable (if c).
# Iterator, Iterable (if not c), or Awaitable (if c), or
# AwaitableGenerator (regardless of c).
# - get_generator_yield_type(t, c) returns ty.
# - get_generator_receive_type(t, c) returns ts.
# - get_generator_return_type(t, c) returns tr.

# XXX Describe how AwaitableGenerator fits into this.

def is_generator_return_type(self, typ: Type, is_coroutine: bool) -> bool:
"""Is `typ` a valid type for a generator/coroutine?

Expand All @@ -311,16 +316,17 @@ def get_generator_yield_type(self, return_type: Type, is_coroutine: bool) -> Typ
if isinstance(return_type, AnyType):
return AnyType()
elif not self.is_generator_return_type(return_type, is_coroutine):
# If the function doesn't have a proper Generator (or superclass) return type, anything
# is permissible.
# If the function doesn't have a proper Generator (or
# Awaitable) return type, anything is permissible.
return AnyType()
elif not isinstance(return_type, Instance):
# Same as above, but written as a separate branch so the typechecker can understand.
return AnyType()
elif return_type.type.fullname() in ('typing.Awaitable', 'typing.AwaitableGenerator'):
# Awaitable, AwaitableGenerator: ty is Any.
return AnyType()
elif return_type.args:
8000 # It must be Generator.
# Generator, Iterator, or Iterable; ty is args[0].
ret_type = return_type.args[0]
# TODO not best fix, better have dedicated yield token
if isinstance(ret_type, NoneTyp):
Expand All @@ -330,34 +336,28 @@ def get_generator_yield_type(self, return_type: Type, is_coroutine: bool) -> Typ
return Void()
return ret_type
else:
# If the function's declared supertype of Generator has no type
# parameters (i.e. is `object`), then the yielded values can't
# be accessed so any type is acceptable.
# object: ty is Any.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it's worth keeping the note that this is Any because the yielded values can't be accessed anywhere.

return AnyType()

def get_generator_receive_type(self, return_type: Type, is_coroutine: bool) -> Type:
"""Given a declared generator return type (t), return the type its yield receives (ts)."""
if isinstance(return_type, AnyType):
return AnyType()
elif not self.is_generator_return_type(return_type, is_coroutine):
# If the function doesn't have a proper Generator (or superclass) return type, anything
# is permissible.
# If the function doesn't have a proper Generator (or
# Awaitable) return type, anything is permissible.
return AnyType()
elif not isinstance(return_type, Instance):
# Same as above, but written as a separate branch so the typechecker can understand.
return AnyType()
elif return_type.type.fullname() == 'typing.Generator':
# Generator is one of the three types which specify the type of values it can receive.
if len(return_type.args) == 3:
return return_type.args[1]
else:
return AnyType()
elif return_type.type.fullname() in ('typing.Awaitable', 'typing.AwaitableGenerator'):
# For these it is always Any.
# Awaitable, AwaitableGenerator: ts is Any.
return AnyType()
elif return_type.type.fullname() == 'typing.Generator' and len(return_type.args) == 3:
# Generator: ts is args[1].
return return_type.args[1]
else:
# `return_type` is a supertype of Generator, so callers won't be able to send it
# values.
# Supertype of Generator (Iterator, Iterable, object), ts is None.
if experiments.STRICT_OPTIONAL:
return NoneTyp(is_ret_type=True)
else:
Expand All @@ -368,28 +368,21 @@ def get_generator_return_type(self, return_type: Type, is_coroutine: bool) -> Ty
if isinstance(return_type, AnyType):
return AnyType()
elif not self.is_generator_return_type(return_type, is_coroutine):
# If the function doesn't have a proper Generator (or superclass) return type, anything
# is permissible.
# If the function doesn't have a proper Generator (or
# Awaitable) return type, anything is permissible.
return AnyType()
elif not isinstance(return_type, Instance):
# Same as above, but written as a separate branch so the typechecker can understand.
return AnyType()
elif return_type.type.fullname() == 'typing.Generator':
# Generator is one of the two types which specify the type of values it returns into
# `yield from` expressions (using a `return` statement).
if len(return_type.args) == 3:
return return_type.args[2]
else:
return AnyType()
elif return_type.type.fullname() in ('typing.Awaitable', 'typing.AwaitableGenerator'):
# These are the other two.
if len(return_type.args) == 1:
return return_type.args[0]
else:
return AnyType()
elif (return_type.type.fullname() in ('typing.Awaitable', 'typing.AwaitableGenerator')
and len(return_type.args) == 1):
# Awaitable, AwaitableGenerator: tr is args[0].
return return_type.args[0]
elif return_type.type.fullname() == 'typing.Generator' and len(return_type.args) == 3:
# Generator: tr is args[2].
return return_type.args[2]
else:
# `return_type` is supertype of Generator, 8000 so callers won't be able to see the return
# type when used in a `yield from` expression.
# Supertype of Generator (Iterator, Iterable, object): tr is any.
return AnyType()

def check_awaitable_expr(self, t: Type, ctx: Context, msg: str) -> Type:
Expand Down
1 change: 1 addition & 0 deletions test-data/unit/check-statements.test
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,7 @@ def f() -> Iterator[int]:
return "foo"
[out]


-- If statement
-- ------------

Expand Down
0