From 5350c0e927493d9c6f6c2facb9edbe281dcc03b1 Mon Sep 17 00:00:00 2001 From: hauntsaninja Date: Fri, 29 Dec 2023 15:54:09 -0600 Subject: [PATCH 1/7] fix --- mypy/checker.py | 5 +++-- mypy/checkexpr.py | 12 +----------- 2 files changed, 4 insertions(+), 13 deletions(-) diff --git a/mypy/checker.py b/mypy/checker.py index 0ac8f6904973f..9d41743578eba 100644 --- a/mypy/checker.py +++ b/mypy/checker.py @@ -992,8 +992,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: diff --git a/mypy/checkexpr.py b/mypy/checkexpr.py index 626584bc3a201..1f25e48419e2e 100644 --- a/mypy/checkexpr.py +++ b/mypy/checkexpr.py @@ -5943,17 +5943,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, is_coroutine=False) if not allow_none_return and isinstance(get_proper_type(expr_type), NoneType): self.chk.msg.does_not_return_value(None, e) From 9a79cf21acb53ed52dfaae65cb33f511c7644aab Mon Sep 17 00:00:00 2001 From: hauntsaninja Date: Fri, 29 Dec 2023 15:55:09 -0600 Subject: [PATCH 2/7] test --- mypyc/test-data/run-generators.test | 1 - test-data/unit/check-statements.test | 28 +++++++++++++++++++++++++++- 2 files changed, 27 insertions(+), 2 deletions(-) diff --git a/mypyc/test-data/run-generators.test b/mypyc/test-data/run-generators.test index bcf9da1846aef..5bf8692dcc02a 100644 --- a/mypyc/test-data/run-generators.test +++ b/mypyc/test-data/run-generators.test @@ -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 diff --git a/test-data/unit/check-statements.test b/test-data/unit/check-statements.test index f5b47e7ab97fc..39b51f0c7e6af 100644 --- a/test-data/unit/check-statements.test +++ b/test-data/unit/check-statements.test @@ -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] @@ -2231,6 +2231,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 testYieldFromUnionOfGenerators] +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]: + return (yield from arg) + +[case testYieldFromInvalidUnionReturn] +from typing import Generator, Union + +class T: pass +class R: pass + +def foo(arg: Union[T, R]) -> Generator[Union[int, str], None, T]: + return (yield from arg) # E: "yield from" can't be applied to "Union[T, R]" + +[case testYieldFromUnionOfGeneratorWithIterableStr] +from typing import Generator, Union, Iterable, Optional + +class T: pass + +def foo(arg: Union[Generator[int, None, T], Iterable[str]]) -> Generator[Union[int, str], None, Optional[T]]: + return (yield from arg) +[builtins fixtures/tuple.pyi] + [case testNoCrashOnStarRightHandSide] x = *(1, 2, 3) # E: can't use starred expression here [builtins fixtures/tuple.pyi] From 1f0349f1a5362bebc34a87328cd6567f57fac3f4 Mon Sep 17 00:00:00 2001 From: hauntsaninja Date: Fri, 29 Dec 2023 19:43:01 -0600 Subject: [PATCH 3/7] test --- mypyc/test-data/run-generators.test | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/mypyc/test-data/run-generators.test b/mypyc/test-data/run-generators.test index 5bf8692dcc02a..e3edce6090ed6 100644 --- a/mypyc/test-data/run-generators.test +++ b/mypyc/test-data/run-generators.test @@ -251,7 +251,7 @@ from traceback import print_tb from contextlib import contextmanager import wrapsys -def generator() -> Iterable[int]: +def generator() -> Generator[int, None, int | None]: try: yield 1 yield 2 @@ -263,6 +263,8 @@ def generator() -> Iterable[int]: print('caught exception with value ' + s) else: print('caught exception without value') + return 0 + return None def no_except() -> Iterable[int]: yield 1 @@ -354,11 +356,11 @@ with ctx_manager() as c: raise Exception File "native.py", line 10, in generator yield 3 - File "native.py", line 30, in wrapper + File "native.py", line 31, in wrapper return (yield from x) File "native.py", line 9, in generator yield 2 - File "native.py", line 30, in wrapper + File "native.py", line 31, in wrapper return (yield from x) caught exception without value caught exception with value some string From b25386201b6794c34d3d841744b04f53fb8f5fb8 Mon Sep 17 00:00:00 2001 From: hauntsaninja Date: Fri, 29 Dec 2023 23:54:41 -0800 Subject: [PATCH 4/7] . --- mypyc/test-data/run-generators.test | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/mypyc/test-data/run-generators.test b/mypyc/test-data/run-generators.test index e3edce6090ed6..7e9804c49582a 100644 --- a/mypyc/test-data/run-generators.test +++ b/mypyc/test-data/run-generators.test @@ -246,12 +246,12 @@ assert run_generator(another_triple()()) == ((1,), None) assert run_generator(outer()) == ((0, 1, 2, 3, 4), None) [case testYieldThrow] -from typing import Generator, Iterable, Any +from typing import Generator, Iterable, Any, Union from traceback import print_tb from contextlib import contextmanager import wrapsys -def generator() -> Generator[int, None, int | None]: +def generator() -> Generator[int, None, Union[int, None]]: try: yield 1 yield 2 From bde97f705ff25456945d78a7543fd430de8fa68a Mon Sep 17 00:00:00 2001 From: Shantanu Jain Date: Wed, 6 Mar 2024 16:53:18 -0800 Subject: [PATCH 5/7] . --- test-data/unit/check-statements.test | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/test-data/unit/check-statements.test b/test-data/unit/check-statements.test index 39b51f0c7e6af..8eae51105d739 100644 --- a/test-data/unit/check-statements.test +++ b/test-data/unit/check-statements.test @@ -2242,18 +2242,16 @@ def foo(arg: Union[Generator[int, None, T], Generator[str, None, T]]) -> Generat [case testYieldFromInvalidUnionReturn] from typing import Generator, Union -class T: pass -class R: pass +class A: pass +class B: pass -def foo(arg: Union[T, R]) -> Generator[Union[int, str], None, T]: - return (yield from arg) # E: "yield from" can't be applied to "Union[T, R]" +def foo(arg: Union[A, B]) -> Generator[Union[int, str], None, A]: + return (yield from arg) # E: "yield from" can't be applied to "Union[A, B]" [case testYieldFromUnionOfGeneratorWithIterableStr] from typing import Generator, Union, Iterable, Optional -class T: pass - -def foo(arg: Union[Generator[int, None, T], Iterable[str]]) -> Generator[Union[int, str], None, Optional[T]]: +def foo(arg: Union[Generator[int, None, bytes], Iterable[str]]) -> Generator[Union[int, str], None, Optional[bytes]]: return (yield from arg) [builtins fixtures/tuple.pyi] From 2f5c5c54b14a992067f22b435ac5d86201974c83 Mon Sep 17 00:00:00 2001 From: Shantanu Jain Date: Wed, 6 Mar 2024 17:12:26 -0800 Subject: [PATCH 6/7] . --- test-data/unit/check-statements.test | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/test-data/unit/check-statements.test b/test-data/unit/check-statements.test index 8eae51105d739..718e365095dd5 100644 --- a/test-data/unit/check-statements.test +++ b/test-data/unit/check-statements.test @@ -2253,6 +2253,16 @@ from typing import Generator, Union, Iterable, Optional def foo(arg: Union[Generator[int, None, bytes], Iterable[str]]) -> Generator[Union[int, str], None, Optional[bytes]]: return (yield from arg) + +def bar(arg: Generator[str, None, str]) -> Generator[str, None, str]: + return foo(arg) # E: Incompatible return value type (got "Generator[Union[int, str], None, Optional[bytes]]", expected "Generator[str, None, str]") + +def launder(arg: Iterable[str]) -> Generator[Union[int, str], None, Optional[bytes]]: + return foo(arg) + +def baz(arg: Generator[str, None, str]) -> Generator[Union[int, str], None, Optional[bytes]]: + # this is unsound, the Generator return type will actually be str + return launder(arg) [builtins fixtures/tuple.pyi] [case testNoCrashOnStarRightHandSide] From 086d2a0877fd16ba5cea28677fdd171facd26b71 Mon Sep 17 00:00:00 2001 From: Shantanu Jain Date: Wed, 6 Mar 2024 17:13:53 -0800 Subject: [PATCH 7/7] . --- test-data/unit/check-statements.test | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/test-data/unit/check-statements.test b/test-data/unit/check-statements.test index 718e365095dd5..71cc807197797 100644 --- a/test-data/unit/check-statements.test +++ b/test-data/unit/check-statements.test @@ -2265,6 +2265,17 @@ def baz(arg: Generator[str, None, str]) -> Generator[Union[int, str], None, Opti return launder(arg) [builtins fixtures/tuple.pyi] +[case testYieldIteratorReturn] +from typing import Iterator + +def get_strings(foo: bool) -> Iterator[str]: + if foo: + return ["foo1", "foo2"] # E: No return value expected + else: + yield "bar1" + yield "bar2" +[builtins fixtures/tuple.pyi] + [case testNoCrashOnStarRightHandSide] x = *(1, 2, 3) # E: can't use starred expression here [builtins fixtures/tuple.pyi]