@@ -265,54 +265,67 @@ def check_overlapping_overloads(self, defn: OverloadedFuncDef) -> None:
265265 # in PEP 492 and only available in Python >= 3.5.
266266 #
267267 # Classic generators can be parameterized with three types:
268- # - ty is the yield type (the type of y in `yield y`)
269- # - ts is the type received by yield (the type of s in `s = yield`)
270- # (it's named `ts` after `send()`, since `tr` is `return`).
271- # - tr is the return type (the type of r in `return r`)
268+ # - ty is the Yield type (the type of y in `yield y`)
269+ # - tc is the type reCeived by yield (the type of c in `c = yield`).
270+ # - tr is the Return type (the type of r in `return r`)
272271 #
273272 # A classic generator must define a return type that's either
274- # `Generator[ty, ts , tr]`, Iterator[ty], or Iterable[ty] (or
275- # object or Any). If ts /tr are not given, both are Void.
273+ # `Generator[ty, tc , tr]`, Iterator[ty], or Iterable[ty] (or
274+ # object or Any). If tc /tr are not given, both are Void.
276275 #
277276 # A coroutine must define a return type corresponding to tr; the
278277 # other two are unconstrained. The "external" return type (seen
279278 # by the caller) is Awaitable[tr].
280279 #
280+ # In addition, there's the synthetic type AwaitableGenerator: it
281+ # inherits from both Awaitable and Generator and can be used both
282+ # in `yield from` and in `await`. This type is set automatically
283+ # for functions decorated with `@types.coroutine` or
284+ # `@asyncio.coroutine`. Its single parameter corresponds to tr.
285+ #
281286 # There are several useful methods, each taking a type t and a
282287 # flag c indicating whether it's for a generator or coroutine:
283288 #
284289 # - is_generator_return_type(t, c) returns whether t is a Generator,
285- # Iterator, Iterable (if not c), or Awaitable (if c).
290+ # Iterator, Iterable (if not c), or Awaitable (if c), or
291+ # AwaitableGenerator (regardless of c).
286292 # - get_generator_yield_type(t, c) returns ty.
287- # - get_generator_receive_type(t, c) returns ts .
293+ # - get_generator_receive_type(t, c) returns tc .
288294 # - get_generator_return_type(t, c) returns tr.
289295
290296 def is_generator_return_type (self , typ : Type , is_coroutine : bool ) -> bool :
291297 """Is `typ` a valid type for a generator/coroutine?
292298
293- True if either Generator or Awaitable is a supertype of `typ`.
299+ True if `typ` is a *supertype* of Generator or Awaitable.
300+ Also true it it's *exactly* AwaitableGenerator (modulo type parameters).
294301 """
295302 if is_coroutine :
303+ # This means we're in Python 3.5 or later.
296304 at = self .named_generic_type ('typing.Awaitable' , [AnyType ()])
297- return is_subtype (at , typ )
305+ if is_subtype (at , typ ):
306+ return True
298307 else :
299308 gt = self .named_generic_type ('typing.Generator' , [AnyType (), AnyType (), AnyType ()])
300- return is_subtype (gt , typ )
309+ if is_subtype (gt , typ ):
310+ return True
311+ return isinstance (typ , Instance ) and typ .type .fullname () == 'typing.AwaitableGenerator'
301312
302313 def get_generator_yield_type (self , return_type : Type , is_coroutine : bool ) -> Type :
303314 """Given the declared return type of a generator (t), return the type it yields (ty)."""
304315 if isinstance (return_type , AnyType ):
305316 return AnyType ()
306317 elif not self .is_generator_return_type (return_type , is_coroutine ):
307- # If the function doesn't have a proper Generator (or superclass) return type, anything
308- # is permissible.
318+ # If the function doesn't have a proper Generator (or
319+ # Awaitable) return type, anything is permissible.
309320 return AnyType ()
310321 elif not isinstance (return_type , Instance ):
311322 # Same as above, but written as a separate branch so the typechecker can understand.
312323 return AnyType ()
313324 elif return_type .type .fullname () == 'typing.Awaitable' :
325+ # Awaitable: ty is Any.
314326 return AnyType ()
315327 elif return_type .args :
328+ # AwaitableGenerator, Generator, Iterator, or Iterable; ty is args[0].
316329 ret_type = return_type .args [0 ]
317330 # TODO not best fix, better have dedicated yield token
318331 if isinstance (ret_type , NoneTyp ):
@@ -324,33 +337,31 @@ def get_generator_yield_type(self, return_type: Type, is_coroutine: bool) -> Typ
324337 else :
325338 # If the function's declared supertype of Generator has no type
326339 # parameters (i.e. is `object`), then the yielded values can't
327- # be accessed so any type is acceptable.
340+ # be accessed so any type is acceptable. IOW, ty is Any.
341+ # (However, see https://github.com/python/mypy/issues/1933)
328342 return AnyType ()
329343
330344 def get_generator_receive_type (self , return_type : Type , is_coroutine : bool ) -> Type :
331- """Given a declared generator return type (t), return the type its yield receives (ts )."""
345+ """Given a declared generator return type (t), return the type its yield receives (tc )."""
332346 if isinstance (return_type , AnyType ):
333347 return AnyType ()
334348 elif not self .is_generator_return_type (return_type , is_coroutine ):
335- # If the function doesn't have a proper Generator (or superclass) return type, anything
336- # is permissible.
349+ # If the function doesn't have a proper Generator (or
350+ # Awaitable) return type, anything is permissible.
337351 return AnyType ()
338352 elif not isinstance (return_type , Instance ):
339353 # Same as above, but written as a separate branch so the typechecker can understand.
340354 return AnyType ()
341- elif return_type .type .fullname () == 'typing.Generator' :
342- # Generator is one of the two types which specify the type of values it can receive.
343- if len (return_type .args ) == 3 :
344- return return_type .args [1 ]
345- else :
346- return AnyType ()
347355 elif return_type .type .fullname () == 'typing.Awaitable' :
348- # Awaitable is one of the two types which specify the type of values it can receive.
349- # According to the stub this is always `Any`.
356+ # Awaitable, AwaitableGenerator: tc is Any.
350357 return AnyType ()
358+ elif (return_type .type .fullname () in ('typing.Generator' , 'typing.AwaitableGenerator' )
359+ and len (return_type .args ) >= 3 ):
360+ # Generator: tc is args[1].
361+ return return_type .args [1 ]
351362 else :
352363 # `return_type` is a supertype of Generator, so callers won't be able to send it
353- # values.
364+ # values. IOW, tc is None.
354365 if experiments .STRICT_OPTIONAL :
355366 return NoneTyp (is_ret_type = True )
356367 else :
@@ -361,29 +372,21 @@ def get_generator_return_type(self, return_type: Type, is_coroutine: bool) -> Ty
361372 if isinstance (return_type , AnyType ):
362373 return AnyType ()
363374 elif not self .is_generator_return_type (return_type , is_coroutine ):
364- # If the function doesn't have a proper Generator (or superclass) return type, anything
365- # is permissible.
375+ # If the function doesn't have a proper Generator (or
376+ # Awaitable) return type, anything is permissible.
366377 return AnyType ()
367378 elif not isinstance (return_type , Instance ):
368379 # Same as above, but written as a separate branch so the typechecker can understand.
369380 return AnyType ()
370- elif return_type .type .fullname () == 'typing.Generator' :
371- # Generator is one of the two types which specify the type of values it returns into
372- # `yield from` expressions (using a `return` statement).
373- if len (return_type .args ) == 3 :
374- return return_type .args [2 ]
375- else :
376- return AnyType ()
377- elif return_type .type .fullname () == 'typing.Awaitable' :
378- # Awaitable is the other type which specifies the type of values it returns into
379- # `yield from` expressions (using `return`).
380- if len (return_type .args ) == 1 :
381- return return_type .args [0 ]
382- else :
383- return AnyType ()
381+ elif return_type .type .fullname () == 'typing.Awaitable' and len (return_type .args ) == 1 :
382+ # Awaitable: tr is args[0].
383+ return return_type .args [0 ]
384+ elif (return_type .type .fullname () in ('typing.Generator' , 'typing.AwaitableGenerator' )
385+ and len (return_type .args ) >= 3 ):
386+ # AwaitableGenerator, Generator: tr is args[2].
387+ return return_type .args [2 ]
384388 else :
385- # `return_type` is supertype of Generator, so callers won't be able to see the return
386- # type when used in a `yield from` expression.
389+ # Supertype of Generator (Iterator, Iterable, object): tr is any.
387390 return AnyType ()
388391
389392 def check_awaitable_expr (self , t : Type , ctx : Context , msg : str ) -> Type :
@@ -540,6 +543,20 @@ def is_implicit_any(t: Type) -> bool:
540543 if not isinstance (typ .ret_type .args [2 ], (Void , NoneTyp , AnyType )):
541544 self .fail (messages .INVALID_GENERATOR_RETURN_ITEM_TYPE , typ )
542545
546+ # Fix the type if decorated with `@types.coroutine` or `@asyncio.coroutine`.
547+ if defn .is_awaitable_coroutine :
548+ # Update the return type to AwaitableGenerator.
549+ # (This doesn't exist in typing.py, only in typing.pyi.)
550+ t = typ .ret_type
551+ c = defn .is_coroutine
552+ ty = self .get_generator_yield_type (t , c )
553+ tc = self .get_generator_receive_type (t , c )
554+ tr = self .get_generator_return_type (t , c )
555+ ret_type = self .named_generic_type ('typing.AwaitableGenerator' ,
556+ [ty , tc , tr , t ])
557+ typ = typ .copy_modified (ret_type = ret_type )
558+ defn .type = typ
559+
543560 # Push return type.
544561 self .return_types .append (typ .ret_type )
545562
@@ -1872,6 +1889,11 @@ def visit_call_expr(self, e: CallExpr) -> Type:
18721889 return self .expr_checker .visit_call_expr (e )
18731890
18741891 def visit_yield_from_expr (self , e : YieldFromExpr ) -> Type :
1892+ # NOTE: Whether `yield from` accepts an `async def` decorated
1893+ # with `@types.coroutine` (or `@asyncio.coroutine`) depends on
1894+ # whether the generator containing the `yield from` is itself
1895+ # thus decorated. But it accepts a generator regardless of
1896+ # how it's decorated.
18751897 return_type = self .return_types [- 1 ]
18761898 subexpr_type = self .accept (e .expr , return_type )
18771899 iter_type = None # type: Type
@@ -1882,6 +1904,8 @@ def visit_yield_from_expr(self, e: YieldFromExpr) -> Type:
18821904 iter_type = AnyType ()
18831905 elif (isinstance (subexpr_type , Instance ) and
18841906 is_subtype (subexpr_type , self .named_type ('typing.Iterable' ))):
1907+ if self .is_async_def (subexpr_type ) and not self .has_coroutine_decorator (return_type ):
1908+ self .msg .yield_from_invalid_operand_type (subexpr_type , e )
18851909 iter_method_type = self .expr_checker .analyze_external_member_access (
18861910 '__iter__' ,
18871911 subexpr_type ,
@@ -1892,8 +1916,12 @@ def visit_yield_from_expr(self, e: YieldFromExpr) -> Type:
18921916 iter_type , _ = self .expr_checker .check_call (iter_method_type , [], [],
18931917 context = generic_generator_type )
18941918 else :
1895- self .msg .yield_from_invalid_operand_type (subexpr_type , e )
1896- iter_type = AnyType ()
1919+ if not (self .is_async_def (subexpr_type ) and self .has_coroutine_decorator (return_type )):
1920+ self .msg .yield_from_invalid_operand_type (subexpr_type , e )
1921+ iter_type = AnyType ()
1922+ else :
1923+ iter_type = self .check_awaitable_expr (subexpr_type , e ,
1924+ messages .INCOMPATIBLE_TYPES_IN_YIELD_FROM )
18971925
18981926 # Check that the iterator's item type matches the type yielded by the Generator function
18991927 # containing this `yield from` expression.
@@ -1919,6 +1947,30 @@ def visit_yield_from_expr(self, e: YieldFromExpr) -> Type:
19191947 else :
19201948 return Void ()
19211949
1950+ def has_coroutine_decorator (self , t : Type ) -> bool :
1951+ """Whether t came from a function decorated with `@coroutine`."""
1952+ return isinstance (t , Instance ) and t .type .fullname () == 'typing.AwaitableGenerator'
1953+
1954+ def is_async_def (self , t : Type ) -> bool :
1955+ """Whether t came from a function defined using `async def`."""
1956+ # In check_func_def(), when we see a function decorated with
1957+ # `@typing.coroutine` or `@async.coroutine`, we change the
1958+ # return type to typing.AwaitableGenerator[...], so that its
1959+ # type is compatible with either Generator or Awaitable.
1960+ # But for the check here we need to know whether the original
1961+ # function (before decoration) was an `async def`. The
1962+ # AwaitableGenerator type conveniently preserves the original
1963+ # type as its 4th parameter (3rd when using 0-origin indexing
1964+ # :-), so that we can recover that information here.
1965+ # (We really need to see whether the original, undecorated
1966+ # function was an `async def`, which is orthogonal to its
1967+ # decorations.)
1968+ if (isinstance (t , Instance )
1969+ and t .type .fullname () == 'typing.AwaitableGenerator'
1970+ and len (t .args ) >= 4 ):
1971+ t = t .args [3 ]
1972+ return isinstance (t , Instance ) and t .type .fullname () == 'typing.Awaitable'
1973+
19221974 def visit_member_expr (self , e : MemberExpr ) -> Type :
19231975 return self .expr_checker .visit_member_expr (e )
19241976
0 commit comments