From bcdd8ec770d8646a1934c1a6f72dc8ea4dc38edc Mon Sep 17 00:00:00 2001 From: Eason09053360 <185830721+Eason09053360@users.noreply.github.com> Date: Thu, 27 Aug 2026 18:44:45 +0800 Subject: [PATCH] Fix airflow config get-value silently succeeding on a missing option `airflow config get-value` swallowed the lookup failure and exited 0 with nothing on stdout, so a script capturing its output could not tell a missing option from an empty one. The parser's "not found" warning also landed on stdout, corrupting the one stream the command exists to produce. The exception was swallowed in the first place to avoid the double deprecation warning that a has_option() pre-check triggered (#40319). That constraint still holds, and so does the need to let a genuine lookup failure -- a failed *_cmd, an unreachable secrets backend -- reach the user with its own message rather than being reported as a missing option. --- .../airflow/cli/commands/config_command.py | 14 +++++++++----- .../unit/cli/commands/test_config_command.py | 19 ++++++++++++++----- 2 files changed, 23 insertions(+), 10 deletions(-) diff --git a/airflow-core/src/airflow/cli/commands/config_command.py b/airflow-core/src/airflow/cli/commands/config_command.py index 77b187b589991..a183297efad1b 100644 --- a/airflow-core/src/airflow/cli/commands/config_command.py +++ b/airflow-core/src/airflow/cli/commands/config_command.py @@ -74,11 +74,15 @@ def get_value(args): # providers are initialized. Theoretically Providers might add new sections and options # but also override defaults for existing options, so without loading all providers we # cannot be sure what is the final value of the option. - try: - value = conf.get(args.section, args.option) - print(value) - except AirflowConfigException: - pass + + # Neither `except AirflowConfigException` (also swallows a failed `*_cmd` or an unreachable + # secrets backend) nor a `has_option()` pre-check (re-emits a deprecated option's warning, + # https://github.com/apache/airflow/issues/40319) works here. `fallback=None` also returns + # before conf.get() logs "not found" to stdout, which get-value must keep clean. + value = conf.get(args.section, args.option, fallback=None) + if value is None: + raise SystemExit(f"The option [{args.section}/{args.option}] is not found in config.") + print(value) class ConfigParameter(NamedTuple): diff --git a/airflow-core/tests/unit/cli/commands/test_config_command.py b/airflow-core/tests/unit/cli/commands/test_config_command.py index 3e3913d694720..ee9ec846baf1c 100644 --- a/airflow-core/tests/unit/cli/commands/test_config_command.py +++ b/airflow-core/tests/unit/cli/commands/test_config_command.py @@ -27,6 +27,7 @@ from airflow.cli.commands import config_command from airflow.cli.commands.config_command import ConfigChange, ConfigParameter from airflow.configuration import conf +from airflow.exceptions import AirflowConfigException from tests_common.test_utils.config import conf_vars @@ -263,11 +264,19 @@ def test_should_not_raise_exception_when_section_for_config_with_value_defined_e config_command.get_value(self.parser.parse_args(["config", "get-value", "some_section", "value"])) - def test_should_raise_exception_when_option_is_missing(self, caplog): - config_command.get_value( - self.parser.parse_args(["config", "get-value", "missing-section", "dags_folder"]) - ) - assert "section/key [missing-section/dags_folder] not found in config" in caplog.text + def test_should_raise_exception_when_option_is_missing(self): + with pytest.raises(SystemExit) as ctx: + config_command.get_value( + self.parser.parse_args(["config", "get-value", "missing-section", "dags_folder"]) + ) + assert str(ctx.value) == "The option [missing-section/dags_folder] is not found in config." + + @conf_vars({("core", "asset_manager_kwargs_cmd"): "false"}) + def test_should_not_report_a_failed_lookup_as_a_missing_option(self): + with pytest.raises(AirflowConfigException, match="Cannot execute false"): + config_command.get_value( + self.parser.parse_args(["config", "get-value", "core", "asset_manager_kwargs"]) + ) class TestConfigLint: