Skip to content

Commit fbdd757

Browse files
525 adding to an ini file without a final newline breaks config (#526)
* Add failing test * Fix newline issue in INI integration * Adjust tests to reflect corrected behaviour
1 parent 868463e commit fbdd757

File tree

3 files changed

+39
-0
lines changed

3 files changed

+39
-0
lines changed

src/usethis/_integrations/file/ini/io_.py

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -305,6 +305,7 @@ def _validated_set(
305305
raise NotImplementedError(msg)
306306

307307
if section_key not in root:
308+
_ensure_newline(root)
308309
root.add_section(section_key)
309310

310311
root.set(section=section_key, option=option_key, value=value)
@@ -554,3 +555,13 @@ def _itermatches(values: Iterable[str], /, *, key: Key):
554555
yield value
555556
else:
556557
assert_never(key)
558+
559+
560+
def _ensure_newline(root: INIDocument) -> None:
561+
"""Add a newline to the INI file."""
562+
sections = list(root.iter_sections())
563+
if not sections:
564+
# No newline necessary
565+
return
566+
final_section = sections[-1]
567+
final_section.add_after.space(newlines=1)

tests/usethis/_core/test_core_tool.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -284,6 +284,7 @@ def test_coverage_rc_file(self, uv_init_dir: Path):
284284
[run]
285285
source =
286286
src
287+
287288
[report]
288289
exclude_also =
289290
if TYPE_CHECKING:
@@ -311,6 +312,7 @@ def test_tox_ini_file(self, uv_init_dir: Path):
311312
[coverage:run]
312313
source =
313314
src
315+
314316
[coverage:report]
315317
exclude_also =
316318
if TYPE_CHECKING:

tests/usethis/_integrations/file/ini/test_ini_io_.py

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1001,6 +1001,32 @@ def relative_path(self) -> Path:
10011001
key =
10021002
new_value
10031003
other
1004+
"""
1005+
)
1006+
1007+
def test_existing_file_no_newline(self, tmp_path: Path):
1008+
# Arrange
1009+
class MyINIFileManager(INIFileManager):
1010+
@property
1011+
def relative_path(self) -> Path:
1012+
return Path("valid.ini")
1013+
1014+
valid_file = tmp_path / "valid.ini"
1015+
valid_file.write_text("[section]\nkey = value")
1016+
1017+
# Act
1018+
with change_cwd(tmp_path), MyINIFileManager() as manager:
1019+
manager.set_value(
1020+
keys=["new_section", "new_key"], value="new_value", exists_ok=True
1021+
)
1022+
1023+
# Assert
1024+
assert (tmp_path / "valid.ini").read_text() == (
1025+
"""\
1026+
[section]
1027+
key = value
1028+
[new_section]
1029+
new_key = new_value
10041030
"""
10051031
)
10061032

0 commit comments

Comments
 (0)