microsoft / pyright

Static Type Checker for Python
Other
13.17k stars 1.41k forks source link

Generator SendType inference #5181

Closed mehdigmira closed 1 year ago

mehdigmira commented 1 year ago

Is your feature request related to a problem? Please describe. Starting from pyright 1.1.305, the following code generates Return type, "Generator[int, Unknown, None]", is partially unknown

# pyright: strict

from __future__ import annotations

def f(x: list[int]):  # Return type, "Generator[int, Unknown, None]", is partially unknown
    yield from x

I see in the changelog that this is probably related to

Bug Fix: Fixed bug in inference of generator function return type that led to false positive error. The "send" type (the second of three type arguments for a Generator) cannot be inferred precisely, so it should be Unknown rather than None. Modified the "send type" type inference logic for generators. Instead of always inferring Unknown, pyright now infers Any if the send type is provably never used within the generator. This is a common case and allows the generator function return type to be inferred without an Unknown partial type in most cases.

As i understand it, there is some special casing to know when the send type is never used. Can this be extended to handle yield from usecases ?

Describe the solution you'd like

I'd like the code in the example to not generate an error

erictraut commented 1 year ago

I don't see a way to determine whether the send type is used with a yield from. It depends on the caller, and the type analyzer doesn't know how a caller will use the return result. You can avoid the Unknown simply by providing an explicit return type annotation rather than relying on return type inference.

mehdigmira commented 1 year ago

The same argument can be made for yield ? how come "pyright now infers Any if the send type is provably never used within the generator" in that case, but not in the yield from case ?

erictraut commented 1 year ago

With the yield statement, it's possible to determine how the function is using the send value. With yield from, it's not possible because the entire generator object is passed back to the caller.

mehdigmira commented 1 year ago

Sorry to annoy you with this, but I don't understand your point. yield from simply delegates the send logic to a subgenerator as described here. If the subgenerator's SendType is inferred to be Any, I don't see why the generator's SendType shouldn't be None as well. Even if the problem can indeed be solved by explicitly defining an explicit return type annotation, type inference is one of the main advantages of pyright, and I think it's too bad that the behavior is noisy when using yield from, which is a common python pattern.

erictraut commented 1 year ago

See this thread for more details.