from __future__ import annotations from typing import Tuple from typing import TYPE_CHECKING from sqlalchemy import Column from sqlalchemy import create_engine from sqlalchemy import Integer from sqlalchemy import lambda_stmt from sqlalchemy import MetaData from sqlalchemy import Result from sqlalchemy import select from sqlalchemy import String from sqlalchemy import Table from sqlalchemy.orm import DeclarativeBase from sqlalchemy.orm import Mapped from sqlalchemy.orm import mapped_column class Base(DeclarativeBase): pass class User(Base): __tablename__ = "a" id: Mapped[int] = mapped_column(primary_key=True) email: Mapped[str] user_table = Table( "user_table", MetaData(), Column("id", Integer), Column("email", String) ) s1 = select(user_table).where(lambda: user_table.c.id == 5) s2 = select(User).where(lambda: User.id == 5) s3 = lambda_stmt(lambda: select(user_table).where(user_table.c.id == 5)) s4 = lambda_stmt(lambda: select(User).where(User.id == 5)) s5 = lambda_stmt(lambda: select(user_table)) + ( lambda s: s.where(user_table.c.id == 5) ) s6 = lambda_stmt(lambda: select(User)) + (lambda s: s.where(User.id == 5)) if TYPE_CHECKING: # EXPECTED_TYPE: StatementLambdaElement reveal_type(s5) # EXPECTED_TYPE: StatementLambdaElement reveal_type(s6) e = create_engine("sqlite://") with e.connect() as conn: result = conn.execute(s6) if TYPE_CHECKING: # EXPECTED_TYPE: CursorResult[Any] reveal_type(result) # we can type these like this my_result: Result[Tuple[User]] = conn.execute(s6) if TYPE_CHECKING: # pyright and mypy disagree on the specific type here, # mypy sees Result as we said, pyright seems to upgrade it to # CursorResult # EXPECTED_RE_TYPE: .*(?:Cursor)?Result\[Tuple\[.*User\]\] reveal_type(my_result)