From bd1191b4c6c905b1dec1a97fdcc3bd8bbb354b76 Mon Sep 17 00:00:00 2001 From: LIU ZHE YOU Date: Thu, 1 Oct 2026 07:02:25 +0000 Subject: [PATCH 1/2] List only .zip archives as packaged Dags A JAR is a zip archive, so every task-handler JAR and dependency JAR in a Dag bundle was listed as a Dag file. Each one got a parse child every min_file_process_interval that found no importer for its suffix, and each bundle refresh opened it and checked every member. Dag discovery now lists only what an importer can import. --- airflow-core/docs/core-concepts/dags.rst | 2 ++ airflow-core/src/airflow/utils/file.py | 4 +++- airflow-core/tests/unit/utils/test_file.py | 12 ++++++++++++ 3 files changed, 17 insertions(+), 1 deletion(-) diff --git a/airflow-core/docs/core-concepts/dags.rst b/airflow-core/docs/core-concepts/dags.rst index 18acf9bbdec3c..3521f52fd1921 100644 --- a/airflow-core/docs/core-concepts/dags.rst +++ b/airflow-core/docs/core-concepts/dags.rst @@ -719,6 +719,8 @@ You can either do this all inside of the Dag bundle, with a standard filesystem package1/__init__.py package1/functions.py +The file name must end in ``.zip``. Other zip archives in the Dag bundle, such as JAR files, are not parsed as Dags. + Note that packaged Dags come with some caveats: * They cannot be used if you have pickling enabled for serialization diff --git a/airflow-core/src/airflow/utils/file.py b/airflow-core/src/airflow/utils/file.py index ff7d7db16623b..12fc294397480 100644 --- a/airflow-core/src/airflow/utils/file.py +++ b/airflow-core/src/airflow/utils/file.py @@ -110,7 +110,9 @@ def find_dag_file_paths(directory: str | os.PathLike[str], safe_mode: bool) -> l for file_path in find_path_from_directory(directory, ".airflowignore", ignore_file_syntax): path = Path(file_path) try: - if path.is_file() and (path.suffix == ".py" or zipfile.is_zipfile(path)): + if path.is_file() and ( + path.suffix == ".py" or (path.suffix.lower() == ".zip" and zipfile.is_zipfile(path)) + ): if might_contain_dag(file_path, safe_mode, conf=conf): file_paths.append(file_path) except Exception: diff --git a/airflow-core/tests/unit/utils/test_file.py b/airflow-core/tests/unit/utils/test_file.py index 115027771284e..a5062d40765c7 100644 --- a/airflow-core/tests/unit/utils/test_file.py +++ b/airflow-core/tests/unit/utils/test_file.py @@ -213,6 +213,18 @@ def test_list_py_file_paths(self, test_zip_path): f"Detected files mismatched expected files:\ndetected_files: {pformat(detected_files)}\nexpected_files: {pformat(expected_files)}" ) + @pytest.mark.parametrize("archive_name", ["task_handler.jar", "no_suffix"]) + def test_list_py_file_paths_skips_zip_archive_without_zip_suffix(self, tmp_path, archive_name): + dag_source = "from airflow.sdk import DAG\n" + (tmp_path / "dag.py").write_text(dag_source) + for name in ("lower.zip", "upper.ZIP", archive_name): + with zipfile.ZipFile(tmp_path / name, "w") as zf: + zf.writestr("dag.py", dag_source) + + assert set(list_py_file_paths(tmp_path)) == { + str(tmp_path / name) for name in ("dag.py", "lower.zip", "upper.ZIP") + } + @pytest.mark.parametrize( ("edge_filename", "expected_modification"), From ec470f5b47b412faca53d982e37469fa2948815f Mon Sep 17 00:00:00 2001 From: LIU ZHE YOU Date: Fri, 2 Oct 2026 17:01:47 +0000 Subject: [PATCH 2/2] Add a significant newsfragment for listing only .zip packaged Dags A packaged Dag stored without the .zip suffix is no longer discovered, so call it out for users upgrading. --- airflow-core/newsfragments/74033.significant.rst | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 airflow-core/newsfragments/74033.significant.rst diff --git a/airflow-core/newsfragments/74033.significant.rst b/airflow-core/newsfragments/74033.significant.rst new file mode 100644 index 0000000000000..d02a2c021c974 --- /dev/null +++ b/airflow-core/newsfragments/74033.significant.rst @@ -0,0 +1,3 @@ +Only ``.zip`` archives are discovered as packaged Dags + +Dag discovery now lists a zip archive as a packaged Dag only when its file name ends in ``.zip``. Before, every zip archive in a Dag bundle was parsed whatever its name, including JAR files and archives with no suffix. A packaged Dag stored under another name is no longer found: rename it to end in ``.zip``. ``.py`` files are listed as before.