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

henry3260 pushed a commit to branch v3-3-test
in repository https://gitbox.apache.org/repos/asf/airflow.git


The following commit(s) were added to refs/heads/v3-3-test by this push:
     new 21f33aa9260 [v3-3-test] Prevent Dag CLI subcommands from being 
silently dropped (#72340) (#72365)
21f33aa9260 is described below

commit 21f33aa9260524a848ab5fa4ebff975d5352ddfb
Author: github-actions[bot] 
<41898282+github-actions[bot]@users.noreply.github.com>
AuthorDate: Tue Sep 1 16:22:07 2026 +0800

    [v3-3-test] Prevent Dag CLI subcommands from being silently dropped 
(#72340) (#72365)
    
    (cherry picked from commit 24d8c66ee11e3003801b51511c4308e97a944a97)
    
    Co-authored-by: Y-C <[email protected]>
---
 airflow-core/src/airflow/cli/cli_config.py     | 14 +++++++-------
 airflow-core/tests/unit/cli/test_cli_parser.py | 20 ++++++++++++++++++++
 2 files changed, 27 insertions(+), 7 deletions(-)

diff --git a/airflow-core/src/airflow/cli/cli_config.py 
b/airflow-core/src/airflow/cli/cli_config.py
index a4c0bc02a1b..c8a5e224e90 100644
--- a/airflow-core/src/airflow/cli/cli_config.py
+++ b/airflow-core/src/airflow/cli/cli_config.py
@@ -2340,24 +2340,24 @@ core_commands: list[CLICommand] = [
 
 def _remove_dag_id_opt(command: ActionCommand):
     cmd = command._asdict()
-    cmd["args"] = (arg for arg in command.args if arg is not ARG_DAG_ID)
+    cmd["args"] = tuple(arg for arg in command.args if arg is not ARG_DAG_ID)
     return ActionCommand(**cmd)
 
 
+# Subcommands ``DAG.cli()`` exposes, via ``get_parser(dag_parser=True)``.
+DAG_CLI_DAGS_SUBCOMMANDS = ("list-runs", "pause", "unpause", "test")
+DAG_CLI_TASKS_SUBCOMMANDS = ("list", "test")
+
 dag_cli_commands: list[CLICommand] = [
     GroupCommand(
         name="dags",
         help="Manage DAGs",
-        subcommands=[
-            _remove_dag_id_opt(sp)
-            for sp in DAGS_COMMANDS
-            if sp.name in ["backfill", "list-runs", "pause", "unpause", "test"]
-        ],
+        subcommands=[_remove_dag_id_opt(sp) for sp in DAGS_COMMANDS if sp.name 
in DAG_CLI_DAGS_SUBCOMMANDS],
     ),
     GroupCommand(
         name="tasks",
         help="Manage tasks",
-        subcommands=[_remove_dag_id_opt(sp) for sp in TASKS_COMMANDS if 
sp.name in ["list", "test", "run"]],
+        subcommands=[_remove_dag_id_opt(sp) for sp in TASKS_COMMANDS if 
sp.name in DAG_CLI_TASKS_SUBCOMMANDS],
     ),
 ]
 DAG_CLI_DICT: dict[str, CLICommand] = {sp.name: sp for sp in dag_cli_commands}
diff --git a/airflow-core/tests/unit/cli/test_cli_parser.py 
b/airflow-core/tests/unit/cli/test_cli_parser.py
index acba2fa5d2e..2a0680f08ef 100644
--- a/airflow-core/tests/unit/cli/test_cli_parser.py
+++ b/airflow-core/tests/unit/cli/test_cli_parser.py
@@ -496,6 +496,26 @@ class TestCli:
             with pytest.raises(SystemExit):
                 parser.parse_args([*cmd_args, "--help"])
 
+    @pytest.mark.parametrize(
+        ("selected_names", "source_commands"),
+        [
+            pytest.param(cli_config.DAG_CLI_DAGS_SUBCOMMANDS, 
cli_config.DAGS_COMMANDS, id="dags"),
+            pytest.param(cli_config.DAG_CLI_TASKS_SUBCOMMANDS, 
cli_config.TASKS_COMMANDS, id="tasks"),
+        ],
+    )
+    def test_dag_cli_subcommands_all_exist(self, selected_names, 
source_commands):
+        """A name that no longer exists is silently dropped, so guard against 
stale entries."""
+        assert set(selected_names) <= {command.name for command in 
source_commands}
+
+    def test_dag_cli_parser_keeps_args_when_rebuilt(self):
+        """``_remove_dag_id_opt`` must not hand argparse a one-shot 
generator."""
+        cli_parser.get_parser.cache_clear()
+        first = 
vars(cli_parser.get_parser(dag_parser=True).parse_args(["dags", "pause"]))
+        cli_parser.get_parser.cache_clear()
+        second = 
vars(cli_parser.get_parser(dag_parser=True).parse_args(["dags", "pause"]))
+        assert "treat_dag_id_as_regex" in first
+        assert first.keys() == second.keys()
+
     def test_positive_int(self):
         assert cli_config.positive_int(allow_zero=True)("1") == 1
         assert cli_config.positive_int(allow_zero=True)("0") == 0

Reply via email to