@@ -265,54 +265,67 @@ def check_overlapping_overloads(self, defn: OverloadedFuncDef) -> None:
265
265
# in PEP 492 and only available in Python >= 3.5.
266
266
#
267
267
# 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`)
272
271
#
273
272
# 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.
276
275
#
277
276
# A coroutine must define a return type corresponding to tr; the
278
277
# other two are unconstrained. The "external" return type (seen
279
278
# by the caller) is Awaitable[tr].
280
279
#
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
+ #
281
286
# There are several useful methods, each taking a type t and a
282
287
# flag c indicating whether it's for a generator or coroutine:
283
288
#
284
289
# - 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).
286
292
# - 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 .
288
294
# - get_generator_return_type(t, c) returns tr.
289
295
290
296
def is_generator_return_type (self , typ : Type , is_coroutine : bool ) -> bool :
291
297
"""Is `typ` a valid type for a generator/coroutine?
292
298
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).
294
301
"""
295
302
if is_coroutine :
303
+ # This means we're in Python 3.5 or later.
296
304
at = self .named_generic_type ('typing.Awaitable' , [AnyType ()])
297
- return is_subtype (at , typ )
305
+ if is_subtype (at , typ ):
306
+ return True
298
307
else :
299
308
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'
301
312
302
313
def get_generator_yield_type (self , return_type : Type , is_coroutine : bool ) -> Type :
303
314
"""Given the declared return type of a generator (t), return the type it yields (ty)."""
304
315
if isinstance (return_type , AnyType ):
305
316
return AnyType ()
306
317
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.
309
320
return AnyType ()
310
321
elif not isinstance (return_type , Instance ):
311
322
# Same as above, but written as a separate branch so the typechecker can understand.
312
323
return AnyType ()
313
324
elif return_type .type .fullname () == 'typing.Awaitable' :
325
+ # Awaitable: ty is Any.
314
326
return AnyType ()
315
327
elif return_type .args :
328
+ # AwaitableGenerator, Generator, Iterator, or Iterable; ty is args[0].
316
329
ret_type = return_type .args [0 ]
317
330
# TODO not best fix, better have dedicated yield token
318
331
if isinstance (ret_type , NoneTyp ):
@@ -324,33 +337,31 @@ def get_generator_yield_type(self, return_type: Type, is_coroutine: bool) -> Typ
324
337
else :
325
338
# If the function's declared supertype of Generator has no type
326
339
# 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)
328
342
return AnyType ()
329
343
330
344
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 )."""
332
346
if isinstance (return_type , AnyType ):
333
347
return AnyType ()
334
348
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.
337
351
return AnyType ()
338
352
elif not isinstance (return_type , Instance ):
339
353
# Same as above, but written as a separate branch so the typechecker can understand.
340
354
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 ()
347
355
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.
350
357
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 ]
351
362
else :
352
363
# `return_type` is a supertype of Generator, so callers won't be able to send it
353
- # values.
364
+ # values. IOW, tc is None.
354
365
if experiments .STRICT_OPTIONAL :
355
366
return NoneTyp (is_ret_type = True )
356
367
else :
@@ -361,29 +372,21 @@ def get_generator_return_type(self, return_type: Type, is_coroutine: bool) -> Ty
361
372
if isinstance (return_type , AnyType ):
362
373
return AnyType ()
363
374
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.
366
377
return AnyType ()
367
378
elif not isinstance (return_type , Instance ):
368
379
# Same as above, but written as a separate branch so the typechecker can understand.
369
380
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 ]
384
388
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.
387
390
return AnyType ()
388
391
389
392
def check_awaitable_expr (self , t : Type , ctx : Context , msg : str ) -> Type :
@@ -540,6 +543,20 @@ def is_implicit_any(t: Type) -> bool:
540
543
if not isinstance (typ .ret_type .args [2 ], (Void , NoneTyp , AnyType )):
541
544
self .fail (messages .INVALID_GENERATOR_RETURN_ITEM_TYPE , typ )
542
545
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
+
543
560
# Push return type.
544
561
self .return_types .append (typ .ret_type )
545
562
@@ -1872,6 +1889,11 @@ def visit_call_expr(self, e: CallExpr) -> Type:
1872
1889
return self .expr_checker .visit_call_expr (e )
1873
1890
1874
1891
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.
1875
1897
return_type = self .return_types [- 1 ]
1876
1898
subexpr_type = self .accept (e .expr , return_type )
1877
1899
iter_type = None # type: Type
@@ -1882,6 +1904,8 @@ def visit_yield_from_expr(self, e: YieldFromExpr) -> Type:
1882
1904
iter_type = AnyType ()
1883
1905
elif (isinstance (subexpr_type , Instance ) and
1884
1906
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 )
1885
1909
iter_method_type = self .expr_checker .analyze_external_member_access (
1886
1910
'__iter__' ,
1887
1911
subexpr_type ,
@@ -1892,8 +1916,12 @@ def visit_yield_from_expr(self, e: YieldFromExpr) -> Type:
1892
1916
iter_type , _ = self .expr_checker .check_call (iter_method_type , [], [],
1893
1917
context = generic_generator_type )
1894
1918
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 )
1897
1925
1898
1926
# Check that the iterator's item type matches the type yielded by the Generator function
1899
1927
# containing this `yield from` expression.
@@ -1919,6 +1947,30 @@ def visit_yield_from_expr(self, e: YieldFromExpr) -> Type:
1919
1947
else :
1920
1948
return Void ()
1921
1949
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
+
1922
1974
def visit_member_expr (self , e : MemberExpr ) -> Type :
1923
1975
return self .expr_checker .visit_member_expr (e )
1924
1976
0 commit comments