Skip to content

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

Closed
@dvarrazzo

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

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