1# Licensed to the Apache Software Foundation (ASF) under one
2# or more contributor license agreements. See the NOTICE file
3# distributed with this work for additional information
4# regarding copyright ownership. The ASF licenses this file
5# to you under the Apache License, Version 2.0 (the
6# "License"); you may not use this file except in compliance
7# with the License. You may obtain a copy of the License at
8#
9# http://www.apache.org/licenses/LICENSE-2.0
10#
11# Unless required by applicable law or agreed to in writing,
12# software distributed under the License is distributed on an
13# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14# KIND, either express or implied. See the License for the
15# specific language governing permissions and limitations
16# under the License.
17"""
18Airflow compatibility imports for seamless migration from Airflow 2 to Airflow 3.
19
20This module provides lazy imports that automatically try Airflow 3 paths first,
21then fall back to Airflow 2 paths, enabling code to work across both versions.
22"""
23
24from __future__ import annotations
25
26from typing import TYPE_CHECKING
27
28from airflow.providers.common.compat.version_compat import AIRFLOW_V_3_0_PLUS
29
30if TYPE_CHECKING:
31 import airflow.sdk.io as io # noqa: F401
32 import airflow.sdk.timezone as timezone # noqa: F401
33 from airflow.models.xcom import XCOM_RETURN_KEY as XCOM_RETURN_KEY
34 from airflow.sdk import (
35 DAG as DAG,
36 Asset as Asset,
37 AssetAlias as AssetAlias,
38 AssetAll as AssetAll,
39 AssetAny as AssetAny,
40 BaseHook as BaseHook,
41 BaseNotifier as BaseNotifier,
42 BaseOperator as BaseOperator,
43 BaseOperatorLink as BaseOperatorLink,
44 BaseSensorOperator as BaseSensorOperator,
45 Connection as Connection,
46 Context as Context,
47 DagRunState as DagRunState,
48 EdgeModifier as EdgeModifier,
49 Label as Label,
50 Metadata as Metadata,
51 ObjectStoragePath as ObjectStoragePath,
52 Param as Param,
53 PokeReturnValue as PokeReturnValue,
54 TaskGroup as TaskGroup,
55 TaskInstanceState as TaskInstanceState,
56 TriggerRule as TriggerRule,
57 Variable as Variable,
58 WeightRule as WeightRule,
59 XComArg as XComArg,
60 chain as chain,
61 chain_linear as chain_linear,
62 cross_downstream as cross_downstream,
63 dag as dag,
64 get_current_context as get_current_context,
65 get_parsing_context as get_parsing_context,
66 setup as setup,
67 task as task,
68 task_group as task_group,
69 teardown as teardown,
70 )
71 from airflow.sdk._shared.listeners import hookimpl as hookimpl
72 from airflow.sdk._shared.observability.metrics.stats import Stats as Stats
73 from airflow.sdk.bases.decorator import (
74 DecoratedMappedOperator as DecoratedMappedOperator,
75 DecoratedOperator as DecoratedOperator,
76 TaskDecorator as TaskDecorator,
77 get_unique_task_id as get_unique_task_id,
78 task_decorator_factory as task_decorator_factory,
79 )
80 from airflow.sdk.bases.sensor import poke_mode_only as poke_mode_only
81 from airflow.sdk.configuration import conf as conf
82 from airflow.sdk.definitions.context import context_merge as context_merge
83 from airflow.sdk.definitions.mappedoperator import MappedOperator as MappedOperator
84 from airflow.sdk.definitions.template import literal as literal
85 from airflow.sdk.exceptions import (
86 AirflowConfigException as AirflowConfigException,
87 AirflowException as AirflowException,
88 AirflowFailException as AirflowFailException,
89 AirflowNotFoundException as AirflowNotFoundException,
90 AirflowOptionalProviderFeatureException as AirflowOptionalProviderFeatureException,
91 AirflowSensorTimeout as AirflowSensorTimeout,
92 AirflowSkipException as AirflowSkipException,
93 AirflowTaskTimeout as AirflowTaskTimeout,
94 ParamValidationError as ParamValidationError,
95 TaskDeferred as TaskDeferred,
96 XComNotFound as XComNotFound,
97 )
98 from airflow.sdk.listener import get_listener_manager as get_listener_manager
99 from airflow.sdk.log import redact as redact
100 from airflow.sdk.plugins_manager import AirflowPlugin as AirflowPlugin
101
102 # Airflow 3-only exceptions (conditionally imported)
103 if AIRFLOW_V_3_0_PLUS:
104 from airflow.sdk.exceptions import (
105 DagRunTriggerException as DagRunTriggerException,
106 DownstreamTasksSkipped as DownstreamTasksSkipped,
107 )
108 from airflow.sdk.execution_time.context import (
109 AIRFLOW_VAR_NAME_FORMAT_MAPPING as AIRFLOW_VAR_NAME_FORMAT_MAPPING,
110 context_to_airflow_vars as context_to_airflow_vars,
111 )
112 from airflow.sdk.execution_time.timeout import timeout as timeout
113 from airflow.sdk.execution_time.xcom import XCom as XCom
114 from airflow.sdk.types import TaskInstanceKey as TaskInstanceKey
115
116
117from airflow.providers.common.compat._compat_utils import create_module_getattr
118
119# Rename map for classes that changed names between Airflow 2.x and 3.x
120# Format: new_name -> (new_path, old_path, old_name)
121_RENAME_MAP: dict[str, tuple[str, str, str]] = {
122 # Assets: Dataset -> Asset rename in Airflow 3.0
123 "Asset": ("airflow.sdk", "airflow.datasets", "Dataset"),
124 "AssetAlias": ("airflow.sdk", "airflow.datasets", "DatasetAlias"),
125 "AssetAll": ("airflow.sdk", "airflow.datasets", "DatasetAll"),
126 "AssetAny": ("airflow.sdk", "airflow.datasets", "DatasetAny"),
127}
128
129# Import map for classes/functions/constants
130# Format: class_name -> module_path(s)
131# - str: single module path (no fallback)
132# - tuple[str, ...]: multiple module paths (try in order, newest first)
133_IMPORT_MAP: dict[str, str | tuple[str, ...]] = {
134 # ============================================================================
135 # Hooks
136 # ============================================================================
137 "BaseHook": ("airflow.sdk", "airflow.hooks.base"),
138 # ============================================================================
139 # Sensors
140 # ============================================================================
141 "BaseSensorOperator": ("airflow.sdk", "airflow.sensors.base"),
142 "PokeReturnValue": ("airflow.sdk", "airflow.sensors.base"),
143 "poke_mode_only": ("airflow.sdk.bases.sensor", "airflow.sensors.base"),
144 # ============================================================================
145 # Operators
146 # ============================================================================
147 "BaseOperator": ("airflow.sdk", "airflow.models.baseoperator"),
148 # ============================================================================
149 # Decorators
150 # ============================================================================
151 "task": ("airflow.sdk", "airflow.decorators"),
152 "dag": ("airflow.sdk", "airflow.decorators"),
153 "task_group": ("airflow.sdk", "airflow.decorators"),
154 "setup": ("airflow.sdk", "airflow.decorators"),
155 "teardown": ("airflow.sdk", "airflow.decorators"),
156 "TaskDecorator": ("airflow.sdk.bases.decorator", "airflow.decorators"),
157 "task_decorator_factory": ("airflow.sdk.bases.decorator", "airflow.decorators.base"),
158 "get_unique_task_id": ("airflow.sdk.bases.decorator", "airflow.decorators.base"),
159 # ============================================================================
160 # Models
161 # ============================================================================
162 "Connection": ("airflow.sdk", "airflow.models.connection"),
163 "Variable": ("airflow.sdk", "airflow.models.variable"),
164 "XCom": ("airflow.sdk.execution_time.xcom", "airflow.models.xcom"),
165 "DAG": ("airflow.sdk", "airflow.models.dag"),
166 "Param": ("airflow.sdk", "airflow.models.param"),
167 "XComArg": ("airflow.sdk", "airflow.models.xcom_arg"),
168 "DecoratedOperator": ("airflow.sdk.bases.decorator", "airflow.decorators.base"),
169 "DecoratedMappedOperator": ("airflow.sdk.bases.decorator", "airflow.decorators.base"),
170 "MappedOperator": ("airflow.sdk.definitions.mappedoperator", "airflow.models.mappedoperator"),
171 # ============================================================================
172 # Assets (Dataset → Asset rename in Airflow 3.0)
173 # ============================================================================
174 # Note: Asset, AssetAlias, AssetAll, AssetAny are handled by _RENAME_MAP
175 # Metadata moved from airflow.datasets.metadata (2.x) to airflow.sdk (3.x)
176 "Metadata": ("airflow.sdk", "airflow.datasets.metadata"),
177 # ============================================================================
178 # Notifiers
179 # ============================================================================
180 "BaseNotifier": ("airflow.sdk", "airflow.notifications.basenotifier"),
181 # ============================================================================
182 # Plugins
183 # ============================================================================
184 "AirflowPlugin": ("airflow.sdk.plugins_manager", "airflow.plugins_manager"),
185 # ============================================================================
186 # Operator Links & Task Groups
187 # ============================================================================
188 "BaseOperatorLink": ("airflow.sdk", "airflow.models.baseoperatorlink"),
189 "TaskInstanceKey": ("airflow.sdk.types", "airflow.models.taskinstancekey"),
190 "TaskGroup": ("airflow.sdk", "airflow.utils.task_group"),
191 # ============================================================================
192 # Operator Utilities (chain, cross_downstream, etc.)
193 # ============================================================================
194 "chain": ("airflow.sdk", "airflow.models.baseoperator"),
195 "chain_linear": ("airflow.sdk", "airflow.models.baseoperator"),
196 "cross_downstream": ("airflow.sdk", "airflow.models.baseoperator"),
197 # ============================================================================
198 # Edge Modifiers & Labels
199 # ============================================================================
200 "EdgeModifier": ("airflow.sdk", "airflow.utils.edgemodifier"),
201 "Label": ("airflow.sdk", "airflow.utils.edgemodifier"),
202 # ============================================================================
203 # State Enums
204 # ============================================================================
205 "DagRunState": ("airflow.sdk", "airflow.utils.state"),
206 "TaskInstanceState": ("airflow.sdk", "airflow.utils.state"),
207 "TriggerRule": ("airflow.sdk", "airflow.utils.trigger_rule"),
208 "WeightRule": ("airflow.sdk", "airflow.utils.weight_rule"),
209 # ============================================================================
210 # IO & Storage
211 # ============================================================================
212 "ObjectStoragePath": ("airflow.sdk", "airflow.io.path"),
213 # ============================================================================
214 # Template Utilities
215 # ============================================================================
216 "literal": ("airflow.sdk.definitions.template", "airflow.utils.template"),
217 # ============================================================================
218 # Context & Utilities
219 # ============================================================================
220 "Context": ("airflow.sdk", "airflow.utils.context"),
221 "context_merge": ("airflow.sdk.definitions.context", "airflow.utils.context"),
222 "context_to_airflow_vars": ("airflow.sdk.execution_time.context", "airflow.utils.operator_helpers"),
223 "AIRFLOW_VAR_NAME_FORMAT_MAPPING": (
224 "airflow.sdk.execution_time.context",
225 "airflow.utils.operator_helpers",
226 ),
227 "get_current_context": ("airflow.sdk", "airflow.operators.python"),
228 "get_parsing_context": ("airflow.sdk", "airflow.utils.dag_parsing_context"),
229 # ============================================================================
230 # Timeout Utilities
231 # ============================================================================
232 "timeout": ("airflow.sdk.execution_time.timeout", "airflow.utils.timeout"),
233 # ============================================================================
234 # XCom & Task Communication
235 # ============================================================================
236 "XCOM_RETURN_KEY": "airflow.models.xcom",
237 # ============================================================================
238 # Exceptions (deprecated in airflow.exceptions, prefer SDK)
239 # ============================================================================
240 # Note: AirflowException and AirflowNotFoundException are not deprecated, but exposing them
241 # here keeps provider imports consistent across Airflow 2 and 3.
242 "AirflowException": ("airflow.sdk.exceptions", "airflow.exceptions"),
243 "AirflowFailException": ("airflow.sdk.exceptions", "airflow.exceptions"),
244 "AirflowNotFoundException": ("airflow.sdk.exceptions", "airflow.exceptions"),
245 "AirflowOptionalProviderFeatureException": ("airflow.sdk.exceptions", "airflow.exceptions"),
246 "AirflowSkipException": ("airflow.sdk.exceptions", "airflow.exceptions"),
247 "AirflowTaskTimeout": ("airflow.sdk.exceptions", "airflow.exceptions"),
248 "AirflowSensorTimeout": ("airflow.sdk.exceptions", "airflow.exceptions"),
249 "ParamValidationError": ("airflow.sdk.exceptions", "airflow.exceptions"),
250 "TaskDeferred": ("airflow.sdk.exceptions", "airflow.exceptions"),
251 "XComNotFound": ("airflow.sdk.exceptions", "airflow.exceptions"),
252 # ============================================================================
253 # Observability
254 # ============================================================================
255 "Stats": ("airflow.sdk._shared.observability.metrics.stats", "airflow.stats"),
256 # ============================================================================
257 # Secrets Masking
258 # ============================================================================
259 "redact": (
260 "airflow.sdk.log",
261 "airflow.sdk._shared.secrets_masker",
262 "airflow.sdk.execution_time.secrets_masker",
263 "airflow.utils.log.secrets_masker",
264 ),
265 # ============================================================================
266 # Listeners
267 # ============================================================================
268 "hookimpl": ("airflow.sdk._shared.listeners", "airflow.listeners"),
269 "get_listener_manager": ("airflow.sdk.listener", "airflow.listeners.listener"),
270 # Configuration
271 # ============================================================================
272 "conf": ("airflow.sdk.configuration", "airflow.configuration"),
273 "AirflowConfigException": ("airflow.sdk.exceptions", "airflow.exceptions"),
274}
275
276# Airflow 3-only exceptions (not available in Airflow 2)
277_AIRFLOW_3_ONLY_EXCEPTIONS: dict[str, tuple[str, ...]] = {
278 "DownstreamTasksSkipped": ("airflow.sdk.exceptions", "airflow.exceptions"),
279 "DagRunTriggerException": ("airflow.sdk.exceptions", "airflow.exceptions"),
280}
281
282# Add Airflow 3-only exceptions to _IMPORT_MAP if running Airflow 3+
283if AIRFLOW_V_3_0_PLUS:
284 _IMPORT_MAP.update(_AIRFLOW_3_ONLY_EXCEPTIONS)
285
286# Module map: module_name -> module_path(s)
287# For entire modules that have been moved (e.g., timezone)
288# Usage: from airflow.providers.common.compat.lazy_compat import timezone
289_MODULE_MAP: dict[str, str | tuple[str, ...]] = {
290 "timezone": ("airflow.sdk.timezone", "airflow.utils.timezone"),
291 "io": ("airflow.sdk.io", "airflow.io"),
292}
293
294# Use the shared utility to create __getattr__
295__getattr__ = create_module_getattr(
296 import_map=_IMPORT_MAP,
297 module_map=_MODULE_MAP,
298 rename_map=_RENAME_MAP,
299)
300
301__all__ = list(_RENAME_MAP.keys()) + list(_IMPORT_MAP.keys()) + list(_MODULE_MAP.keys())