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

kaxilnaik pushed a commit to branch v3-0-test
in repository https://gitbox.apache.org/repos/asf/airflow.git


The following commit(s) were added to refs/heads/v3-0-test by this push:
     new 5f45f83a8e3 [v3-0-test] Add `state` attribute to `RuntimeTaskInstance` 
(#50031) (#50101)
5f45f83a8e3 is described below

commit 5f45f83a8e3430d12919521dbb17b98e9a5b9485
Author: github-actions[bot] 
<41898282+github-actions[bot]@users.noreply.github.com>
AuthorDate: Fri May 2 15:13:04 2025 +0530

    [v3-0-test] Add `state` attribute to `RuntimeTaskInstance` (#50031) (#50101)
    
    (cherry picked from commit fe84b8555dc6e14090f5608c57ad42feec93b1e8)
    
    Co-authored-by: Kaxil Naik <[email protected]>
---
 .../src/airflow/api_fastapi/execution_api/app.py   |  3 +-
 devel-common/src/tests_common/pytest_plugin.py     | 12 ++--
 task-sdk/src/airflow/sdk/api/client.py             |  4 +-
 .../src/airflow/sdk/api/datamodels/_generated.py   | 15 +++++
 task-sdk/src/airflow/sdk/execution_time/comms.py   |  8 +--
 .../src/airflow/sdk/execution_time/supervisor.py   | 19 +++---
 .../src/airflow/sdk/execution_time/task_runner.py  | 67 ++++++++++---------
 .../task_sdk/definitions/test_mappedoperator.py    |  8 +--
 .../tests/task_sdk/definitions/test_xcom_arg.py    | 24 +++----
 .../task_sdk/execution_time/test_supervisor.py     | 18 ++---
 .../task_sdk/execution_time/test_task_runner.py    | 78 +++++++++++++---------
 11 files changed, 145 insertions(+), 111 deletions(-)

diff --git a/airflow-core/src/airflow/api_fastapi/execution_api/app.py 
b/airflow-core/src/airflow/api_fastapi/execution_api/app.py
index 1c021015ed5..ef51da98279 100644
--- a/airflow-core/src/airflow/api_fastapi/execution_api/app.py
+++ b/airflow-core/src/airflow/api_fastapi/execution_api/app.py
@@ -197,7 +197,7 @@ def get_extra_schemas() -> dict[str, dict]:
     """Get all the extra schemas that are not part of the main FastAPI app."""
     from airflow.api_fastapi.execution_api.datamodels.taskinstance import 
TaskInstance
     from airflow.executors.workloads import BundleInfo
-    from airflow.utils.state import TerminalTIState
+    from airflow.utils.state import TaskInstanceState, TerminalTIState
 
     return {
         "TaskInstance": TaskInstance.model_json_schema(),
@@ -205,6 +205,7 @@ def get_extra_schemas() -> dict[str, dict]:
         # Include the combined state enum too. In the datamodels we separate 
out SUCCESS from the other states
         # as that has different payload requirements
         "TerminalTIState": {"type": "string", "enum": list(TerminalTIState)},
+        "TaskInstanceState": {"type": "string", "enum": 
list(TaskInstanceState)},
     }
 
 
diff --git a/devel-common/src/tests_common/pytest_plugin.py 
b/devel-common/src/tests_common/pytest_plugin.py
index a86377b5ac3..b461ddc409d 100644
--- a/devel-common/src/tests_common/pytest_plugin.py
+++ b/devel-common/src/tests_common/pytest_plugin.py
@@ -48,7 +48,7 @@ if TYPE_CHECKING:
     from airflow.models.taskinstance import TaskInstance
     from airflow.providers.standard.operators.empty import EmptyOperator
     from airflow.sdk import Context
-    from airflow.sdk.api.datamodels._generated import IntermediateTIState, 
TerminalTIState
+    from airflow.sdk.api.datamodels._generated import TaskInstanceState as 
TIState
     from airflow.sdk.bases.operator import BaseOperator as TaskSDKBaseOperator
     from airflow.sdk.execution_time.comms import StartupDetails, ToSupervisor
     from airflow.sdk.execution_time.task_runner import RuntimeTaskInstance
@@ -2016,7 +2016,7 @@ class RunTaskCallable(Protocol):
     """Protocol for better type hints for the fixture `run_task`."""
 
     @property
-    def state(self) -> IntermediateTIState | TerminalTIState: ...
+    def state(self) -> TIState: ...
 
     @property
     def msg(self) -> ToSupervisor | None: ...
@@ -2048,7 +2048,7 @@ class RunTaskCallable(Protocol):
         ti_id: UUID | None = None,
         max_tries: int | None = None,
         context_update: dict[str, Any] | None = None,
-    ) -> tuple[IntermediateTIState | TerminalTIState, ToSupervisor | None, 
BaseException | None]: ...
+    ) -> tuple[TIState, ToSupervisor | None, BaseException | None]: ...
 
 
 @pytest.fixture
@@ -2193,7 +2193,7 @@ def run_task(create_runtime_ti, mock_supervisor_comms, 
spy_agency) -> RunTaskCal
 
             task = MyTaskOperator(task_id="test_task")
             run_task(task)
