Skip to content

Failure to account for None-returning generators in StopIteration.args #18073

Description

@dvarrazzo

In psycopg 3 we have generators returning a final result, which sometimes is None, sometimes a different object. We also have some consumers of these generators, which wait for I/O completion according to the values yielded by the generators and finally return the final generator result.

Something similar can be represented with the following code:

from typing import Any, Generator, TypeAlias, TypeVar

RV = TypeVar("RV")

PQGen: TypeAlias = Generator[Any, Any, RV]


def str_gen() -> PQGen[str]:
    yield 10
    return "abc"


def none_gen() -> PQGen[None]:
    yield 10
    return None


def wait(gen: PQGen[RV]) -> RV:
    try:
        while True:
            next(gen)
    except StopIteration as ex:
        rv: RV = ex.args[0] if ex.args else None
        return rv


print(f"{wait(str_gen())=}")
print(f"{wait(none_gen())=}")

If a generator returns None, the StopIteration.args tuple will empty, which is accounted for in the wait()'s return statement (removing the check will cause an IndexError on wait(none_gen())). However, since Mypy 1.12, the return statement in the wait returns an error:

bug_mypy.py:24: error: Incompatible types in assignment (expression has type "Any | None", variable has type "RV")  [assignment]

This wasn't reported as error in Mypy 1.11

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugmypy got something wrong

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions