Skip to content

Commit 5fb8fc8

Browse files
authored
Merge pull request #419 from twisted/writer
Refactor _writer & bring to 100% coverage
2 parents 6045131 + e8acd1b commit 5fb8fc8

File tree

5 files changed

+61
-28
lines changed

5 files changed

+61
-28
lines changed

README.rst

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -158,7 +158,7 @@ as the option 'title_format', already exists in newsfile, ``ValueError`` will be
158158
you "already produced newsfiles for this version".
159159

160160
If ``single_file`` is set to ``false`` instead, each versioned ``towncrier build`` will generate a
161-
separate newsfile, whose name is formatted as the patten given by option ``filename``.
161+
separate newsfile, whose name is formatted as the pattern given by option ``filename``.
162162
For example, if ``filename="{version}-notes.rst"``, then the release note with version "7.8.9" will
163163
be written to the file "7.8.9-notes.rst". If the newsfile already exists, its content
164164
will be overwriten with new release note, without throwing a ``ValueError`` warning.

src/towncrier/_writer.py

Lines changed: 50 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -6,38 +6,64 @@
66
affecting existing content.
77
"""
88

9-
10-
import os
9+
from pathlib import Path
1110

1211

1312
def append_to_newsfile(
14-
directory, filename, start_string, top_line, content, single_file=True
13+
directory, filename, start_string, top_line, content, single_file
1514
):
15+
"""
16+
Write *content* to *directory*/*filename* behind *start_string*.
17+
18+
Double-check *top_line* (i.e. the release header) is not already in the
19+
file.
1620
17-
news_file = os.path.join(directory, filename)
21+
if *single_file* is True, add it to an existing file, otherwise create a
22+
fresh one.
23+
"""
24+
news_file = Path(directory) / filename
1825

19-
if single_file:
20-
if not os.path.exists(news_file):
21-
existing_content = ""
22-
else:
23-
with open(news_file, encoding="utf8") as f:
24-
existing_content = f.read()
25-
existing_content = existing_content.split(start_string, 1)
26-
else:
27-
existing_content = [""]
26+
header, prev_body = _figure_out_existing_content(
27+
news_file, start_string, single_file
28+
)
2829

29-
if top_line and top_line in existing_content[-1]:
30+
if top_line and top_line in prev_body:
3031
raise ValueError("It seems you've already produced newsfiles for this version?")
3132

32-
with open(os.path.join(directory, filename), "wb") as f:
33+
# Leave newlines alone. This probably leads to inconsistent newlines,
34+
# because we've loaded existing content with universal newlines, but that's
35+
# the original behavior.
36+
with news_file.open("w", encoding="utf8", newline="") as f:
37+
if header:
38+
f.write(header)
39+
40+
f.write(content)
41+
42+
if prev_body:
43+
f.write(f"\n\n{prev_body}")
44+
45+
46+
def _figure_out_existing_content(news_file, start_string, single_file):
47+
"""
48+
Try to read *news_file* and split it into header (everything before
49+
*start_string*) and the old body (everything after *start_string*).
50+
51+
If there's no *start_string*, return empty header.
52+
53+
Empty file and per-release files have neither.
54+
"""
55+
if not single_file or not news_file.exists():
56+
# Per-release news files always start empty.
57+
# Non-existent files have no existing content.
58+
return "", ""
59+
60+
# If we didn't use universal newlines here, we wouldn't find *start_string*
61+
# which usually contains a `\n`.
62+
with news_file.open(encoding="utf8") as f:
63+
content = f.read()
3364

34-
if len(existing_content) > 1:
35-
f.write(existing_content.pop(0).rstrip().encode("utf8"))
36-
if start_string:
37-
f.write(("\n\n" + start_string + "\n").encode("utf8"))
65+
t = content.split(start_string, 1)
66+
if len(t) == 2:
67+
return f"{t[0].rstrip()}\n\n{start_string}\n", t[1].lstrip()
3868

39-
f.write(content.encode("utf8"))
40-
if existing_content:
41-
if existing_content[0]:
42-
f.write(b"\n\n")
43-
f.write(existing_content[0].lstrip().encode("utf8"))
69+
return "", content.lstrip()

src/towncrier/newsfragments/419.misc

Whitespace-only changes.

src/towncrier/test/test_build.py

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -717,6 +717,7 @@ def do_build_once_with(version, fragment_file, fragment):
717717
"01-01-2001",
718718
"--yes",
719719
],
720+
catch_exceptions=False,
720721
)
721722
# not git repository, manually remove fragment file
722723
Path(f"newsfragments/{fragment_file}").unlink()
@@ -780,7 +781,7 @@ def test_bullet_points_false(self):
780781
"""
781782
When all_bullets is false, subsequent lines are not indented.
782783
783-
The automatic ticket number inserted by towcier will allign with the
784+
The automatic ticket number inserted by towncrier will align with the
784785
manual bullet.
785786
"""
786787
runner = CliRunner()
@@ -794,7 +795,7 @@ def test_bullet_points_false(self):
794795
)
795796
os.mkdir("newsfragments")
796797
with open("newsfragments/123.feature", "w") as f:
797-
f.write("wow!\n" "~~~~\n" "\n" "No indentation at all.")
798+
f.write("wow!\n~~~~\n\nNo indentation at all.")
798799
with open("newsfragments/124.bugfix", "w") as f:
799800
f.write("#. Numbered bullet list.")
800801
with open("newsfragments/125.removal", "w") as f:
@@ -1010,7 +1011,7 @@ def test_start_string(self):
10101011
with open("newsfragments/123.feature", "w") as f:
10111012
f.write("Adds levitation")
10121013
with open("NEWS.rst", "w") as f:
1013-
f.write("a line\n\nanother\n\nRelease notes start marker\n")
1014+
f.write("a line\n\nanother\n\nRelease notes start marker\na footer!\n")
10141015

10151016
result = runner.invoke(
10161017
_main,
@@ -1045,6 +1046,7 @@ def test_start_string(self):
10451046
- Adds levitation (#123)
10461047
10471048
1049+
a footer!
10481050
"""
10491051
)
10501052

src/towncrier/test/test_write.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@
1818

1919

2020
class WritingTests(TestCase):
21+
maxDiff = None
22+
2123
def test_append_at_top(self):
2224

2325
fragments = OrderedDict(
@@ -109,6 +111,7 @@ def test_append_at_top(self):
109111
wrap=True,
110112
versiondata={"name": "MyProject", "version": "1.0", "date": "never"},
111113
),
114+
single_file=True,
112115
)
113116

114117
with open(os.path.join(tempdir, "NEWS.rst")) as f:
@@ -221,6 +224,7 @@ def test_append_at_top_with_hint(self):
221224
wrap=True,
222225
versiondata={"name": "MyProject", "version": "1.0", "date": "never"},
223226
),
227+
single_file=True,
224228
)
225229

226230
with open(os.path.join(tempdir, "NEWS.rst")) as f:
@@ -259,6 +263,7 @@ def test_multiple_file_no_start_string(self):
259263
start_string=None,
260264
top_line="",
261265
content=content,
266+
single_file=True,
262267
)
263268

264269
with open(os.path.join(tempdir, "NEWS.rst")) as f:

0 commit comments

Comments
 (0)