-            assert run_task.state == TerminalTIState.SUCCESS
+            assert run_task.state == TaskInstanceState.SUCCESS
             assert run_task.error is None
     """
     import structlog
@@ -2305,7 +2305,7 @@ def run_task(create_runtime_ti, mock_supervisor_comms, 
spy_agency) -> RunTaskCal
             self._context = None
 
         @property
-        def state(self) -> IntermediateTIState | TerminalTIState:
+        def state(self) -> TIState:
             """Get the task state."""
             return self._state
 
@@ -2344,7 +2344,7 @@ def run_task(create_runtime_ti, mock_supervisor_comms, 
spy_agency) -> RunTaskCal
             ti_id: UUID | None = None,
             max_tries: int | None = None,
             context_update: dict[str, Any] | None = None,
-        ) -> tuple[IntermediateTIState | TerminalTIState, ToSupervisor | None, 
BaseException | None]:
+        ) -> tuple[TIState, ToSupervisor | None, BaseException | None]:
             now = timezone.utcnow()
             if logical_date is None:
                 logical_date = now
diff --git a/task-sdk/src/airflow/sdk/api/client.py 
b/task-sdk/src/airflow/sdk/api/client.py
index 399954ddfcb..46e4fe6907a 100644
--- a/task-sdk/src/airflow/sdk/api/client.py
+++ b/task-sdk/src/airflow/sdk/api/client.py
@@ -41,9 +41,9 @@ from airflow.sdk.api.datamodels._generated import (
     DagRunStateResponse,
     DagRunType,
     PrevSuccessfulDagRunResponse,
+    TaskInstanceState,
     TaskStatesResponse,
     TerminalStateNonSuccess,
-    TerminalTIState,
     TIDeferredStatePayload,
     TIEnterRunningPayload,
     TIHeartbeatInfo,
@@ -148,7 +148,7 @@ class TaskInstanceOperations:
 
     def finish(self, id: uuid.UUID, state: TerminalStateNonSuccess, when: 
datetime):
         """Tell the API server that this TI has reached a terminal state."""
-        if state == TerminalTIState.SUCCESS:
+        if state == TaskInstanceState.SUCCESS:
             raise ValueError("Logic error. SUCCESS state should call the 
`succeed` function instead")
         # TODO: handle the naming better. finish sounds wrong as "even" 
deferred is essentially finishing.
         body = TITerminalStatePayload(end_date=when, 
state=TerminalStateNonSuccess(state))
diff --git a/task-sdk/src/airflow/sdk/api/datamodels/_generated.py 
b/task-sdk/src/airflow/sdk/api/datamodels/_generated.py
index d9c5982e539..7c7647635ea 100644
--- a/task-sdk/src/airflow/sdk/api/datamodels/_generated.py
+++ b/task-sdk/src/airflow/sdk/api/datamodels/_generated.py
@@ -384,6 +384,21 @@ class TerminalTIState(str, Enum):
     REMOVED = "removed"
 
 
+class TaskInstanceState(str, Enum):
+    REMOVED = "removed"
+    SCHEDULED = "scheduled"
+    QUEUED = "queued"
+    RUNNING = "running"
+    SUCCESS = "success"
+    RESTARTING = "restarting"
+    FAILED = "failed"
+    UP_FOR_RETRY = "up_for_retry"
+    UP_FOR_RESCHEDULE = "up_for_reschedule"
+    UPSTREAM_FAILED = "upstream_failed"
+    SKIPPED = "skipped"
+    DEFERRED = "deferred"
+
+
 class AssetEventDagRunReference(BaseModel):
     """
     Schema for AssetEvent model used in DagRun.
diff --git a/task-sdk/src/airflow/sdk/execution_time/comms.py 
b/task-sdk/src/airflow/sdk/execution_time/comms.py
index 039e2a5409c..699fe5c0370 100644
--- a/task-sdk/src/airflow/sdk/execution_time/comms.py
+++ b/task-sdk/src/airflow/sdk/execution_time/comms.py
@@ -63,8 +63,8 @@ from airflow.sdk.api.datamodels._generated import (
     DagRunStateResponse,
     PrevSuccessfulDagRunResponse,
     TaskInstance,
+    TaskInstanceState,
     TaskStatesResponse,
-    TerminalTIState,
     TIDeferredStatePayload,
     TIRescheduleStatePayload,
     TIRetryStatePayload,
@@ -370,9 +370,9 @@ class TaskState(BaseModel):
     """
 
     state: Literal[
-        TerminalTIState.FAILED,
-        TerminalTIState.SKIPPED,
-        TerminalTIState.REMOVED,
+        TaskInstanceState.FAILED,
+        TaskInstanceState.SKIPPED,
+        TaskInstanceState.REMOVED,
     ]
     end_date: datetime | None = None
     type: Literal["TaskState"] = "TaskState"
diff --git a/task-sdk/src/airflow/sdk/execution_time/supervisor.py 
b/task-sdk/src/airflow/sdk/execution_time/supervisor.py
index ff804f3f769..001f8deb701 100644
--- a/task-sdk/src/airflow/sdk/execution_time/supervisor.py
+++ b/task-sdk/src/airflow/sdk/execution_time/supervisor.py
@@ -54,10 +54,9 @@ from airflow.sdk.api.client import Client, 
ServerResponseError
 from airflow.sdk.api.datamodels._generated import (
     AssetResponse,
     ConnectionResponse,
-    IntermediateTIState,
     TaskInstance,
+    TaskInstanceState,
     TaskStatesResponse,
-    TerminalTIState,
     VariableResponse,
     XComResponse,
 )
@@ -130,10 +129,10 @@ SERVER_TERMINATED = "SERVER_TERMINATED"
 # "Directly" here means that the PATCH API calls to transition into these 
states are
 # made from _handle_request() itself and don't have to come all the way to 
wait().
 STATES_SENT_DIRECTLY = [
-    IntermediateTIState.DEFERRED,
-    IntermediateTIState.UP_FOR_RESCHEDULE,
-    IntermediateTIState.UP_FOR_RETRY,
-    TerminalTIState.SUCCESS,
+    TaskInstanceState.DEFERRED,
+    TaskInstanceState.UP_FOR_RESCHEDULE,
+    TaskInstanceState.UP_FOR_RETRY,
+    TaskInstanceState.SUCCESS,
     SERVER_TERMINATED,
 ]
 
@@ -977,10 +976,10 @@ class ActivitySubprocess(WatchedSubprocess):
         Not valid before the process has finished.
         """
         if self._exit_code == 0:
-            return self._terminal_state or TerminalTIState.SUCCESS
+            return self._terminal_state or TaskInstanceState.SUCCESS
         if self._exit_code != 0 and self._terminal_state == SERVER_TERMINATED:
             return SERVER_TERMINATED
-        return TerminalTIState.FAILED
+        return TaskInstanceState.FAILED
 
     def _handle_request(self, msg: ToSupervisor, log: FilteringBoundLogger):
         log.debug("Received message from task runner", msg=msg)
@@ -1045,10 +1044,10 @@ class ActivitySubprocess(WatchedSubprocess):
             else:
                 resp = xcom
         elif isinstance(msg, DeferTask):
-            self._terminal_state = IntermediateTIState.DEFERRED
+            self._terminal_state = TaskInstanceState.DEFERRED
             self.client.task_instances.defer(self.id, msg)
         elif isinstance(msg, RescheduleTask):
-            self._terminal_state = IntermediateTIState.UP_FOR_RESCHEDULE
+            self._terminal_state = TaskInstanceState.UP_FOR_RESCHEDULE
             self.client.task_instances.reschedule(self.id, msg)
         elif isinstance(msg, SkipDownstreamTasks):
             self.client.task_instances.skip_downstream_tasks(self.id, msg)
diff --git a/task-sdk/src/airflow/sdk/execution_time/task_runner.py 
b/task-sdk/src/airflow/sdk/execution_time/task_runner.py
index 00c09528c19..6e1e5f885f5 100644
--- a/task-sdk/src/airflow/sdk/execution_time/task_runner.py
+++ b/task-sdk/src/airflow/sdk/execution_time/task_runner.py
@@ -42,9 +42,8 @@ from airflow.dag_processing.bundles.manager import 
DagBundlesManager
 from airflow.listeners.listener import get_listener_manager
 from airflow.sdk.api.datamodels._generated import (
     AssetProfile,
-    IntermediateTIState,
     TaskInstance,
-    TerminalTIState,
+    TaskInstanceState,
     TIRunContext,
 )
 from airflow.sdk.bases.operator import BaseOperator, ExecutorSafeguard
@@ -94,7 +93,6 @@ from airflow.sdk.execution_time.context import (
 )
 from airflow.sdk.execution_time.xcom import XCom
 from airflow.utils.net import get_hostname
-from airflow.utils.state import TaskInstanceState
 from airflow.utils.timezone import coerce_datetime
 
 if TYPE_CHECKING:
@@ -131,6 +129,8 @@ class RuntimeTaskInstance(TaskInstance):
 
     end_date: AwareDatetime | None = None
 
+    state: TaskInstanceState | None = None
+
     is_mapped: bool | None = None
     """True if the original task was mapped."""
 
@@ -600,6 +600,7 @@ def parse(what: StartupDetails, log: Logger) -> 
RuntimeTaskInstance:
         _ti_context_from_server=what.ti_context,
         max_tries=what.ti_context.max_tries,
         start_date=what.start_date,
+        state=TaskInstanceState.RUNNING,
     )
 
 
