2025, Oct 03 21:17

Аннотация зависимости SQLAlchemy в FastAPI: Generator[Session] без предупреждений Pylance

Разбираем предупреждение Pylance в FastAPI и SQLAlchemy: sessionmaker не подходит в аннотации — как корректно указать Generator[Session] для зависимости сессии.

Аннотирование зависимости сессии SQLAlchemy в конфигурации в стиле FastAPI нередко приводит к предупреждению Pylance: “Variable not allowed in type expression.” Оно возникает, когда вы помечаете тип возвращаемого значения переменной с фабрикой sessionmaker, а не реальному типу объекта, который фактически выдаётся. Исправление становится очевидным, если понять, что именно возвращает функция.

Проблема

Рассмотрим минимальную зависимость, которая открывает сессию базы данных и отдаёт её через yield:

from sqlalchemy.orm import sessionmaker
SessionBuilder = sessionmaker(db_engine, **sa_session_cfg)
def open_session() -> SessionBuilder:
    with SessionBuilder() as conn:
        yield conn

Pylance помечает аннотацию «-> SessionBuilder» предупреждением «Variable not allowed in type expression.»

Что происходит

Можно попросить проверяющий типов показать, какой экземпляр возвращает фабрика. reveal_type показывает, что вызов экземпляра sessionmaker создаёт Session:

from typing import reveal_type
from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker
tmp_engine = create_engine("sqlite:///:memory:")
BuildSession = sessionmaker(tmp_engine)
reveal_type(BuildSession())

$ pyright stackoverflow.py
/path/to/example/stackoverflow.py
/path/to/example/stackoverflow.py:8:13 - information: Type of "BuildSession()" is "Session"
0 errors, 0 warnings, 1 information

Если бы функция была обычной (без yield), возвращаемый тип просто был бы Session. Но поскольку используется yield, функция возвращает генератор, который выдаёт Session. Поэтому аннотация должна описывать тип выдаваемого значения, а не переменную, в которой хранится фабрика.

Решение

Аннотируйте функцию как возвращающую генератор, выдающий Session. Это снимает предупреждение и точнее выражает намерение:

from typing import Generator
from sqlalchemy import create_engine
from sqlalchemy.orm import Session, sessionmaker
core_engine = create_engine("sqlite:///:memory:")
SessionFactory = sessionmaker(core_engine)
def provide_session() -> Generator[Session]:
    with SessionFactory() as handle:
        yield handle

Здесь функция отдаёт Session, значит Generator[Session] — корректная аннотация. Если бы это не был генератор и объект просто возвращался, типом был бы Session.

Почему это важно

Точные аннотации помогают проверяющему типов лучше понимать поток выполнения и находить реальные проблемы вместо поверхностных предупреждений. Они также ясно показывают потребителям зависимости, что именно предоставляется: выданный Session, а не значение фабрики.

Выводы

Используйте проверку типов, чтобы понять, что возвращает фабрика, и аннотируйте именно этот тип. Для зависимостей на генераторах, которые отдают ресурс, указывайте возвращаемый тип как Generator этого ресурса. В нашем случае корректно писать Generator[Session], а не переменную, в которой хранится sessionmaker.

Статья основана на вопросе на StackOverflow от postcoital-solitaire и ответе от daveruinseverything.