Skip to content

Commit db10a4d

Browse files
jaracoadiroiban
andauthored
Prefer pathlib for read/write operations (#591)
* Rely on pathlib when reading and writing simple text. * Universal newlines is the default, so remove the comment. * Add news fragment. * Add compatibility shim for Python 3.9 and earlier. * Suppress coverage check for line 20 as it emits a spurious failure. * Only exclude branch coverage (regular coverage is fine) Co-authored-by: Adi Roiban <[email protected]> * Add comment explaining motivation for the compatibility shim. --------- Co-authored-by: Adi Roiban <[email protected]>
1 parent 7221d5d commit db10a4d

5 files changed

Lines changed: 36 additions & 20 deletions

File tree

src/towncrier/_builder.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
import textwrap
99

1010
from collections import defaultdict
11+
from pathlib import Path
1112
from typing import Any, DefaultDict, Iterable, Iterator, Mapping, Sequence
1213

1314
from jinja2 import Template
@@ -111,8 +112,7 @@ def find_fragments(
111112

112113
full_filename = os.path.join(section_dir, basename)
113114
fragment_filenames.append(full_filename)
114-
with open(full_filename, "rb") as f:
115-
data = f.read().decode("utf-8", "replace")
115+
data = Path(full_filename).read_text(encoding="utf-8", errors="replace")
116116

117117
if (ticket, category, counter) in file_content:
118118
raise ValueError(

src/towncrier/_writer.py

Lines changed: 25 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,22 @@
88

99
from __future__ import annotations
1010

11+
import sys
12+
1113
from pathlib import Path
14+
from typing import Any
15+
16+
17+
if sys.version_info < (3, 10):
18+
# Compatibility shim for newline parameter to write_text, added in 3.10
19+
def _newline_write_text(path: Path, content: str, **kwargs: Any) -> None:
20+
with path.open("w", **kwargs) as strm: # pragma: no branch
21+
strm.write(content)
22+
23+
else:
24+
25+
def _newline_write_text(path: Path, content: str, **kwargs: Any) -> None:
26+
path.write_text(content, **kwargs)
1227

1328

1429
def append_to_newsfile(
@@ -37,15 +52,17 @@ def append_to_newsfile(
3752
if top_line and top_line in prev_body:
3853
raise ValueError("It seems you've already produced newsfiles for this version?")
3954

40-
# Leave newlines alone. This probably leads to inconsistent newlines,
41-
# because we've loaded existing content with universal newlines, but that's
42-
# the original behavior.
43-
with news_file.open("w", encoding="utf-8", newline="") as f:
44-
if header:
45-
f.write(header)
55+
_newline_write_text(
56+
news_file,
4657
# If there is no previous body that means we're writing a brand new news file.
4758
# We don't want extra whitespace at the end of this new file.
48-
f.write(content + prev_body if prev_body else content.rstrip() + "\n")
59+
header + (content + prev_body if prev_body else content.rstrip() + "\n"),
60+
encoding="utf-8",
61+
# Leave newlines alone. This probably leads to inconsistent newlines,
62+
# because we've loaded existing content with universal newlines, but that's
63+
# the original behavior.
64+
newline="",
65+
)
4966

5067

5168
def _figure_out_existing_content(
@@ -64,10 +81,7 @@ def _figure_out_existing_content(
6481
# Non-existent files have no existing content.
6582
return "", ""
6683

67-
# If we didn't use universal newlines here, we wouldn't find *start_string*
68-
# which usually contains a `\n`.
69-
with news_file.open(encoding="utf-8") as f:
70-
content = f.read()
84+
content = Path(news_file).read_text(encoding="utf-8")
7185

7286
t = content.split(start_string, 1)
7387
if len(t) == 2:

src/towncrier/build.py

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,13 +5,13 @@
55
Build a combined news file from news fragments.
66
"""
77

8-
98
from __future__ import annotations
109

1110
import os
1211
import sys
1312

1413
from datetime import date
14+
from pathlib import Path
1515

1616
import click
1717

@@ -171,8 +171,7 @@ def __main(
171171
.read_text(encoding="utf-8")
172172
)
173173
else:
174-
with open(config.template, encoding="utf-8") as tmpl:
175-
template = tmpl.read()
174+
template = Path(config.template).read_text(encoding="utf-8")
176175

177176
click.echo("Finding news fragments...", err=to_err)
178177

src/towncrier/create.py

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@
99

1010
import os
1111

12+
from pathlib import Path
13+
1214
import click
1315

1416
from ._settings import config_option_help, load_config_from_options
@@ -170,10 +172,10 @@ def __main(
170172
click.echo("Aborted creating news fragment due to empty message.")
171173
ctx.exit(1)
172174

173-
with open(segment_file, "w", encoding="utf-8") as f:
174-
f.write(content)
175-
if config.create_eof_newline and content and not content.endswith("\n"):
176-
f.write("\n")
175+
add_newline = bool(
176+
config.create_eof_newline and content and not content.endswith("\n")
177+
)
178+
Path(segment_file).write_text(content + "\n" * add_newline, encoding="utf-8")
177179

178180
click.echo(f"Created news fragment at {segment_file}")
179181

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Leveraged pathlib in most file operations.

0 commit comments

Comments
 (0)