How should a context manager be annotated with Python type hints?
import typing
@contextlib.contextmanager
def foo() -> ???:
yield
The documentation on contextlib doesn't mention types much.
The documentation on typing.ContextManager is not all that helpful either.
There's also typing.Generator, which at least has an example. Does that mean I should use typing.Generator[None, None, None]
and not typing.ContextManager
?
import typing
@contextlib.contextmanager
def foo() -> typing.Generator[None, None, None]:
yield
Whenever I'm not 100% sure what types a function accepts, I like to consult typeshed, which is the canonical repository of type hints for Python. Mypy directly bundles and uses typeshed to help it perform its typechecking, for example.
We can find the stubs for contextlib here: https://github.com/python/typeshed/blob/master/stdlib/2and3/contextlib.pyi
It's a little overwhelming, but the line we care about is this one:
It states that the decorator takes in a
Callable[..., Iterator[_T]]
-- a function with arbitrary arguments returning some iterator. So in conclusion, it would be fine to do:So, why does using
Generator[None, None, None]
also work, as suggested by the comments?It's because
Generator
is a subtype ofIterator
-- we can again check this for ourselves by consulting typeshed. So, if our function returns a generator, it's still compatible with whatcontextmanager
expects so mypy accepts it without an issue.The
Iterator[]
version doesn't work when you want to return the contextmanager's reference. For instance, the following code:Will produce an error on the
return assert_timing(high=seconds)
line:Incompatible return value type (got "_GeneratorContextManager[None]", expected "Iterator[None]")
Any legit usage of the function:
Will result in something like this:
You could fix it like this...
But I am going to use the new
ContextManager[]
object instead and silence out mypy for the decorator:With my PyCharm, I do the following to make its type hinting work:
The return type of the function wrapped by a context manager is
Iterator[None]
.