Skip to content

Commit fa54630

Browse files
uranusjrSubham-KRLX
authored andcommitted
Remove TaskInstance and TaskLogReader unused methods (apache#59922)
1 parent f421433 commit fa54630

54 files changed

Lines changed: 335 additions & 767 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.
Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
Methods removed from TaskInstance
22

3-
On class ``TaskInstance``, functions ``run()``, ``render_templates()``, and
4-
private members related to them have been removed. The class has been
5-
considered internal since 3.0, and should not be relied on in user code.
3+
On class ``TaskInstance``, functions ``run()``, ``render_templates()``,
4+
``get_template_context()``, and private members related to them have been
5+
removed. The class has been considered internal since 3.0, and should not be
6+
relied on in user code.

airflow-core/src/airflow/cli/commands/task_command.py

Lines changed: 35 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,8 @@
6262

6363
from sqlalchemy.orm.session import Session
6464

65+
from airflow.sdk import Context
66+
from airflow.sdk.types import Operator as SdkOperator
6567
from airflow.serialization.definitions.mappedoperator import Operator
6668

6769
CreateIfNecessary = Literal[False, "db", "memory"]
@@ -224,6 +226,24 @@ def _get_ti(
224226
return ti, dr_created
225227

226228

229+
def _get_template_context(ti: TaskInstance, task: SdkOperator) -> Context:
230+
from airflow.api_fastapi.execution_api.datamodels.taskinstance import DagRun, TaskInstance, TIRunContext
231+
from airflow.sdk.execution_time.task_runner import RuntimeTaskInstance
232+
233+
runtime_ti = RuntimeTaskInstance.model_construct(
234+
**TaskInstance.model_validate(ti, from_attributes=True).model_dump(exclude_unset=True),
235+
task=task,
236+
_ti_context_from_server=TIRunContext(
237+
dag_run=DagRun.model_validate(ti.dag_run, from_attributes=True),
238+
max_tries=ti.max_tries,
239+
variables=[],
240+
connections=[],
241+
xcom_keys_to_clear=[],
242+
),
243+
)
244+
return runtime_ti.get_template_context()
245+
246+
227247
class TaskCommandMarker:
228248
"""Marker for listener hooks, to properly detect from which component they are called."""
229249

@@ -441,27 +461,21 @@ def task_render(args, dag: DAG | None = None) -> None:
441461
create_if_necessary="memory",
442462
)
443463

444-
with create_session() as session:
445-
context = ti.get_template_context(session=session)
446-
task = sdk_dag.get_task(args.task_id)
447-
# TODO (GH-52141): After sdk separation, ti.get_template_context() would
448-
# contain serialized operators, but we need the real operators for
449-
# rendering. This does not make sense and eventually we should rewrite
450-
# this entire function so "ti" is a RuntimeTaskInstance instead, but for
451-
# now we'll just manually fix it to contain the right objects.
452-
context["task"] = context["ti"].task = task
453-
task.render_template_fields(context)
454-
for attr in context["task"].template_fields:
455-
print(
456-
textwrap.dedent(
457-
f"""\
458-
# ----------------------------------------------------------
459-
# property: {attr}
460-
# ----------------------------------------------------------
461-
"""
462-
)
463-
+ str(getattr(context["task"], attr)) # This shouldn't be dedented.
464-
)
464+
task = sdk_dag.get_task(args.task_id)
465+
context = _get_template_context(ti, task)
466+
task.render_template_fields(context)
467+
for attr in task.template_fields:
468+
print(
469+
textwrap.dedent(
470+
f"""\
471+
# ----------------------------------------------------------
472+
# property: {attr}
473+
# ----------------------------------------------------------
474+
"""
475+
),
476+
getattr(context["task"], attr), # This shouldn't be dedented.
477+
sep="",
478+
)
465479

466480

467481
@cli_utils.action_cli(check_db=False)

airflow-core/src/airflow/models/taskinstance.py

Lines changed: 1 addition & 159 deletions
Original file line numberDiff line numberDiff line change
@@ -27,13 +27,11 @@
2727
from collections import defaultdict
2828
from collections.abc import Collection, Iterable
2929
from datetime import datetime, timedelta
30-
from functools import cache
3130
from typing import TYPE_CHECKING, Any
3231
from urllib.parse import quote
3332

3433
import attrs
3534
import dill
36-
import lazy_object_proxy
3735
import uuid6
3836
from sqlalchemy import (
3937
JSON,
@@ -72,7 +70,7 @@
7270
from airflow.assets.manager import asset_manager
7371
from airflow.configuration import conf
7472
from airflow.listeners.listener import get_listener_manager
75-
from airflow.models.asset import AssetEvent, AssetModel
73+
from airflow.models.asset import AssetModel
7674
from airflow.models.base import Base, StringID, TaskInstanceDependencies
7775
from airflow.models.dag_version import DagVersion
7876

@@ -106,7 +104,6 @@
106104
from datetime import datetime
107105
from typing import Literal
108106

109-
import pendulum
110107
from sqlalchemy.engine import Connection as SAConnection, Engine
111108
from sqlalchemy.orm.session import Session
112109
from sqlalchemy.sql import Update
@@ -115,7 +112,6 @@
115112
from airflow.api_fastapi.execution_api.datamodels.asset import AssetProfile
116113
from airflow.models.dag import DagModel
117114
from airflow.models.dagrun import DagRun
118-
from airflow.sdk import Context
119115
from airflow.serialization.definitions.dag import SerializedDAG
120116
from airflow.serialization.definitions.mappedoperator import Operator
121117
from airflow.serialization.definitions.taskgroup import SerializedTaskGroup
@@ -1567,160 +1563,6 @@ def is_eligible_to_retry(self) -> bool:
15671563

15681564
return bool(self.task.retries and self.try_number <= self.max_tries)
15691565

1570-
# TODO (GH-52141): We should remove this entire function (only makes sense at runtime).
1571-
def get_template_context(
1572-
self,
1573-
session: Session | None = None,
1574-
ignore_param_exceptions: bool = True,
1575-
) -> Context:
1576-
"""
1577-
Return TI Context.
1578-
1579-
:param session: SQLAlchemy ORM Session
1580-
:param ignore_param_exceptions: flag to suppress value exceptions while initializing the ParamsDict
1581-
"""
1582-
# Do not use provide_session here -- it expunges everything on exit!
1583-
if not session:
1584-
session = settings.get_session()()
1585-
1586-
from airflow.exceptions import NotMapped
1587-
from airflow.sdk.api.datamodels._generated import (
1588-
DagRun as DagRunSDK,
1589-
PrevSuccessfulDagRunResponse,
1590-
TIRunContext,
1591-
)
1592-
from airflow.sdk.definitions.param import process_params
1593-
from airflow.sdk.execution_time.context import InletEventsAccessors
1594-
from airflow.sdk.execution_time.task_runner import RuntimeTaskInstance
1595-
from airflow.serialization.definitions.mappedoperator import get_mapped_ti_count
1596-
from airflow.utils.context import (
1597-
ConnectionAccessor,
1598-
OutletEventAccessors,
1599-
VariableAccessor,
1600-
)
1601-
1602-
if TYPE_CHECKING:
1603-
assert session
1604-
1605-
def _get_dagrun(session: Session) -> DagRun:
1606-
dag_run = self.get_dagrun(session)
1607-
if dag_run in session:
1608-
return dag_run
1609-
# The dag_run may not be attached to the session anymore since the
1610-
# code base is over-zealous with use of session.expunge_all().
1611-
# Re-attach it if the relation is not loaded so we can load it when needed.
1612-
info: Any = inspect(dag_run)
1613-
if info.attrs.consumed_asset_events.loaded_value is not NO_VALUE:
1614-
return dag_run
1615-
# If dag_run is not flushed to db at all (e.g. CLI commands using
1616-
# in-memory objects for ad-hoc operations), just set the value manually.
1617-
if not info.has_identity:
1618-
dag_run.consumed_asset_events = []
1619-
return dag_run
1620-
return session.merge(dag_run, load=False)
1621-
1622-
task: Any = self.task
1623-
dag = task.dag
1624-
dag_run = _get_dagrun(session)
1625-
1626-
validated_params = process_params(dag, task, dag_run.conf, suppress_exception=ignore_param_exceptions)
1627-
runtime_ti = RuntimeTaskInstance.model_construct(
1628-
id=self.id,
1629-
task_id=self.task_id,
1630-
dag_id=self.dag_id,
1631-
run_id=self.run_id,
1632-
try_numer=self.try_number,
1633-
map_index=self.map_index,
1634-
task=self.task,
1635-
max_tries=self.max_tries,
1636-
hostname=self.hostname,
1637-
_ti_context_from_server=TIRunContext(
1638-
dag_run=DagRunSDK.model_validate(dag_run, from_attributes=True),
1639-
max_tries=self.max_tries,
1640-
should_retry=self.is_eligible_to_retry(),
1641-
),
1642-
start_date=self.start_date,
1643-
dag_version_id=self.dag_version_id,
1644-
)
1645-
1646-
context: Context = runtime_ti.get_template_context()
1647-
1648-
@cache # Prevent multiple database access.
1649-
def _get_previous_dagrun_success() -> PrevSuccessfulDagRunResponse:
1650-
dr_from_db = self.get_previous_dagrun(state=DagRunState.SUCCESS, session=session)
1651-
if dr_from_db:
1652-
return PrevSuccessfulDagRunResponse.model_validate(dr_from_db, from_attributes=True)
1653-
return PrevSuccessfulDagRunResponse()
1654-
1655-
def get_prev_data_interval_start_success() -> pendulum.DateTime | None:
1656-
return timezone.coerce_datetime(_get_previous_dagrun_success().data_interval_start)
1657-
1658-
def get_prev_data_interval_end_success() -> pendulum.DateTime | None:
1659-
return timezone.coerce_datetime(_get_previous_dagrun_success().data_interval_end)
1660-
1661-
def get_prev_start_date_success() -> pendulum.DateTime | None:
1662-
return timezone.coerce_datetime(_get_previous_dagrun_success().start_date)
1663-
1664-
def get_prev_end_date_success() -> pendulum.DateTime | None:
1665-
return timezone.coerce_datetime(_get_previous_dagrun_success().end_date)
1666-
1667-
def get_triggering_events() -> dict[str, list[AssetEvent]]:
1668-
asset_events = dag_run.consumed_asset_events
1669-
triggering_events: dict[str, list[AssetEvent]] = defaultdict(list)
1670-
for event in asset_events:
1671-
if event.asset:
1672-
triggering_events[event.asset.uri].append(event)
1673-
1674-
return triggering_events
1675-
1676-
# NOTE: If you add to this dict, make sure to also update the following:
1677-
# * Context in task-sdk/src/airflow/sdk/definitions/context.py
1678-
# * KNOWN_CONTEXT_KEYS in airflow/utils/context.py
1679-
# * Table in docs/apache-airflow/templates-ref.rst
1680-
1681-
context.update(
1682-
{
1683-
"outlet_events": OutletEventAccessors(),
1684-
"inlet_events": InletEventsAccessors(task.inlets),
1685-
"params": validated_params,
1686-
"prev_data_interval_start_success": get_prev_data_interval_start_success(),
1687-
"prev_data_interval_end_success": get_prev_data_interval_end_success(),
1688-
"prev_start_date_success": get_prev_start_date_success(),
1689-
"prev_end_date_success": get_prev_end_date_success(),
1690-
"test_mode": self.test_mode,
1691-
# ti/task_instance are added here for ti.xcom_{push,pull}
1692-
"task_instance": self,
1693-
"ti": self,
1694-
"triggering_asset_events": lazy_object_proxy.Proxy(get_triggering_events),
1695-
"var": {
1696-
"json": VariableAccessor(deserialize_json=True),
1697-
"value": VariableAccessor(deserialize_json=False),
1698-
},
1699-
"conn": ConnectionAccessor(),
1700-
}
1701-
)
1702-
1703-
try:
1704-
expanded_ti_count: int | None = get_mapped_ti_count(task, self.run_id, session=session)
1705-
context["expanded_ti_count"] = expanded_ti_count
1706-
if expanded_ti_count:
1707-
setattr(
1708-
self,
1709-
"_upstream_map_indexes",
1710-
{
1711-
upstream.task_id: self.get_relevant_upstream_map_indexes(
1712-
upstream,
1713-
expanded_ti_count,
1714-
session=session,
1715-
)
1716-
for upstream in task.upstream_list
1717-
},
1718-
)
1719-
except NotMapped:
1720-
pass
1721-
1722-
return context
1723-
17241566
def set_duration(self) -> None:
17251567
"""Set task instance duration."""
17261568
if self.end_date and self.start_date:

airflow-core/src/airflow/serialization/enums.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,6 @@ class DagAttributeTypes(str, Enum):
6363
ASSET_UNIQUE_KEY = "asset_unique_key"
6464
ASSET_ALIAS_UNIQUE_KEY = "asset_alias_unique_key"
6565
CONNECTION = "connection"
66-
TASK_CONTEXT = "task_context"
6766
ARG_NOT_SET = "arg_not_set"
6867
TASK_CALLBACK_REQUEST = "task_callback_request"
6968
DAG_CALLBACK_REQUEST = "dag_callback_request"

airflow-core/src/airflow/serialization/serialized_objects.py

Lines changed: 1 addition & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,6 @@
4040
from dateutil import relativedelta
4141
from pendulum.tz.timezone import FixedTimezone, Timezone
4242

43-
from airflow import macros
4443
from airflow._shared.module_loading import import_string, qualname
4544
from airflow._shared.timezones.timezone import from_timestamp, parse_timezone, utcnow
4645
from airflow.callbacks.callback_requests import DagCallbackRequest, TaskCallbackRequest
@@ -100,7 +99,6 @@
10099
from airflow.timetables.base import DagRunInfo, Timetable
101100
from airflow.triggers.base import BaseTrigger, StartTriggerArgs
102101
from airflow.utils.code_utils import get_python_source
103-
from airflow.utils.context import ConnectionAccessor, Context, VariableAccessor
104102
from airflow.utils.db import LazySelectSequence
105103

106104
if TYPE_CHECKING:
@@ -654,12 +652,6 @@ def serialize(
654652
elif isinstance(var, MappedArgument):
655653
data = {"input": encode_expand_input(var._input), "key": var._key}
656654
return cls._encode(data, type_=DAT.MAPPED_ARGUMENT)
657-
elif var.__class__ == Context:
658-
d = {}
659-
for k, v in var.items():
660-
obj = cls.serialize(v, strict=strict)
661-
d[str(k)] = obj
662-
return cls._encode(d, type_=DAT.TASK_CONTEXT)
663655
else:
664656
return cls.default_serialization(strict, var)
665657

@@ -686,21 +678,7 @@ def deserialize(cls, encoded_var: Any) -> Any:
686678
raise ValueError(f"The encoded_var should be dict and is {type(encoded_var)}")
687679
var = encoded_var[Encoding.VAR]
688680
type_ = encoded_var[Encoding.TYPE]
689-
if type_ == DAT.TASK_CONTEXT:
690-
d = {}
691-
for k, v in var.items():
692-
if k == "task": # todo: add `_encode` of Operator so we don't need this
693-
continue
694-
d[k] = cls.deserialize(v)
695-
d["task"] = d["task_instance"].task # todo: add `_encode` of Operator so we don't need this
696-
d["macros"] = macros
697-
d["var"] = {
698-
"json": VariableAccessor(deserialize_json=True),
699-
"value": VariableAccessor(deserialize_json=False),
700-
}
701-
d["conn"] = ConnectionAccessor()
702-
return Context(**d)
703-
elif type_ == DAT.DICT:
681+
if type_ == DAT.DICT:
704682
return {k: cls.deserialize(v) for k, v in var.items()}
705683
elif type_ == DAT.ASSET_EVENT_ACCESSORS:
706684
return decode_outlet_event_accessors(var)

0 commit comments

Comments
 (0)