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

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


The following commit(s) were added to refs/heads/v3-1-test by this push:
     new 2ffe48384d5 [v3-1-test] Fix config list output for multi-line values 
(#58115) (#58378)
2ffe48384d5 is described below

commit 2ffe48384d521caf1a9fba58aaf213e5f952fc64
Author: Jarek Potiuk <[email protected]>
AuthorDate: Sun Nov 16 23:43:01 2025 +0100

    [v3-1-test] Fix config list output for multi-line values (#58115) (#58378)
    
    Closes: #57355
    
    When a user runs `airflow config list --include-descriptions 
--include-examples`,
    multi-line values (like `dag_bundle_config_list`) would cause a
    `configparser.ParsingError` due to improper indentation.
    
    This fix pretty-prints the JSON value using `json.dumps(indent=4)`
    and then adds an additional four-space indent to each new line.
    This ensures the INI parser treats the entire block as a
    single, valid multi-line value.
    (cherry picked from commit d009b6467544e657245f14543dcc6dd11046fcb1)
    
    Co-authored-by: Aaron Chen <[email protected]>
---
 airflow-core/src/airflow/configuration.py          |  8 ++++
 airflow-core/tests/unit/core/test_configuration.py | 46 ++++++++++++++++++++++
 2 files changed, 54 insertions(+)

diff --git a/airflow-core/src/airflow/configuration.py 
b/airflow-core/src/airflow/configuration.py
index 1bfbbcf62b4..361b5765fd4 100644
--- a/airflow-core/src/airflow/configuration.py
+++ b/airflow-core/src/airflow/configuration.py
@@ -589,6 +589,14 @@ class AirflowConfigParser(ConfigParser):
                 value = "\n# ".join(value_lines)
                 file.write(f"# {option} = {value}\n")
             else:
+                if "\n" in value:
+                    try:
+                        value = json.dumps(json.loads(value), indent=4)
+                        value = value.replace(
+                            "\n", "\n    "
+                        )  # indent multi-line JSON to satisfy configparser 
format
+                    except JSONDecodeError:
+                        pass
                 file.write(f"{option} = {value}\n")
         if needs_separation:
             file.write("\n")
diff --git a/airflow-core/tests/unit/core/test_configuration.py 
b/airflow-core/tests/unit/core/test_configuration.py
index 0735245f509..bcce279d086 100644
--- a/airflow-core/tests/unit/core/test_configuration.py
+++ b/airflow-core/tests/unit/core/test_configuration.py
@@ -1028,6 +1028,52 @@ key7 =
             for key, value in expected_backend_kwargs.items():
                 assert getattr(secrets_backend, key) == value
 
+    def test_write_pretty_prints_multiline_json(self):
+        """
+        Tests that the `write` method correctly pretty-prints
+        a config value that is a valid multi-line JSON string.
+        """
+        json_string = '[\n{\n"name": "dags-folder",\n"classpath": 
"test.class"\n}\n]'
+
+        test_conf = AirflowConfigParser()
+        test_conf.add_section("test_json")
+        test_conf.set("test_json", "my_json_config", json_string)
+
+        with StringIO() as string_file:
+            test_conf.write(string_file, include_descriptions=False, 
include_env_vars=False)
+            content = string_file.getvalue()
+
+        expected_formatted_string = (
+            "my_json_config = [\n"
+            "        {\n"
+            '            "name": "dags-folder",\n'
+            '            "classpath": "test.class"\n'
+            "        }\n"
+            "    ]\n"
+        )
+
+        assert expected_formatted_string in content
+        assert json_string not in content
+
+    def test_write_handles_multiline_non_json_string(self):
+        """
+        Tests that `write` does not crash when encountering a multi-line string
+        that is NOT valid JSON.
+        """
+        multiline_string = "This is the first line.\nThis is the second line."
+
+        test_conf = AirflowConfigParser()
+        test_conf.add_section("test_multiline")
+        test_conf.set("test_multiline", "my_string_config", multiline_string)
+
+        with StringIO() as string_file:
+            test_conf.write(string_file, include_descriptions=False, 
include_env_vars=False)
+            content = string_file.getvalue()
+
+        expected_raw_output = "my_string_config = This is the first 
line.\nThis is the second line.\n"
+
+        assert expected_raw_output in content
+
 
 @mock.patch.dict(
     "os.environ",

Reply via email to