This is an automated email from the ASF dual-hosted git repository.
henry3260 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 24d8c66ee11 Prevent Dag CLI subcommands from being silently dropped
(#72340)
24d8c66ee11 is described below
commit 24d8c66ee11e3003801b51511c4308e97a944a97
Author: Y-C <[email protected]>
AuthorDate: Tue Sep 1 15:17:24 2026 +0800
Prevent Dag CLI subcommands from being silently dropped (#72340)
---
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 21b2099c3c7..813bf0704fc 100644
--- a/airflow-core/src/airflow/cli/cli_config.py
+++ b/airflow-core/src/airflow/cli/cli_config.py
@@ -2348,24 +2348,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