@@ -765,7 +766,7 @@ def _prepare(ti: RuntimeTaskInstance, log: Logger, context: 
Context) -> ToSuperv
 
 def _defer_task(
     defer: TaskDeferred, ti: RuntimeTaskInstance, log: Logger
-) -> tuple[ToSupervisor, IntermediateTIState]:
+) -> tuple[ToSupervisor, TaskInstanceState]:
     # TODO: Should we use structlog.bind_contextvars here for dag_id, task_id 
& run_id?
 
     log.info("Pausing task as DEFERRED. ", dag_id=ti.dag_id, 
task_id=ti.task_id, run_id=ti.run_id)
@@ -778,7 +779,7 @@ def _defer_task(
         next_method=defer.method_name,
         next_kwargs=defer.kwargs or {},
     )
-    state = IntermediateTIState.DEFERRED
+    state = TaskInstanceState.DEFERRED
 
     return msg, state
 
@@ -787,7 +788,7 @@ def run(
     ti: RuntimeTaskInstance,
     context: Context,
     log: Logger,
-) -> tuple[IntermediateTIState | TerminalTIState, ToSupervisor | None, 
BaseException | None]:
+) -> tuple[TaskInstanceState, ToSupervisor | None, BaseException | None]:
     """Run the task in this process."""
     from airflow.exceptions import (
         AirflowException,
@@ -807,7 +808,7 @@ def run(
         assert isinstance(ti.task, BaseOperator)
 
     msg: ToSupervisor | None = None
-    state: IntermediateTIState | TerminalTIState
+    state: TaskInstanceState
     error: BaseException | None = None
 
     try:
@@ -827,7 +828,7 @@ def run(
             # catch it and handle it like a normal task failure
             if early_exit := _prepare(ti, log, context):
                 msg = early_exit
-                state = TerminalTIState.FAILED
+                ti.state = state = TaskInstanceState.FAILED
                 return state, msg, error
 
             result = _execute_task(context, ti, log)
@@ -848,16 +849,16 @@ def run(
         if e.args:
             log.info("Skipping task.", reason=e.args[0])
         msg = TaskState(
-            state=TerminalTIState.SKIPPED,
+            state=TaskInstanceState.SKIPPED,
             end_date=datetime.now(tz=timezone.utc),
         )
-        state = TerminalTIState.SKIPPED
+        state = TaskInstanceState.SKIPPED
     except AirflowRescheduleException as reschedule:
         log.info("Rescheduling task, marking task as UP_FOR_RESCHEDULE")
         msg = RescheduleTask(
             reschedule_date=reschedule.reschedule_date, 
end_date=datetime.now(tz=timezone.utc)
         )
-        state = IntermediateTIState.UP_FOR_RESCHEDULE
+        state = TaskInstanceState.UP_FOR_RESCHEDULE
     except (AirflowFailException, AirflowSensorTimeout) as e:
         # If AirflowFailException is raised, task should not retry.
         # If a sensor in reschedule mode reaches timeout, task should not 
retry.
@@ -865,10 +866,10 @@ def run(
         # TODO: Handle fail_stop here: 
https://github.com/apache/airflow/issues/44951
         # TODO: Handle addition to Log table: 
https://github.com/apache/airflow/issues/44952
         msg = TaskState(
-            state=TerminalTIState.FAILED,
+            state=TaskInstanceState.FAILED,
             end_date=datetime.now(tz=timezone.utc),
         )
-        state = TerminalTIState.FAILED
+        state = TaskInstanceState.FAILED
         error = e
     except (AirflowTaskTimeout, AirflowException) as e:
         # We should allow retries if the task has defined it.
@@ -881,10 +882,10 @@ def run(
         # If these are thrown, we should mark the TI state as failed.
         log.exception("Task failed with exception")
         msg = TaskState(
-            state=TerminalTIState.FAILED,
+            state=TaskInstanceState.FAILED,
             end_date=datetime.now(tz=timezone.utc),
         )
-        state = TerminalTIState.FAILED
+        state = TaskInstanceState.FAILED
         error = e
     except SystemExit as e:
         # SystemExit needs to be retried if they are eligible.
@@ -898,14 +899,16 @@ def run(
     finally:
         if msg:
             SUPERVISOR_COMMS.send_request(msg=msg, log=log)
+
     # Return the message to make unit tests easier too
+    ti.state = state
     return state, msg, error
 
 
 def _handle_current_task_success(
     context: Context,
     ti: RuntimeTaskInstance,
-) -> tuple[SucceedTask, TerminalTIState]:
+) -> tuple[SucceedTask, TaskInstanceState]:
     task_outlets = list(_build_asset_profiles(ti.task.outlets))
     outlet_events = list(_serialize_outlet_events(context["outlet_events"]))
     msg = SucceedTask(
@@ -913,21 +916,21 @@ def _handle_current_task_success(
         task_outlets=task_outlets,
         outlet_events=outlet_events,
     )
-    return msg, TerminalTIState.SUCCESS
+    return msg, TaskInstanceState.SUCCESS
 
 
 def _handle_current_task_failed(
     ti: RuntimeTaskInstance,
-) -> tuple[RetryTask, IntermediateTIState] | tuple[TaskState, TerminalTIState]:
+) -> tuple[RetryTask, TaskInstanceState] | tuple[TaskState, TaskInstanceState]:
     end_date = datetime.now(tz=timezone.utc)
     if ti._ti_context_from_server and ti._ti_context_from_server.should_retry:
-        return RetryTask(end_date=end_date), IntermediateTIState.UP_FOR_RETRY
-    return TaskState(state=TerminalTIState.FAILED, end_date=end_date), 
TerminalTIState.FAILED
+        return RetryTask(end_date=end_date), TaskInstanceState.UP_FOR_RETRY
+    return TaskState(state=TaskInstanceState.FAILED, end_date=end_date), 
TaskInstanceState.FAILED
 
 
 def _handle_trigger_dag_run(
     drte: DagRunTriggerException, context: Context, ti: RuntimeTaskInstance, 
log: Logger
-) -> tuple[ToSupervisor, IntermediateTIState | TerminalTIState]:
+) -> tuple[ToSupervisor, TaskInstanceState]:
     """Handle exception from TriggerDagRunOperator."""
     log.info("Triggering Dag Run.", trigger_dag_id=drte.trigger_dag_id)
     SUPERVISOR_COMMS.send_request(
@@ -948,12 +951,12 @@ def _handle_trigger_dag_run(
                 "Dag Run already exists, skipping task as 
skip_when_already_exists is set to True.",
                 dag_id=drte.trigger_dag_id,
             )
-            msg = TaskState(state=TerminalTIState.SKIPPED, 
end_date=datetime.now(tz=timezone.utc))
-            state = TerminalTIState.SKIPPED
+            msg = TaskState(state=TaskInstanceState.SKIPPED, 
end_date=datetime.now(tz=timezone.utc))
+            state = TaskInstanceState.SKIPPED
         else:
             log.error("Dag Run already exists, marking task as failed.", 
dag_id=drte.trigger_dag_id)
-            msg = TaskState(state=TerminalTIState.FAILED, 
end_date=datetime.now(tz=timezone.utc))
-            state = TerminalTIState.FAILED
+            msg = TaskState(state=TaskInstanceState.FAILED, 
end_date=datetime.now(tz=timezone.utc))
+            state = TaskInstanceState.FAILED
 
         return msg, state
 
@@ -998,8 +1001,8 @@ def _handle_trigger_dag_run(
                 log.error(
                     "DagRun finished with failed state.", 
dag_id=drte.trigger_dag_id, state=comms_msg.state
                 )
-                msg = TaskState(state=TerminalTIState.FAILED, 
end_date=datetime.now(tz=timezone.utc))
-                state = TerminalTIState.FAILED
+                msg = TaskState(state=TaskInstanceState.FAILED, 
end_date=datetime.now(tz=timezone.utc))
+                state = TaskInstanceState.FAILED
                 return msg, state
             if comms_msg.state in drte.allowed_states:
                 log.info(
@@ -1151,7 +1154,7 @@ def _push_xcom_if_needed(result: Any, ti: 
RuntimeTaskInstance, log: Logger):
 
 def finalize(
     ti: RuntimeTaskInstance,
-    state: IntermediateTIState | TerminalTIState,
+    state: TaskInstanceState,
     context: Context,
     log: Logger,
     error: BaseException | None = None,
@@ -1172,7 +1175,7 @@ def finalize(
             )
 
     log.debug("Running finalizers", ti=ti)
-    if state == TerminalTIState.SUCCESS:
+    if state == TaskInstanceState.SUCCESS:
         _run_task_state_change_callbacks(task, "on_success_callback", context, 
log)
         try:
             get_listener_manager().hook.on_task_instance_success(
@@ -1180,9 +1183,9 @@ def finalize(
             )
         except Exception:
             log.exception("error calling listener")
-    elif state == TerminalTIState.SKIPPED:
+    elif state == TaskInstanceState.SKIPPED:
         _run_task_state_change_callbacks(task, "on_skipped_callback", context, 
log)
-    elif state == IntermediateTIState.UP_FOR_RETRY:
+    elif state == TaskInstanceState.UP_FOR_RETRY:
         _run_task_state_change_callbacks(task, "on_retry_callback", context, 
log)
         try:
             get_listener_manager().hook.on_task_instance_failed(
@@ -1192,7 +1195,7 @@ def finalize(
             log.exception("error calling listener")
         if error and task.email_on_retry and task.email:
             _send_task_error_email(task.email, ti, error)
-    elif state == TerminalTIState.FAILED:
+    elif state == TaskInstanceState.FAILED:
         _run_task_state_change_callbacks(task, "on_failure_callback", context, 
log)
         try:
             get_listener_manager().hook.on_task_instance_failed(
diff --git a/task-sdk/tests/task_sdk/definitions/test_mappedoperator.py 
b/task-sdk/tests/task_sdk/definitions/test_mappedoperator.py
index 5373202ab9f..d6b8ca8da4e 100644
--- a/task-sdk/tests/task_sdk/definitions/test_mappedoperator.py
+++ b/task-sdk/tests/task_sdk/definitions/test_mappedoperator.py
@@ -24,7 +24,7 @@ from unittest import mock
 import pendulum
 import pytest
 
-from airflow.sdk.api.datamodels._generated import TerminalTIState
+from airflow.sdk.api.datamodels._generated import TaskInstanceState
 from airflow.sdk.bases.operator import BaseOperator
 from airflow.sdk.definitions.dag import DAG
 from airflow.sdk.definitions.mappedoperator import MappedOperator
@@ -405,7 +405,7 @@ def test_nested_mapped_task_groups():
             g1.expand(x=t())
 
 
-RunTI = Callable[[DAG, str, int], TerminalTIState]
+RunTI = Callable[[DAG, str, int], TaskInstanceState]
 
 
 def test_map_cross_product(run_ti: RunTI, mock_supervisor_comms):
@@ -439,7 +439,7 @@ def test_map_cross_product(run_ti: RunTI, 
mock_supervisor_comms):
     mock_supervisor_comms.get_message.side_effect = xcom_get
 
     states = [run_ti(dag, "show", map_index) for map_index in range(6)]
-    assert states == [TerminalTIState.SUCCESS] * 6
+    assert states == [TaskInstanceState.SUCCESS] * 6
     assert outputs == [
         (1, ("a", "x")),
         (1, ("b", "y")),
@@ -479,7 +479,7 @@ def test_map_product_same(run_ti: RunTI, 
mock_supervisor_comms):
     mock_supervisor_comms.get_message.side_effect = xcom_get
 
     states = [run_ti(dag, "show", map_index) for map_index in range(4)]
-    assert states == [TerminalTIState.SUCCESS] * 4
+    assert states == [TaskInstanceState.SUCCESS] * 4
     assert outputs == [(1, 1), (1, 2), (2, 1), (2, 2)]
 
 
diff --git a/task-sdk/tests/task_sdk/definitions/test_xcom_arg.py 
b/task-sdk/tests/task_sdk/definitions/test_xcom_arg.py
index 9a16be08a35..cc968194132 100644
--- a/task-sdk/tests/task_sdk/definitions/test_xcom_arg.py
+++ b/task-sdk/tests/task_sdk/definitions/test_xcom_arg.py
@@ -25,13 +25,13 @@ import structlog
 from pytest_unordered import unordered
 
 from airflow.exceptions import AirflowSkipException
-from airflow.sdk.api.datamodels._generated import TerminalTIState
+from airflow.sdk.api.datamodels._generated import TaskInstanceState
 from airflow.sdk.definitions.dag import DAG
 from airflow.sdk.execution_time.comms import GetXCom, XComResult
 
 log = structlog.get_logger(__name__)
 
-RunTI = Callable[[DAG, str, int], TerminalTIState]
+RunTI = Callable[[DAG, str, int], TaskInstanceState]
 
 
 def test_xcom_map(run_ti: RunTI, mock_supervisor_comms):
@@ -55,7 +55,7 @@ def test_xcom_map(run_ti: RunTI, mock_supervisor_comms):
     mock_supervisor_comms.get_message.return_value = 
XComResult(key="return_value", value=["a", "b", "c"])
 
     for map_index in range(3):
-        assert run_ti(dag, "pull", map_index) == TerminalTIState.SUCCESS
+        assert run_ti(dag, "pull", map_index) == TaskInstanceState.SUCCESS
 
     assert results == {"aa", "bb", "cc"}
 
@@ -85,7 +85,7 @@ def test_xcom_map_transform_to_none(run_ti: RunTI, 
mock_supervisor_comms):
 
     # Run "pull". This should automatically convert "c" to None.
     for map_index in range(3):
-        assert run_ti(dag, "pull", map_index) == TerminalTIState.SUCCESS
+        assert run_ti(dag, "pull", map_index) == TaskInstanceState.SUCCESS
 
     assert results == {"a", "b", None}
 
@@ -115,13 +115,13 @@ def test_xcom_convert_to_kwargs_fails_task(run_ti: RunTI, 
mock_supervisor_comms,
 
     # The first two "pull" tis should succeed.
     for map_index in range(2):
-        assert run_ti(dag, "pull", map_index) == TerminalTIState.SUCCESS
+        assert run_ti(dag, "pull", map_index) == TaskInstanceState.SUCCESS
 
     # Clear captured logs from the above
     captured_logs[:] = []
 
     # But the third one fails because the map() result cannot be used as 
kwargs.
-    assert run_ti(dag, "pull", 2) == TerminalTIState.FAILED
+    assert run_ti(dag, "pull", 2) == TaskInstanceState.FAILED
 
     assert captured_logs == unordered(
         [
@@ -165,7 +165,7 @@ def test_xcom_map_error_fails_task(mock_supervisor_comms, 
run_ti, captured_logs)
     # Mock xcom result from push task
     mock_supervisor_comms.get_message.return_value = 
XComResult(key="return_value", value=["a", "b", "c"])
     # The third one (for "c") will fail.
-    assert run_ti(dag, "pull", 2) == TerminalTIState.FAILED
+    assert run_ti(dag, "pull", 2) == TaskInstanceState.FAILED
 
     assert captured_logs == unordered(
         [
@@ -209,7 +209,7 @@ def test_xcom_map_nest(mock_supervisor_comms, run_ti):
 
     # Now "pull" should apply the mapping functions in order.
     for map_index in range(3):
-        assert run_ti(dag, "pull", map_index) == TerminalTIState.SUCCESS
+        assert run_ti(dag, "pull", map_index) == TaskInstanceState.SUCCESS
     assert results == {"aa", "bb", "cc"}
 
 
@@ -256,7 +256,7 @@ def test_xcom_map_zip_nest(mock_supervisor_comms, run_ti):
 
     # Run "pull".
     for map_index in range(4):
-        assert run_ti(dag, "pull", map_index) == TerminalTIState.SUCCESS
+        assert run_ti(dag, "pull", map_index) == TaskInstanceState.SUCCESS
 
     assert results == {"aa", "bbbb", "cccccc", "dddddddd"}
 
@@ -287,7 +287,7 @@ def test_xcom_map_raise_to_skip(run_ti, 
mock_supervisor_comms):
     # Run "forward". This should automatically skip "c".
     states = [run_ti(dag, "forward", map_index) for map_index in range(3)]
 
-    assert states == [TerminalTIState.SUCCESS, TerminalTIState.SUCCESS, 
TerminalTIState.SKIPPED]
+    assert states == [TaskInstanceState.SUCCESS, TaskInstanceState.SUCCESS, 
TaskInstanceState.SKIPPED]
 
     assert result == ["a", "b"]
 
@@ -353,9 +353,9 @@ def test_xcom_concat(run_ti, mock_supervisor_comms):
     mock_supervisor_comms.get_message.side_effect = xcom_get
 
     # Run "pull_one" and "pull_all".
-    assert run_ti(dag, "pull_all", None) == TerminalTIState.SUCCESS
+    assert run_ti(dag, "pull_all", None) == TaskInstanceState.SUCCESS
     assert all_results == ["a", "b", "c", 1, 2]
 
     states = [run_ti(dag, "pull_one", map_index) for map_index in range(5)]
-    assert states == [TerminalTIState.SUCCESS] * 5
+    assert states == [TaskInstanceState.SUCCESS] * 5
     assert agg_results == {"a", "b", "c", 1, 2}
diff --git a/task-sdk/tests/task_sdk/execution_time/test_supervisor.py 
b/task-sdk/tests/task_sdk/execution_time/test_supervisor.py
index a3eef617a4e..0795e1155c9 100644
--- a/task-sdk/tests/task_sdk/execution_time/test_supervisor.py
+++ b/task-sdk/tests/task_sdk/execution_time/test_supervisor.py
@@ -48,7 +48,7 @@ from airflow.sdk.api.datamodels._generated import (
     AssetResponse,
     DagRunState,
     TaskInstance,
-    TerminalTIState,
+    TaskInstanceState,
 )
 from airflow.sdk.exceptions import AirflowRuntimeError, ErrorType
 from airflow.sdk.execution_time.comms import (
@@ -312,7 +312,7 @@ class TestWatchedSubprocess:
         @spy_agency.spy_for(ActivitySubprocess._on_child_started)
         def _on_child_started(self, *args, **kwargs):
             # Set it up so we are in overtime straight away
-            self._terminal_state = TerminalTIState.SUCCESS
+            self._terminal_state = TaskInstanceState.SUCCESS
             ActivitySubprocess._on_child_started.call_original(self, *args, 
**kwargs)
 
         heartbeat_spy = 
spy_agency.spy_on(sdk_client.TaskInstanceOperations.heartbeat)
@@ -650,11 +650,11 @@ class TestWatchedSubprocess:
                 False,
                 id="no_terminal_state",
             ),
-            pytest.param(TerminalTIState.SUCCESS, 15.0, 10, False, 
id="below_threshold"),
-            pytest.param(TerminalTIState.SUCCESS, 9.0, 10, True, 
id="above_threshold"),
-            pytest.param(TerminalTIState.FAILED, 9.0, 10, True, 
id="above_threshold_failed_state"),
-            pytest.param(TerminalTIState.SKIPPED, 9.0, 10, True, 
id="above_threshold_skipped_state"),
-            pytest.param(TerminalTIState.SUCCESS, None, 20, False, 
id="task_end_datetime_none"),
+            pytest.param(TaskInstanceState.SUCCESS, 15.0, 10, False, 
id="below_threshold"),
+            pytest.param(TaskInstanceState.SUCCESS, 9.0, 10, True, 
id="above_threshold"),
+            pytest.param(TaskInstanceState.FAILED, 9.0, 10, True, 
id="above_threshold_failed_state"),
+            pytest.param(TaskInstanceState.SKIPPED, 9.0, 10, True, 
id="above_threshold_skipped_state"),
+            pytest.param(TaskInstanceState.SUCCESS, None, 20, False, 
id="task_end_datetime_none"),
         ],
     )
     def test_overtime_handling(
@@ -1181,10 +1181,10 @@ class TestHandleRequest:
                 OKResponse(ok=True),
                 id="delete_xcom",
             ),
-            # we aren't adding all states under TerminalTIState here, because 
this test's scope is only to check
+            # we aren't adding all states under TaskInstanceState here, 
because this test's scope is only to check
             # if it can handle TaskState message
             pytest.param(
-                TaskState(state=TerminalTIState.SKIPPED, 
end_date=timezone.parse("2024-10-31T12:00:00Z")),
+                TaskState(state=TaskInstanceState.SKIPPED, 
end_date=timezone.parse("2024-10-31T12:00:00Z")),
                 b"",
                 "",
                 (),
diff --git a/task-sdk/tests/task_sdk/execution_time/test_task_runner.py 
b/task-sdk/tests/task_sdk/execution_time/test_task_runner.py
index 044e7864a58..f9ad46f357e 100644
--- a/task-sdk/tests/task_sdk/execution_time/test_task_runner.py
+++ b/task-sdk/tests/task_sdk/execution_time/test_task_runner.py
@@ -52,9 +52,8 @@ from airflow.sdk.api.datamodels._generated import (
     AssetProfile,
     AssetResponse,
     DagRunState,
-    IntermediateTIState,
     TaskInstance,
-    TerminalTIState,
+    TaskInstanceState,
 )
 from airflow.sdk.bases.xcom import BaseXCom
 from airflow.sdk.definitions._internal.types import SET_DURING_EXECUTION
@@ -113,7 +112,6 @@ from airflow.sdk.execution_time.task_runner import (
 )
 from airflow.sdk.execution_time.xcom import XCom
 from airflow.utils import timezone
-from airflow.utils.state import TaskInstanceState
 from airflow.utils.types import NOTSET, ArgNotSet
 
 from tests_common.test_utils.mock_operators import AirflowLink
@@ -306,7 +304,9 @@ def test_run_deferred_basic(time_machine, 
create_runtime_ti, mock_supervisor_com
 
     # Run the task
     ti = create_runtime_ti(dag_id="basic_deferred_run", task=task)
-    state, msg, err = run(ti, context=ti.get_template_context(), 
log=mock.MagicMock())
+    run(ti, context=ti.get_template_context(), log=mock.MagicMock())
+
+    assert ti.state == TaskInstanceState.DEFERRED
 
     # send_request will only be called when the TaskDeferred exception is 
raised
     
mock_supervisor_comms.send_request.assert_any_call(msg=expected_defer_task, 
log=mock.ANY)
@@ -328,7 +328,7 @@ def test_run_downstream_skipped(mocked_parse, 
create_runtime_ti, mock_supervisor
     context = ti.get_template_context()
     log = mock.MagicMock()
     run(ti, context=context, log=log)
-    finalize(ti, context=context, log=mock.MagicMock(), 
state=TerminalTIState.SUCCESS)
+    finalize(ti, context=context, log=mock.MagicMock(), 
state=TaskInstanceState.SUCCESS)
 
     assert listener.state == [TaskInstanceState.RUNNING, 
TaskInstanceState.SUCCESS]
     log.info.assert_called_with("Skipping downstream tasks.")
@@ -360,6 +360,7 @@ def test_resume_from_deferred(time_machine, 
create_runtime_ti, mock_supervisor_c
     state, msg, err = run(ti, context=ti.get_template_context(), 
log=mock.MagicMock())
     assert err is None
     assert state == TaskInstanceState.SUCCESS
+    assert ti.state == TaskInstanceState.SUCCESS
 
     spy_agency.assert_spy_called_with(spy, mock.ANY, event=instant)
 
@@ -381,8 +382,10 @@ def test_run_basic_skipped(time_machine, 
create_runtime_ti, mock_supervisor_comm
 
     run(ti, context=ti.get_template_context(), log=mock.MagicMock())
 
+    assert ti.state == TaskInstanceState.SKIPPED
+
     mock_supervisor_comms.send_request.assert_called_with(
-        msg=TaskState(state=TerminalTIState.SKIPPED, end_date=instant), 
log=mock.ANY
+        msg=TaskState(state=TaskInstanceState.SKIPPED, end_date=instant), 
log=mock.ANY
     )
 
 
@@ -401,9 +404,11 @@ def test_run_raises_base_exception(time_machine, 
create_runtime_ti, mock_supervi
 
     run(ti, context=ti.get_template_context(), log=mock.MagicMock())
 
+    assert ti.state == TaskInstanceState.FAILED
+
     mock_supervisor_comms.send_request.assert_called_with(
         msg=TaskState(
-            state=TerminalTIState.FAILED,
+            state=TaskInstanceState.FAILED,
             end_date=instant,
         ),
         log=mock.ANY,
@@ -426,9 +431,11 @@ def test_run_raises_system_exit(time_machine, 
create_runtime_ti, mock_supervisor
     log = mock.MagicMock()
     run(ti, context=ti.get_template_context(), log=log)
 
+    assert ti.state == TaskInstanceState.FAILED
+
     mock_supervisor_comms.send_request.assert_called_with(
         msg=TaskState(
-            state=TerminalTIState.FAILED,
+            state=TaskInstanceState.FAILED,
             end_date=instant,
         ),
         log=mock.ANY,
@@ -455,9 +462,11 @@ def test_run_raises_airflow_exception(time_machine, 
create_runtime_ti, mock_supe
 
     run(ti, context=ti.get_template_context(), log=mock.MagicMock())
 
+    assert ti.state == TaskInstanceState.FAILED
+
     mock_supervisor_comms.send_request.assert_called_with(
         msg=TaskState(
-            state=TerminalTIState.FAILED,
+            state=TaskInstanceState.FAILED,
             end_date=instant,
         ),
         log=mock.ANY,
@@ -481,10 +490,12 @@ def test_run_task_timeout(time_machine, 
create_runtime_ti, mock_supervisor_comms
 
     run(ti, context=ti.get_template_context(), log=mock.MagicMock())
 
+    assert ti.state == TaskInstanceState.FAILED
+
     # this state can only be reached if the try block passed down the 
exception to handler of AirflowTaskTimeout
     mock_supervisor_comms.send_request.assert_called_with(
         msg=TaskState(
-            state=TerminalTIState.FAILED,
+            state=TaskInstanceState.FAILED,
             end_date=instant,
         ),
         log=mock.ANY,
@@ -526,6 +537,7 @@ def test_basic_templated_dag(mocked_parse, make_ti_context, 
mock_supervisor_comm
     spy_agency.assert_spy_called(task.prepare_for_execution)
     assert ti.task._lock_for_execution
     assert ti.task is not task, "ti.task should be a copy of the original task"
+    assert ti.state == TaskInstanceState.SUCCESS
 
     mock_supervisor_comms.send_request.assert_any_call(
         msg=SetRenderedFields(
@@ -644,7 +656,7 @@ def test_startup_and_run_dag_with_rtif(
         mock.call.send_request(
             msg=SucceedTask(
                 end_date=instant,
-                state=TerminalTIState.SUCCESS,
+                state=TaskInstanceState.SUCCESS,
                 task_outlets=[],
                 outlet_events=[],
             ),
@@ -699,9 +711,11 @@ def test_get_context_in_task(create_runtime_ti, 
time_machine, mock_supervisor_co
 
     run(ti, context=ti.get_template_context(), log=mock.MagicMock())
 
+    assert ti.state == TaskInstanceState.SUCCESS
+
     # Ensure the task is Successful
     mock_supervisor_comms.send_request.assert_called_once_with(
-        msg=SucceedTask(state=TerminalTIState.SUCCESS, end_date=instant, 
task_outlets=[], outlet_events=[]),
+        msg=SucceedTask(state=TaskInstanceState.SUCCESS, end_date=instant, 
task_outlets=[], outlet_events=[]),
         log=mock.ANY,
     )
 
@@ -747,8 +761,10 @@ def test_run_basic_failed(
 
     run(ti, context=ti.get_template_context(), log=mock.MagicMock())
 
+    assert ti.state == TaskInstanceState.FAILED
+
     mock_supervisor_comms.send_request.assert_called_once_with(
-        msg=TaskState(state=TerminalTIState.FAILED, end_date=instant), 
log=mock.ANY
+        msg=TaskState(state=TaskInstanceState.FAILED, end_date=instant), 
log=mock.ANY
     )
 
 
@@ -1445,7 +1461,7 @@ class TestRuntimeTaskInstance:
 
         mock_supervisor_comms.send_request.assert_called_once_with(
             msg=SucceedTask(
-                state=TerminalTIState.SUCCESS, end_date=instant, 
task_outlets=[], outlet_events=[]
+                state=TaskInstanceState.SUCCESS, end_date=instant, 
task_outlets=[], outlet_events=[]
             ),
             log=mock.ANY,
         )
@@ -1454,7 +1470,7 @@ class TestRuntimeTaskInstance:
             finalize(
                 runtime_ti,
                 log=mock.MagicMock(),
-                state=TerminalTIState.SUCCESS,
+                state=TaskInstanceState.SUCCESS,
                 context=runtime_ti.get_template_context(),
             )
             mock_xcom_set.assert_called_once_with(
@@ -1491,7 +1507,7 @@ class TestRuntimeTaskInstance:
 
         finalize(
             runtime_ti,
-            state=TerminalTIState.SUCCESS,
+            state=TaskInstanceState.SUCCESS,
             context=runtime_ti.get_template_context(),
             log=mock.MagicMock(),
         )
@@ -1862,7 +1878,7 @@ class TestDagParamRuntime:
 
         mock_supervisor_comms.send_request.assert_called_once_with(
             msg=SucceedTask(
-                state=TerminalTIState.SUCCESS, end_date=instant, 
task_outlets=[], outlet_events=[]
+                state=TaskInstanceState.SUCCESS, end_date=instant, 
task_outlets=[], outlet_events=[]
             ),
             log=mock.ANY,
         )
@@ -1891,7 +1907,7 @@ class TestDagParamRuntime:
         run(runtime_ti, context=runtime_ti.get_template_context(), 
log=mock.MagicMock())
         mock_supervisor_comms.send_request.assert_called_once_with(
             msg=SucceedTask(
-                state=TerminalTIState.SUCCESS, end_date=instant, 
task_outlets=[], outlet_events=[]
+                state=TaskInstanceState.SUCCESS, end_date=instant, 
task_outlets=[], outlet_events=[]
             ),
             log=mock.ANY,
         )
@@ -1918,7 +1934,7 @@ class TestDagParamRuntime:
         run(runtime_ti, context=runtime_ti.get_template_context(), 
log=mock.MagicMock())
         mock_supervisor_comms.send_request.assert_called_once_with(
             msg=SucceedTask(
-                state=TerminalTIState.SUCCESS, end_date=instant, 
task_outlets=[], outlet_events=[]
+                state=TaskInstanceState.SUCCESS, end_date=instant, 
task_outlets=[], outlet_events=[]
             ),
             log=mock.ANY,
         )
@@ -1953,7 +1969,7 @@ class TestDagParamRuntime:
 
         mock_supervisor_comms.send_request.assert_called_once_with(
             msg=SucceedTask(
-                state=TerminalTIState.SUCCESS, end_date=instant, 
task_outlets=[], outlet_events=[]
+                state=TaskInstanceState.SUCCESS, end_date=instant, 
task_outlets=[], outlet_events=[]
             ),
             log=mock.ANY,
         )
@@ -1992,7 +2008,7 @@ class TestDagParamRuntime:
 
         mock_supervisor_comms.send_request.assert_called_once_with(
             msg=SucceedTask(
-                state=TerminalTIState.SUCCESS, end_date=instant, 
task_outlets=[], outlet_events=[]
+                state=TaskInstanceState.SUCCESS, end_date=instant, 
task_outlets=[], outlet_events=[]
             ),
             log=mock.ANY,
         )
@@ -2023,7 +2039,7 @@ class TestDagParamRuntime:
 
         mock_supervisor_comms.send_request.assert_any_call(
             msg=SucceedTask(
-                state=TerminalTIState.SUCCESS, end_date=instant, 
task_outlets=[], outlet_events=[]
+                state=TaskInstanceState.SUCCESS, end_date=instant, 
task_outlets=[], outlet_events=[]
             ),
             log=mock.ANY,
         )
@@ -2195,28 +2211,28 @@ class TestTaskRunnerCallsCallbacks:
             pytest.param(
                 _execute_success,
                 False,
-                TerminalTIState.SUCCESS,
+                TaskInstanceState.SUCCESS,
                 ["on-execute callback", "execute success", "on-success 
callback"],
                 id="success",
             ),
             pytest.param(
                 _execute_skipped,
                 False,
-                TerminalTIState.SKIPPED,
+                TaskInstanceState.SKIPPED,
                 ["on-execute callback", "execute skipped", "on-skipped 
callback"],
                 id="skipped",
             ),
             pytest.param(
                 _execute_failure,
                 False,
-                TerminalTIState.FAILED,
+                TaskInstanceState.FAILED,
                 ["on-execute callback", "execute failure", "on-failure 
callback"],
                 id="failure",
             ),
             pytest.param(
                 _execute_failure,
                 True,
-                IntermediateTIState.UP_FOR_RETRY,
+                TaskInstanceState.UP_FOR_RETRY,
                 ["on-execute callback", "execute failure", "on-retry 
callback"],
                 id="retry",
             ),
@@ -2263,7 +2279,7 @@ class TestTaskRunnerCallsCallbacks:
                 "on_success_callback",
                 _execute_success,
                 False,
-                TerminalTIState.SUCCESS,
+                TaskInstanceState.SUCCESS,
                 ["on-execute 1", "on-execute 3", "execute success", 
"on-success 1", "on-success 3"],
                 [],
                 id="success",
@@ -2272,7 +2288,7 @@ class TestTaskRunnerCallsCallbacks:
                 "on_skipped_callback",
                 _execute_skipped,
                 False,
-                TerminalTIState.SKIPPED,
+                TaskInstanceState.SKIPPED,
                 ["on-execute 1", "on-execute 3", "execute skipped", 
"on-skipped 1", "on-skipped 3"],
                 [],
                 id="skipped",
@@ -2281,7 +2297,7 @@ class TestTaskRunnerCallsCallbacks:
                 "on_failure_callback",
                 _execute_failure,
                 False,
-                TerminalTIState.FAILED,
+                TaskInstanceState.FAILED,
                 ["on-execute 1", "on-execute 3", "execute failure", 
"on-failure 1", "on-failure 3"],
                 [(1, mock.call("Task failed with exception"))],
                 id="failure",
@@ -2290,7 +2306,7 @@ class TestTaskRunnerCallsCallbacks:
                 "on_retry_callback",
                 _execute_failure,
                 True,
-                IntermediateTIState.UP_FOR_RETRY,
+                TaskInstanceState.UP_FOR_RETRY,
                 ["on-execute 1", "on-execute 3", "execute failure", "on-retry 
1", "on-retry 3"],
                 [(1, mock.call("Task failed with exception"))],
                 id="retry",
@@ -2555,7 +2571,7 @@ class TestTriggerDagRunOperator:
     @pytest.mark.parametrize(
         ["allowed_states", "failed_states", "intermediate_state"],
         [
-            ([DagRunState.SUCCESS], None, IntermediateTIState.DEFERRED),
+            ([DagRunState.SUCCESS], None, TaskInstanceState.DEFERRED),
         ],
     )
     def test_handle_trigger_dag_run_deferred(


Reply via email to