Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions airflow-core/src/airflow/configuration.py
Original file line number Diff line number Diff line change
Expand Up @@ -606,6 +606,14 @@ def _write_value(
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")
Expand Down
46 changes: 46 additions & 0 deletions airflow-core/tests/unit/core/test_configuration.py
Original file line number Diff line number Diff line change
Expand Up @@ -1189,6 +1189,52 @@ def test_validate_is_idempotent(self):

assert test_conf.get("core", "executor") == "LocalExecutor"

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",
Expand Down