This is an automated email from the ASF dual-hosted git repository.

potiuk pushed a commit to branch ci-upgrade-main
in repository https://gitbox.apache.org/repos/asf/airflow.git

commit 7aa3e0e30878408720d08acd26e6edb98b11e8d2
Author: Jarek Potiuk <[email protected]>
AuthorDate: Mon Oct 19 13:04:26 2026 +0200

    Make fab and standard provider typing hold for SQLAlchemy 2.1
    
    The CI upgrade moves uv.lock to SQLAlchemy 2.1.4, where Select is
    parametrised by column types (Select[int]) and asc()/desc() of an
    InstrumentedAttribute return OrderByList | UnaryExpression. Use a bare
    Select alias (as airflow-core does since #74629) and narrow the ordering
    column to ColumnElement so the types hold on both 2.0 and 2.1.
    
    Generated-by: Claude Opus 5
---
 .../src/airflow/providers/fab/auth_manager/api_fastapi/sorting.py   | 6 ++++--
 .../standard/src/airflow/providers/standard/utils/sensor_helper.py  | 5 ++++-
 2 files changed, 8 insertions(+), 3 deletions(-)

diff --git 
a/providers/fab/src/airflow/providers/fab/auth_manager/api_fastapi/sorting.py 
b/providers/fab/src/airflow/providers/fab/auth_manager/api_fastapi/sorting.py
index b19d43cb9c8..6933f6da425 100644
--- 
a/providers/fab/src/airflow/providers/fab/auth_manager/api_fastapi/sorting.py
+++ 
b/providers/fab/src/airflow/providers/fab/auth_manager/api_fastapi/sorting.py
@@ -17,7 +17,7 @@
 from __future__ import annotations
 
 from collections.abc import Mapping
-from typing import TYPE_CHECKING, Any
+from typing import TYPE_CHECKING, Any, cast
 
 from fastapi import HTTPException, status
 from sqlalchemy import asc, desc
@@ -45,5 +45,7 @@ def build_ordering(
             detail=f"Ordering with '{order_by}' is disallowed or the attribute 
does not exist on the model",
         )
 
-    col = allowed[key]
+    # SQLAlchemy 2.1 types asc()/desc() of an InstrumentedAttribute as 
``OrderByList | UnaryExpression``;
+    # narrowing to ColumnElement selects the overload returning 
UnaryExpression on both 2.0 and 2.1.
+    col = cast("ColumnElement[Any]", allowed[key])
     return desc(col) if is_desc else asc(col)
diff --git 
a/providers/standard/src/airflow/providers/standard/utils/sensor_helper.py 
b/providers/standard/src/airflow/providers/standard/utils/sensor_helper.py
index f16e86bf34a..7562c67b68d 100644
--- a/providers/standard/src/airflow/providers/standard/utils/sensor_helper.py
+++ b/providers/standard/src/airflow/providers/standard/utils/sensor_helper.py
@@ -30,6 +30,9 @@ if TYPE_CHECKING:
     from sqlalchemy.orm import Session
     from sqlalchemy.sql import Select
 
+    # TODO: use Select[int] once the minimum SQLAlchemy is 2.1; 2.0 uses 
Select[tuple[int]].
+    CountSelect = Select
+
 
 @provide_session
 def _get_count(
@@ -89,7 +92,7 @@ def _get_count(
 
 def _count_stmt(
     model: type[DagRun] | type[TaskInstance], states: list[str], dttm_filter: 
list[Any], external_dag_id: str
-) -> Select[tuple[int]]:
+) -> CountSelect:
     """
     Get the count of records against dttm filter and states.
 

Reply via email to