Skip to content
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

Fix union of generators having no return type #15168

Closed
wants to merge 15 commits into from
Closed
5 changes: 3 additions & 2 deletions mypy/checker.py
Original file line number Diff line number Diff line change
Expand Up @@ -952,8 +952,9 @@ def get_generator_return_type(self, return_type: Type, is_coroutine: bool) -> Ty
# AwaitableGenerator, Generator: tr is args[2].
return return_type.args[2]
else:
# Supertype of Generator (Iterator, Iterable, object): tr is any.
return AnyType(TypeOfAny.special_form)
# We have a supertype of Generator (Iterator, Iterable, object)
# Treat `Iterator[X]` as a shorthand for `Generator[X, Any, None]`.
return NoneType()

def visit_func_def(self, defn: FuncDef) -> None:
if not self.recurse_into_functions:
Expand Down
12 changes: 1 addition & 11 deletions mypy/checkexpr.py
Original file line number Diff line number Diff line change
Expand Up @@ -5232,17 +5232,7 @@ def visit_yield_from_expr(self, e: YieldFromExpr, allow_none_return: bool = Fals

# Determine the type of the entire yield from expression.
iter_type = get_proper_type(iter_type)
if isinstance(iter_type, Instance) and iter_type.type.fullname == "typing.Generator":
expr_type = self.chk.get_generator_return_type(iter_type, False)
else:
# Non-Generators don't return anything from `yield from` expressions.
# However special-case Any (which might be produced by an error).
actual_item_type = get_proper_type(actual_item_type)
if isinstance(actual_item_type, AnyType):
expr_type = AnyType(TypeOfAny.from_another_any, source_any=actual_item_type)
else:
# Treat `Iterator[X]` as a shorthand for `Generator[X, None, Any]`.
expr_type = NoneType()
expr_type = self.chk.get_generator_return_type(iter_type, False)

if not allow_none_return and isinstance(get_proper_type(expr_type), NoneType):
self.chk.msg.does_not_return_value(None, e)
Expand Down
1 change: 0 additions & 1 deletion mypyc/test-data/run-generators.test
Original file line number Diff line number Diff line change
Expand Up @@ -263,7 +263,6 @@ def generator() -> Iterable[int]:
print('caught exception with value ' + s)
else:
print('caught exception without value')
return 0

def no_except() -> Iterable[int]:
yield 1
Expand Down
28 changes: 27 additions & 1 deletion test-data/unit/check-statements.test
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ def f() -> Generator[int, None, None]:
from typing import Iterator
def f() -> Iterator[int]:
yield 1
return "foo"
return "foo" # E: No return value expected
[out]


Expand Down Expand Up @@ -2195,6 +2195,32 @@ class B: pass
def foo(x: int) -> Union[Generator[A, None, None], Generator[B, None, None]]:
yield x # E: Incompatible types in "yield" (actual type "int", expected type "Union[A, B]")

[case testReturnTypeOfGeneratorUnion]
from typing import Generator, Union

class T: pass

def foo(arg: Union[Generator[int, None, T], Generator[str, None, T]]) -> Generator[Union[int, str], None, T]:
omarsilva1 marked this conversation as resolved.
Show resolved Hide resolved
return (yield from arg)

[case testYieldFromError]
from typing import Generator, Union

class T: pass
class R: pass

def foo(arg: Union[T, R]) -> Generator[Union[int, str], None, T]:
omarsilva1 marked this conversation as resolved.
Show resolved Hide resolved
return (yield from arg) # E: "yield from" can't be applied to "Union[T, R]"

[case testUnionOfGeneratorWithIterableStr]
from typing import Generator, Union, Iterable

class T: pass

def foo(arg: Union[Generator[int, None, T], Iterable[str]]) -> Generator[Union[int, str], None, T]:
return (yield from arg)
[builtins fixtures/tuple.pyi]

[case testNoCrashOnStarRightHandSide]
x = *(1, 2, 3) # E: Can use starred expression only as assignment target
[builtins fixtures/tuple.pyi]
Expand Down
Loading