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