From c0535dd7691c8b57d87fd5411cb0202866256fd6 Mon Sep 17 00:00:00 2001 From: Yurii Karabas <1998uriyyo@gmail.com> Date: Tue, 27 Jan 2026 00:46:40 +0100 Subject: [PATCH] Fix mypy error on scalar call with tuple[Any, ...] --- lib/sqlalchemy/engine/result.py | 11 ++++++ test/typing/plain_files/sql/typed_results.py | 35 ++++++++++++++++++++ 2 files changed, 46 insertions(+) mode change 100644 => 100755 lib/sqlalchemy/engine/result.py diff --git a/lib/sqlalchemy/engine/result.py b/lib/sqlalchemy/engine/result.py old mode 100644 new mode 100755 index 946f3192bd..5c1cd85a58 --- a/lib/sqlalchemy/engine/result.py +++ b/lib/sqlalchemy/engine/result.py @@ -47,6 +47,7 @@ from ..sql.base import _generative from ..sql.base import InPlaceGenerative from ..util import deprecated from ..util import NONE_SET +from ..util.typing import Never from ..util.typing import Self from ..util.typing import TupleAny from ..util.typing import TypeVarTuple @@ -1064,6 +1065,16 @@ class Result(_WithKeys, ResultInternal[Row[Unpack[_Ts]]]): raise_for_second_row=True, raise_for_none=True, scalar=False ) + # special case to handle mypy issue: + # https://github.com/python/mypy/issues/20651 + @overload + def scalar(self: Result[Never, Unpack[TupleAny]]) -> Optional[Any]: + pass + + @overload + def scalar(self: Result[_T, Unpack[TupleAny]]) -> Optional[_T]: + pass + def scalar(self: Result[_T, Unpack[TupleAny]]) -> Optional[_T]: """Fetch the first column of the first row, and close the result set. diff --git a/test/typing/plain_files/sql/typed_results.py b/test/typing/plain_files/sql/typed_results.py index 98dde5ad9f..8448bf9bb6 100644 --- a/test/typing/plain_files/sql/typed_results.py +++ b/test/typing/plain_files/sql/typed_results.py @@ -6,11 +6,13 @@ from typing import assert_type from typing import cast from typing import Optional from typing import Sequence +from typing import Tuple from typing import Type from typing import Unpack from sqlalchemy import Column from sqlalchemy import column +from sqlalchemy import Connection from sqlalchemy import create_engine from sqlalchemy import func from sqlalchemy import insert @@ -623,3 +625,36 @@ def test_outerjoin_10173() -> None: print(stmt4) print(stmt, stmt2, stmt3) + + +def test_13091() -> None: + with e.connect() as conn: + stmt = select(t_user.c.id) + assert_type(stmt, Select[Unpack[Tuple[Any, ...]]]) + result = conn.execute(stmt) + + assert_type(result, CursorResult[Unpack[Tuple[Any, ...]]]) + data = result.scalar() + assert_type(data, Any | None) + + +def test_13091_2( + conn: Connection, table: Table, c: Column[int], c2: Column[str] +) -> None: + assert_type(table.select(), Select[Unpack[Tuple[Any, ...]]]) + r1 = conn.execute(table.select()) + assert_type(r1, CursorResult[Unpack[Tuple[Any, ...]]]) + d1 = r1.scalar() + assert_type(d1, Any | None) + r2 = conn.execute(select(table)) + assert_type(r2, CursorResult[Unpack[Tuple[Any, ...]]]) + d2 = r2.scalar() + assert_type(d2, Any | None) + r3 = conn.execute(select(c)) + assert_type(r3, CursorResult[int]) + d3 = r3.scalar() + assert_type(d3, int | None) + r4 = conn.execute(select(c, c2)) + assert_type(r4, CursorResult[int, str]) + d4 = r3.scalar() + assert_type(d4, int | None) -- 2.47.3