TIL in Python typing
yield return type
If a function returns a generator with the
yield keyword, it makes sense to type it with
typing.Generator. For example, in Pytest fixtures with cleanup logic:
from typing import Generator @pytest.fixture def httpclient() -> Generator[TestClient, None, None]: app = create_app() yield TestClient(app) app.shutdown()
But I've always hated those superfluous Nones. After using cachew, a decorator-based persistent cache that uses iterators to retain the return-type, it turns out there's a simpler way:
from typing import Iterator @pytest.fixture def httpclient() -> Iterator[TestClient]: app = create_app() yield TestClient(app) app.shutdown()
Generators implement the iterator-protocol, so it's technically correct. I won't be truly satisfied until it can be shortened to an import-less
Something else that's hard to look at every day is a lengthy FastAPI Depends, particularly if it applies to a bunch of endpoints:
def get_authed_user() -> User: ... @app.get("/dashboard") def dashboard(*, user: User = Depends(get_authed_user)): ...
It turns out there's a Python type called
Annotated[T, ...metadata]) that lets you bind runtime logic to a type. For example, you could do this in FastAPI 0.95.0:
from typing import Annotated def get_authed_user() -> User: ... AuthenticatedUser = Annotated[User, Depends(get_authed_user)] @app.get("/dashboard") def dashboard(*, user: AuthenticatedUser): ...
I'm looking forward to
black spreading my functions over fewer pages.