Airflow 3 moved the classes DAG authors use into two places: airflow.sdk for the building blocks
(DAG, task, Variable, Param…) and provider packages for the operators, mostly
apache-airflow-providers-standard. Many old paths still work with a deprecation warning, a few are
gone. The tables show which is which on Airflow 3.3.2.
Copy and paste
The new imports for a typical DAG file:
import pendulum
from airflow.sdk import DAG, dag, task, task_group, TaskGroup, Variable, Param, Asset, TriggerRule
from airflow.sdk import get_current_context
from airflow.providers.standard.operators.bash import BashOperator
from airflow.providers.standard.operators.python import PythonOperator, BranchPythonOperator
from airflow.providers.standard.operators.empty import EmptyOperator
from airflow.providers.standard.operators.trigger_dagrun import TriggerDagRunOperator
from airflow.providers.standard.sensors.external_task import ExternalTaskSensor
from airflow.providers.smtp.operators.smtp import EmailOperator
Fix a folder of DAGs with ruff, then review the changes:
pip install "ruff>=0.13.1"
ruff check dags/ --select AIR3,F401 --fix --unsafe-fixes
Core classes
| Airflow 2 | Airflow 3 | Old path on 3.3.2 |
|---|---|---|
from airflow import DAG |
from airflow.sdk import DAG |
works |
from airflow.models.dag import DAG |
from airflow.sdk import DAG |
warning |
from airflow.decorators import dag, task, task_group |
from airflow.sdk import dag, task, task_group |
warning |
from airflow.models import Variable |
from airflow.sdk import Variable |
works |
from airflow.models.param import Param |
from airflow.sdk import Param |
warning |
from airflow.models.baseoperator import BaseOperator, chain |
from airflow.sdk import BaseOperator, chain |
warning |
from airflow.hooks.base import BaseHook |
from airflow.sdk import BaseHook |
warning |
from airflow.sensors.base import BaseSensorOperator |
from airflow.sdk import BaseSensorOperator |
warning |
from airflow.utils.task_group import TaskGroup |
from airflow.sdk import TaskGroup |
warning |
from airflow.operators.python import get_current_context |
from airflow.sdk import get_current_context |
warning |
from airflow.utils.context import Context |
from airflow.sdk import Context |
warning |
from airflow.utils.trigger_rule import TriggerRule |
from airflow.sdk import TriggerRule |
warning |
from airflow.models.connection import Connection |
from airflow.sdk import Connection |
works |
from airflow.models.xcom_arg import XComArg |
from airflow.sdk import XComArg |
warning |
from airflow.utils.edgemodifier import Label |
from airflow.sdk import Label |
works |
from airflow.exceptions import AirflowSkipException |
from airflow.sdk.exceptions import AirflowSkipException |
warning |
from airflow.datasets import Dataset |
from airflow.sdk import Asset |
removed |
from airflow.utils.dates import days_ago |
pendulum.datetime(2026, 1, 1, tz="UTC") |
removed |
Operators and sensors
| Airflow 2 | Airflow 3 | Old path on 3.3.2 |
|---|---|---|
airflow.operators.bash.BashOperator |
airflow.providers.standard.operators.bash.BashOperator |
warning |
airflow.operators.python.PythonOperator (and Branch..., ShortCircuit...) |
airflow.providers.standard.operators.python |
warning |
airflow.operators.empty.EmptyOperator |
airflow.providers.standard.operators.empty.EmptyOperator |
warning |
airflow.operators.dummy.DummyOperator |
airflow.providers.standard.operators.empty.EmptyOperator |
removed |
airflow.operators.trigger_dagrun.TriggerDagRunOperator |
airflow.providers.standard.operators.trigger_dagrun.TriggerDagRunOperator |
warning |
airflow.sensors.external_task.ExternalTaskSensor |
airflow.providers.standard.sensors.external_task.ExternalTaskSensor |
warning |
airflow.sensors.filesystem.FileSensor |
airflow.providers.standard.sensors.filesystem.FileSensor |
warning |
airflow.operators.email.EmailOperator |
airflow.providers.smtp.operators.smtp.EmailOperator |
warning |
airflow.operators.subdag.SubDagOperator |
a TaskGroup |
removed |
“Warning” means the import works but logs a DeprecatedImportWarning such as
The airflow.operators.bash.BashOperator attribute is deprecated. The upgrade guide says those paths
will be removed in a future version, so change them now.
Not only imports
| Airflow 2 | Airflow 3 |
|---|---|
schedule_interval="@daily" |
schedule="@daily" |
timetable=MyTimetable() |
schedule=MyTimetable() |
{{ execution_date }}, context["execution_date"] |
{{ logical_date }}, context["logical_date"] |
{{ yesterday_ds }}, {{ tomorrow_ds }} |
{{ macros.ds_add(ds, -1) }}, {{ macros.ds_add(ds, 1) }} |
catchup defaults to True |
catchup defaults to False |
| A daily run is dated the day before | A daily run is dated the day it runs |
schedule_interval and timetable are not just deprecated: DAG(...) rejects them with a
TypeError. The last row changes which data a DAG loads; see
why your DAG processes yesterday’s data.
What ruff finds and what it misses
Ruff’s AIR rules, from version 0.13.1, flag the imports and arguments above: AIR301 and AIR302 for
what is removed or moved in Airflow 3, AIR311 and AIR312 for what still works but is deprecated. On
an Airflow 2 DAG:
dags/old_style.py:16:5: AIR301 [*] `schedule_interval` is removed in Airflow 3.0
dags/old_style.py:19:13: AIR302 [*] `airflow.operators.dummy.DummyOperator` is moved into `standard` provider in Airflow 3.0;
dags/old_style.py:20:11: AIR312 `airflow.operators.bash.BashOperator` is deprecated and moved into `standard` provider in Airflow 3.0;
Two details from running it on real files:
- Keep F401 in the command.
--fixadds the new import but leaves the old one in place. ForDummyOperatorthat old line fails on Airflow 3 even though nothing uses it.F401removes unused imports, which cleans it up. It also removes your other unused imports. - Ruff does not read strings.
context["execution_date"]and{{ execution_date }}inside a template are not flagged. Search for them:
grep -rnE "execution_date|yesterday_ds|tomorrow_ds|prev_ds|next_ds" dags/
Tested with Airflow 3.3.2 and ruff 0.17.