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

Yield type inferred incorrectly in nested Generators #81

Closed
jolaf opened this issue Sep 26, 2019 · 13 comments
Closed

Yield type inferred incorrectly in nested Generators #81

jolaf opened this issue Sep 26, 2019 · 13 comments

Comments

@jolaf
Copy link

jolaf commented Sep 26, 2019

The following code:

from typing import Generator

from typeguard import TypeChecker

def a() -> Generator[str, None, None]:
    yield "OK"

def b() -> Generator[Generator[str, None, None], None, None]:
    yield a()
    yield a()

with TypeChecker('__main__'):
    for f in b():
        print(next(f))

produces the following output:

$ python3 Test.py 
OK
/usr/local/lib/python3.6/dist-packages/typeguard/__init__.py:932: TypeWarning: [MainThread] return from __main__.b() at Test.py:10: type of yielded value must be str; got generator instead
  warn(TypeWarning(memo, event, frame, exc))
OK

Note that the problem occurs only at the second yield, but not at the first one.

This is an old issue that I'm observing for some time, but I thought it to be a consequence of #77, but #77 is fixed now in v2.5.1, but this issue still persists.

@jolaf
Copy link
Author

jolaf commented Sep 26, 2019

$ python3 --version
Python 3.6.8

@agronholm
Copy link
Owner

Using @typechecked instead gives another unexpected error: TypeError: type of value yielded from generator must be collections.abc.Generator; got typeguard.TypeCheckedGenerator instead
I guess I need to implicitly inherit from (or register with collections.abc.Generator) the TypeCheckedGenerator class?

@jolaf
Copy link
Author

jolaf commented Sep 26, 2019

Note that the problem persists if a() is typed as Iterator[str] instead.

@agronholm
Copy link
Owner

Note that the problem persists if a() is typed as Iterator[str] instead.

Which seems strange because I didn't register TypeCheckedGenerator as an Iterator either. What am I missing here?

@agronholm
Copy link
Owner

Oh, sorry, I misread your post.

@agronholm
Copy link
Owner

What I meant to say is that this works:

from typing import Generator, Iterator

from typeguard import typechecked


@typechecked
def a() -> Iterator[str]:
    yield "OK"


@typechecked
def b() -> Generator[Iterator[str], None, None]:
    yield a()
    yield a()


for f in b():
    print(f.send(None))

@jolaf
Copy link
Author

jolaf commented Nov 8, 2019

Crashes with import hook as well.

TypeGuardTest.py:

from typeguard.importhook import install_import_hook

install_import_hook('Test')

import Test

Test.py:

from typing import Generator

def a() -> Generator[str, None, None]:
    yield "OK"

def b() -> Generator[Generator[str, None, None], None, None]:
    yield a()
    yield a()

for f in b():
    print(next(f))
$ python3 TypeGuardTest.py 
Traceback (most recent call last):
  File "TypeGuardTest.py", line 5, in <module>
    import Test
  File "<frozen importlib._bootstrap>", line 971, in _find_and_load
  File "<frozen importlib._bootstrap>", line 955, in _find_and_load_unlocked
  File "<frozen importlib._bootstrap>", line 665, in _load_unlocked
  File "/usr/local/lib/python3.6/dist-packages/typeguard/importhook.py", line 80, in exec_module
    return super().exec_module(module)
  File "Test.py", line 10, in <module>
    for f in b():
  File "/usr/local/lib/python3.6/dist-packages/typeguard/__init__.py", line 635, in __next__
    return self.send(None)
  File "/usr/local/lib/python3.6/dist-packages/typeguard/__init__.py", line 652, in send
    check_type('value yielded from generator', value, self.__yield_type, memo=self.__memo)
  File "/usr/local/lib/python3.6/dist-packages/typeguard/__init__.py", line 516, in check_type
    check_type(argname, value, origin_type, memo)
  File "/usr/local/lib/python3.6/dist-packages/typeguard/__init__.py", line 544, in check_type
    format(argname, qualified_name(expected_type), qualified_name(value)))
TypeError: type of value yielded from generator must be collections.abc.Generator; got typeguard.TypeCheckedGenerator instead

Note that the crash seems different than the original one.
I'm not sure if it deserves a separate issue.

@jolaf
Copy link
Author

jolaf commented Nov 8, 2019

This issue seems to be a real blocker for me, preventing me from running my full-scale software under typeguard with import hook. :(

@agronholm
Copy link
Owner

I've fixed this locally and I will soon push the changes once the tests and everything check out.

@agronholm
Copy link
Owner

This fixes generators for @typechecked. If it's still broken with the profiler hook, I frankly don't care.

@jolaf
Copy link
Author

jolaf commented Nov 9, 2019

Okay, but it crashes with import hook too.
#81 (comment)

@agronholm
Copy link
Owner

Have you tested against the fix I made? The example code in the comment does work for me now.

@jolaf
Copy link
Author

jolaf commented Nov 10, 2019

Yes, looks fixed, thanks!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants