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

kaxil pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/airflow.git


The following commit(s) were added to refs/heads/main by this push:
     new 1f5331de1fa Document the experimental Dag importer interface (#74333)
1f5331de1fa is described below

commit 1f5331de1fa652231fae3b0e3e31d4780f4936eb
Author: Dilnaz Amanzholova <[email protected]>
AuthorDate: Fri Oct 9 22:57:57 2026 +0200

    Document the experimental Dag importer interface (#74333)
---
 .../dag-importers.rst                              | 169 +++++++++++++++++++++
 .../dagfile-processing.rst                         |  10 +-
 .../docs/administration-and-deployment/index.rst   |   1 +
 airflow-core/docs/core-concepts/dags.rst           |   4 +-
 airflow-core/docs/public-airflow-interface.rst     |  16 ++
 task-sdk/docs/api.rst                              |  35 +++++
 6 files changed, 229 insertions(+), 6 deletions(-)

diff --git a/airflow-core/docs/administration-and-deployment/dag-importers.rst 
b/airflow-core/docs/administration-and-deployment/dag-importers.rst
new file mode 100644
index 00000000000..81b74576e76
--- /dev/null
+++ b/airflow-core/docs/administration-and-deployment/dag-importers.rst
@@ -0,0 +1,169 @@
+ .. Licensed to the Apache Software Foundation (ASF) under one
+    or more contributor license agreements.  See the NOTICE file
+    distributed with this work for additional information
+    regarding copyright ownership.  The ASF licenses this file
+    to you under the Apache License, Version 2.0 (the
+    "License"); you may not use this file except in compliance
+    with the License.  You may obtain a copy of the License at
+
+ ..   http://www.apache.org/licenses/LICENSE-2.0
+
+ .. Unless required by applicable law or agreed to in writing,
+    software distributed under the License is distributed on an
+    "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+    KIND, either express or implied.  See the License for the
+    specific language governing permissions and limitations
+    under the License.
+
+.. _dag-importers:
+
+Dag Importers
+=============
+
+|experimental|
+
+.. versionadded:: 3.4.0
+
+.. warning::
+
+    The Dag importer interface may still change in a minor release while it is 
being stabilized. If you
+    maintain a custom importer, check the release notes when you upgrade.
+
+A Dag importer turns the contents of a :doc:`Dag bundle <dag-bundles>` into 
Dags. Importers let Airflow
+load Dags from formats other than Python files, such as YAML or JSON pipeline 
definitions, without changes
+to the Dag processor, the scheduler, or the workers.
+
+Airflow ships with two importers:
+
+**airflow.sdk.importers.PythonDagImporter** (``.py`` and ``.pyc``)
+    Loads a Python file as a module and collects the Dags it defines, as 
described in
+    :ref:`concepts-dag-loading`.
+
+**airflow.sdk.importers.ZipImporter** (``.zip``)
+    Imports each Python file in a zip archive, as described in 
:ref:`concepts-packaging-dags`.
+
+Each :doc:`language SDK <../authoring-and-scheduling/language-sdks/index>` 
with a configured coordinator
+also registers an importer for its bundle format.
+
+How Airflow uses importers
+--------------------------
+
+An importer handles a Dag definition: a unit of Dag source, such as a Python 
file or a member of a zip
+archive.
+
+* The Dag processor asks each importer to list the Dag definitions it can 
handle in a bundle, and queues the
+  files that hold them. A file holding several definitions, such as a zip 
archive, is parsed as one.
+* A parsing process imports each definition with the importer that listed it. 
The resulting Dags go through
+  the same validation and :doc:`cluster policies <cluster-policies>` as Dags 
defined in Python, and the
+  importer's errors are shown as import errors.
+* The source code the importer returns for a definition is shown in the 
**Code** tab of the UI.
+* A worker uses the same importer to load the Dag before it runs a task.
+
+Language SDK importers work differently: the Dag processor runs the SDK 
runtime to parse their files instead of
+calling the importer, and a worker runs their tasks only on a queue routed to 
a coordinator. See
+:doc:`../authoring-and-scheduling/language-sdks/index`.
+
+A Dag definition must be a file in the bundle, or nested in one, as a zip 
member is. The Dag processor
+ignores any other definition with a warning.
+
+Configuring importers
+---------------------
+
+Register importers in :ref:`config:dag_processor__dag_importer_configs`. Each 
entry supplies the
+``classpath`` of the importer and, optionally, the ``extensions`` it handles 
and the ``kwargs`` to create it
+with:
+
+.. code-block:: ini
+
+    [dag_processor]
+    dag_importer_configs = [
+        {
+          "classpath": "my_company.importers.YamlDagImporter",
+          "extensions": [".yaml", ".yml"]
+        }
+      ]
+
+Without ``extensions``, the importer handles the extensions its class 
declares. An entry with only a
+classpath can be given as a string, for example 
``["my_company.importers.YamlDagImporter"]``.
+
+To register an importer for one bundle only, add an ``importers`` key, in the 
same format, to that bundle's
+entry in :ref:`config:dag_processor__dag_bundle_config_list`:
+
+.. code-block:: ini
+
+    [dag_processor]
+    dag_bundle_config_list = [
+        {
+          "name": "pipelines",
+          "classpath": "airflow.dag_processing.bundles.local.LocalDagBundle",
+          "kwargs": {"path": "/opt/airflow/pipelines"},
+          "importers": ["my_company.importers.YamlDagImporter"]
+        }
+      ]
+
+Each extension is handled by one importer. Importers are registered in this 
order, and a later importer
+takes over an extension from an earlier one, with a warning in the logs:
+
+#. the built-in importers;
+#. the importers of the language SDK coordinators;
+#. ``dag_importer_configs``;
+#. the bundle's ``importers``.
+
+This lets you replace a built-in importer, for example to handle ``.py`` files 
differently. However, replacing the
+``.py`` importer does not change how ``.py`` members of zip archives are 
imported: ``ZipImporter`` imports them
+with its own importers, which its ``internal_importers`` kwarg sets.
+
+.. important::
+
+    Workers import Dags with the same importers as the Dag processor. Install 
the package that provides your
+    importer, and set the same importer configuration, on the Dag processor, 
on the workers, and anywhere
+    else Dags are parsed, such as where you run ``airflow dags`` commands.
+
+Writing a custom importer
+-------------------------
+
+An importer subclasses :class:`~airflow.sdk.importers.AbstractDagImporter`, 
typed with the class of Dag
+definition it handles, and implements:
+
+``can_handle(definition)``
+    Whether the importer can import a definition, a path, or a file name.
+``list_dag_definitions(bundle, *, safe_mode)``
+    Yield the Dag definitions in the bundle that the importer handles.
+``import_definition(definition, bundle)``
+    Import the Dags of one definition and return them in a 
:class:`~airflow.sdk.importers.DagImportResult`.
+``get_source_code(definition, dag_id=None)``
+    Return the source of a definition and its language, as a 
:class:`~airflow.sdk.importers.DagSourceCode`.
+    ``dag_id`` names the Dag whose source is wanted when a definition holds 
several; an importer that cannot
+    tell them apart ignores it.
+
+For file-based formats, 
:func:`~airflow.sdk.importers.find_file_dag_definitions` walks the bundle the 
same
+way the built-in importers do: it applies :ref:`.airflowignore 
<concepts:airflowignore>` and yields a
+:class:`~airflow.sdk.importers.FilesystemDagDefinition` for each file with one 
of the given extensions.
+
+Keep the following in mind when you write an importer:
+
+* Report a definition that fails to import as a 
:class:`~airflow.sdk.importers.DagImportError` in the
+  result instead of raising, so it is shown as an import error. In 
``list_dag_definitions``, yield a
+  ``DagImportError`` for a file that cannot be read, so the rest of the bundle 
is still listed.
+* Rely only on ``bundle.name`` and ``bundle.path``. Only the Dag processor's 
bundle listing passes the
+  configured bundle; parsing processes, workers and the CLI pass a 
``LocalDagBundle`` built from its name and
+  path. When they list definitions, ``bundle.path`` is the single file being 
parsed;
+  ``find_file_dag_definitions`` handles both a directory and a file.
+* ``list_dag_definitions`` runs in the Dag processor's main loop, with no 
timeout, each time a bundle is
+  refreshed, so keep it cheap: a slow listing delays the parsing of every 
bundle. Only the import of a
+  definition, in a parsing process, is bounded by 
:ref:`config:dag_processor__dag_file_processor_timeout`.
+  ``dagbag_import_timeout`` applies to the built-in Python importer and to 
language SDK parsing, not to
+  custom importers.
+* Declare ``supported_extensions`` as an attribute: Airflow replaces it with 
the ``extensions`` of the
+  importer's configuration entry.
+* ``safe_mode`` is the value of :ref:`config:core__dag_discovery_safe_mode`, 
except on a worker, which always
+  passes ``False``. To skip files that clearly hold no Dag before a parsing 
process is started for them,
+  override 
:meth:`~airflow.sdk.importers.AbstractDagImporter.might_contain_dag` and call 
it in
+  ``list_dag_definitions``.
+* Airflow creates one importer instance per bundle and reuses it for every 
definition. The Dag processor
+  creates it before it forks the parsing processes, so keep ``__init__`` cheap 
and do not open connections or
+  start threads there.
+
+For a definition nested in a file, such as a member of an archive, subclass
+:class:`~airflow.sdk.importers.FileDagDefinition`. See the :doc:`Task SDK API 
reference <task-sdk:api>` for
+the full interface.
diff --git 
a/airflow-core/docs/administration-and-deployment/dagfile-processing.rst 
b/airflow-core/docs/administration-and-deployment/dagfile-processing.rst
index 5fead75ef95..f27289f00a5 100644
--- a/airflow-core/docs/administration-and-deployment/dagfile-processing.rst
+++ b/airflow-core/docs/administration-and-deployment/dagfile-processing.rst
@@ -19,7 +19,8 @@
 Dag File Processing
 -------------------
 
-Dag File Processing refers to the process of reading the python files that 
define your Dags and storing them such that the scheduler can schedule them.
+Dag File Processing refers to the process of reading the files that define 
your Dags and storing them such that the scheduler can schedule them.
+Airflow reads these files through :doc:`Dag importers <dag-importers>`; the 
built-in ones read Python files and zip archives.
 
 There are two primary components involved in Dag file processing.  The 
``DagFileProcessorManager`` is a process executing an infinite loop that 
determines which files need
 to be processed, and the ``DagFileProcessorProcess`` is a separate process 
that is started to convert an individual file into one or more Dag objects.
@@ -30,7 +31,7 @@ The ``DagFileProcessorManager`` coordinates this work but 
never runs user code i
 
 ``DagFileProcessorManager`` has the following steps:
 
-1. Check for new files:  If the elapsed time since the Dag was last refreshed 
is > :ref:`config:dag_processor__refresh_interval` then update the file paths 
list
+1. Check for new files:  If the elapsed time since the Dag was last refreshed 
is > :ref:`config:dag_processor__refresh_interval` then update the file paths 
list with the files holding the Dag definitions that the bundle's importers list
 2. Exclude recently processed files:  Exclude files that have been processed 
more recently than 
:ref:`min_file_process_interval<config:dag_processor__min_file_process_interval>`
 and have not been modified
 3. Queue file paths: Add files discovered to the file path queue
 4. Process files:  Start a new ``DagFileProcessorProcess`` for each file, up 
to a maximum of :ref:`config:dag_processor__parsing_processes`
@@ -40,9 +41,8 @@ The ``DagFileProcessorManager`` coordinates this work but 
never runs user code i
 ``DagFileProcessorProcess`` has the following steps:
 
 1. Process file: The entire process must complete within 
:ref:`dag_file_processor_timeout<config:dag_processor__dag_file_processor_timeout>`
-2. The Dag files are loaded as Python module: Must complete within 
:ref:`dagbag_import_timeout<config:core__dagbag_import_timeout>`
-3. Process modules:  Find Dag objects within Python module
-4. Return DagBag:  Provide the ``DagFileProcessorManager`` a list of the 
discovered Dag objects
+2. Import Dag definitions: Each Dag definition in the file is imported by the 
importer that listed it. The Python importer loads the file as a Python module, 
which must complete within 
:ref:`dagbag_import_timeout<config:core__dagbag_import_timeout>`, and finds the 
Dag objects within it
+3. Return DagBag:  Provide the ``DagFileProcessorManager`` a list of the 
discovered Dag objects
 
 
 Fine-tuning your Dag processor performance
diff --git a/airflow-core/docs/administration-and-deployment/index.rst 
b/airflow-core/docs/administration-and-deployment/index.rst
index 2d0273f863f..019e2b0b751 100644
--- a/airflow-core/docs/administration-and-deployment/index.rst
+++ b/airflow-core/docs/administration-and-deployment/index.rst
@@ -29,6 +29,7 @@ This section contains information about deploying Dags into 
production and the a
     lineage
     listeners
     dag-bundles
+    dag-importers
     dag-serialization
     modules_management
     scheduler
diff --git a/airflow-core/docs/core-concepts/dags.rst 
b/airflow-core/docs/core-concepts/dags.rst
index 21804eb26bc..724619aae9f 100644
--- a/airflow-core/docs/core-concepts/dags.rst
+++ b/airflow-core/docs/core-concepts/dags.rst
@@ -147,7 +147,7 @@ Chain can also do *pairwise* dependencies for lists the 
same size (this is diffe
 Loading Dags
 ------------
 
-Airflow loads Dags from Python source files in Dag bundles. It will take each 
file, execute it, and then load any Dag objects from that file.
+Airflow loads Dags from Python source files in Dag bundles. It will take each 
file, execute it, and then load any Dag objects from that file. Other formats 
can be loaded with a :doc:`Dag importer 
</administration-and-deployment/dag-importers>`.
 
 This means you can define multiple Dags per Python file, or even spread one 
very complex Dag across multiple Python files using imports.
 
@@ -707,6 +707,8 @@ This is especially useful if your tasks are built 
dynamically from configuration
             EmptyOperator(task_id="extract_orders")
 
 
+.. _concepts-packaging-dags:
+
 Packaging Dags
 --------------
 
diff --git a/airflow-core/docs/public-airflow-interface.rst 
b/airflow-core/docs/public-airflow-interface.rst
index b2180aeca87..ab608501e86 100644
--- a/airflow-core/docs/public-airflow-interface.rst
+++ b/airflow-core/docs/public-airflow-interface.rst
@@ -429,6 +429,22 @@ You can read more about executors and how to write your 
own in :doc:`core-concep
   of people did, but there were some hard-coded behaviours that preferred 
in-built
   executors, and custom executors could not provide full functionality that 
built-in executors had.
 
+Dag Importers
+-------------
+
+|experimental|
+
+Dag importers turn the contents of a Dag bundle into Dags, which lets Airflow 
load Dags from formats other
+than Python files. All Dag importers derive from 
:class:`~airflow.sdk.importers.AbstractDagImporter`.
+
+The importer interface, as listed in the :doc:`Task SDK API reference 
<task-sdk:api>`, is public, but it may
+still change in a minor release while it is being stabilized.
+
+You can read more about Dag importers and how to write your own in
+:doc:`administration-and-deployment/dag-importers`.
+
+.. versionadded:: 3.4.0
+
 Secrets Backends
 ----------------
 
diff --git a/task-sdk/docs/api.rst b/task-sdk/docs/api.rst
index 4f83dc1d838..78809e802ef 100644
--- a/task-sdk/docs/api.rst
+++ b/task-sdk/docs/api.rst
@@ -310,6 +310,41 @@ See 
:doc:`apache-airflow:authoring-and-scheduling/language-sdks/index` for a con
 
 .. autoapiclass:: airflow.sdk.coordinators.node.NodeCoordinator
 
+Dag Importers
+-------------
+
+|experimental|
+
+Dag importers turn the contents of a Dag bundle into Dags. This interface may 
still change in a minor
+release while it is being stabilized. See 
:doc:`apache-airflow:administration-and-deployment/dag-importers` for how
+importers are configured and how to write one.
+
+.. autoapiclass:: airflow.sdk.importers.AbstractDagImporter
+
+.. autoapiclass:: airflow.sdk.importers.DagDefinition
+
+.. autoapiclass:: airflow.sdk.importers.FileDagDefinition
+
+.. autoapiclass:: airflow.sdk.importers.FilesystemDagDefinition
+
+.. autoapiclass:: airflow.sdk.importers.DagImportResult
+
+.. autoapiclass:: airflow.sdk.importers.DagImportError
+
+.. autoapiclass:: airflow.sdk.importers.DagImportWarning
+
+.. autoapiclass:: airflow.sdk.importers.DagSourceCode
+
+.. autoapifunction:: airflow.sdk.importers.find_file_dag_definitions
+
+.. autoapifunction:: airflow.sdk.importers.get_file_suffix
+
+.. rubric:: Built-in importers
+
+.. autoapiclass:: airflow.sdk.importers.PythonDagImporter
+
+.. autoapiclass:: airflow.sdk.importers.ZipImporter
+
 Execution Time Components
 -------------------------
 .. rubric:: Context

Reply via email to