Skip to content

Commit 3f74d9e

Browse files
authored
#706 Refactor markdown template to make it easier to read (#709)
2 parents 33cf5bc + d5cf5c7 commit 3f74d9e

File tree

6 files changed

+139
-60
lines changed

6 files changed

+139
-60
lines changed

.gitattributes

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
# Hint Github.com about the language use in our template files.
2+
# The project templates indicate the target output type (.md/.rst) rather than the template type (jinja)
3+
# Reference: https://github.com/github-linguist/linguist/blob/main/docs/overrides.md#using-gitattributes
4+
src/towncrier/templates/* linguist-language=Jinja

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -160,7 +160,7 @@ source = ["src", ".nox/tests-*/**/site-packages"]
160160

161161
[tool.coverage.report]
162162
show_missing = true
163-
skip_covered = true
163+
skip_covered = false
164164
exclude_lines = [
165165
"pragma: no cover",
166166
"if TYPE_CHECKING:",
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Refactor the default markdown template to make it easier to understand, extend, and customize.

src/towncrier/templates/default.md

Lines changed: 109 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -1,58 +1,116 @@
1-
{% if render_title %}
2-
{% if versiondata.name %}
3-
{{header_prefix}} {{ versiondata.name }} {{ versiondata.version }} ({{ versiondata.date }})
4-
{% else %}
5-
{{header_prefix}} {{ versiondata.version }} ({{ versiondata.date }})
6-
{% endif %}
7-
{% endif %}
8-
{% for section, _ in sections.items() %}
9-
{% if section %}
10-
11-
{{header_prefix}}# {{section}}
12-
{% endif %}
13-
14-
{% if sections[section] %}
15-
{% for category, val in definitions.items() if category in sections[section] %}
16-
{{header_prefix}}#{% if section %}#{% endif %} {{ definitions[category]['name'] }}
17-
18-
{% for text, values in sections[section][category].items() %}
19-
- {{ text }}
20-
{%- if values %}
21-
{% if "\n - " in text or '\n * ' in text %}
22-
23-
24-
(
25-
{%- else %}
26-
{% if text %} ({% endif %}
27-
{%- endif -%}
28-
{%- for issue in values %}
29-
{{ issue.split(": ", 1)[0] }}{% if not loop.last %}, {% endif %}
30-
{%- endfor %}
31-
{% if text %}){% endif %}
1+
{#-
2+
══════════════════════════════════════════════════════════════════════════════
3+
TOWNCRIER MARKDOWN TEMPLATE
4+
══════════════════════════════════════════════════════════════════════════════
5+
6+
─── Macro: heading ─────────────────────────────────────────────────────────
7+
Purpose:
8+
Generates Markdown headings with the appropriate number of # characters.
9+
Based on header_prefix (default: "#") and the level argument.
10+
11+
Arguments:
12+
level The relative heading level (1=#, 2=##, 3=###, etc.)
13+
-#}
14+
{%- macro heading(level) -%}
15+
{{- "#" * ( header_prefix | length + level -1 ) }}
16+
{%- endmacro -%}
17+
18+
{#-
19+
─── Variable: newline ──────────────────────────────────────────────────────
20+
Purpose:
21+
Consistent newline handling. -#}
22+
{%- set newline = "\n" -%}
23+
24+
{#- ════════════════════════ TEMPLATE GENERATION ════════════════════════ -#}
25+
{#- ─── TITLE HEADING ─── #}
26+
{#- render_title is false when title_format is specified in the config #}
27+
{%- if render_title %}
28+
{%- if versiondata.name %}
29+
{{- heading(1) ~ " " ~ versiondata.name ~ " " ~ versiondata.version ~ " (" ~ versiondata.date ~ ")" ~ newline }}
30+
{%- else %}
31+
{{- heading(1) ~ " " ~ versiondata.version ~ " (" ~ versiondata.date ~ ")" ~ newline }}
32+
{%- endif %}
33+
{%- endif %}
34+
{#- If title_format is specified, we start with a new line #}
35+
{{- newline }}
36+
37+
{%- for section, _ in sections.items() %}
38+
{#- ─── SECTION HEADING ─── #}
39+
{%- if section %}
40+
{{- newline }}
41+
{{- heading(2) ~ " " ~ section ~ newline }}
42+
{{- newline }}
43+
{%- endif %}
3244

33-
{% else %}
45+
{%- if sections[section] %}
3446

35-
{% endif %}
36-
{% endfor %}
47+
{%- for category, val in definitions.items() if category in sections[section] %}
48+
{%- set issue_pks = [] %}
49+
{#- ─── CATEGORY HEADING ─── #}
50+
{#- Increase heading level if section is not present #}
51+
{{- heading(3 if section else 2) ~" " ~ definitions[category]['name'] ~ newline }}
52+
{{- newline }}
3753

38-
{% if issues_by_category[section][category] and "]: " in issues_by_category[section][category][0] %}
39-
{% for issue in issues_by_category[section][category] %}
40-
{{ issue }}
41-
{% endfor %}
54+
{#- ─── RENDER ENTRIES ─── #}
55+
{%- for text, values in sections[section][category].items() %}
56+
{#- Prepare the string of issue numbers (e.g., "#1, #9, #142") #}
57+
{%- set issue_pks = [] %}
58+
{%- for v_issue in values %}
59+
{%- set _= issue_pks.append(v_issue.split(": ", 1)[0]) %}
60+
{%- endfor %}
61+
{%- set issues_list = issue_pks | join(", ") %}
4262

43-
{% endif %}
44-
{% if sections[section][category]|length == 0 %}
45-
No significant changes.
63+
{#- Check if text contains a sublist #}
64+
{%- set text_has_sublist = (("\n - " in text) or ("\n * " in text)) %}
4665

47-
{% else %}
48-
{% endif %}
49-
{% endfor %}
50-
{% else %}
51-
No significant changes.
66+
{#- CASE 1: No text, only issues #}
67+
{#- Output: - #1, #9, #142 #}
68+
{%- if not text and issues_list %}
69+
{{- "- " ~ issues_list ~ newline }}
5270

53-
{% endif %}
54-
{% endfor +%}
55-
{#
56-
This comment adds one more newline at the end of the rendered newsfile content.
71+
{#- Cases where both text and issues exist #}
72+
{%- elif text and issues_list %}
73+
{%- if text_has_sublist %}
74+
{#- CASE 3: Text with sublist #}
75+
{#- Output: - TEXT\n\n (#1, #9, #142) #}
76+
{{- "- " ~ text ~ newline ~ newline ~ " (" ~ issues_list ~ ")" ~ newline }}
77+
{%- else %}
78+
{#- CASE 2: Text, no sublist #}
79+
{#- Output: - TEXT (#1, #9, #142) #}
80+
{{- "- " ~ text ~ " (" ~ issues_list ~ ")" ~ newline }}
81+
{%- endif %}
82+
83+
{%- elif text %}
84+
{#- Implicit Case: Text, but no issues #}
85+
{#- Output: - TEXT #}
86+
{{- "- " ~ text ~ newline }}
87+
{%- endif %}
88+
{%- endfor %}
89+
90+
{#- New line between list and link references #}
91+
{{- newline }}
92+
93+
{#- Link references #}
94+
{%- if issues_by_category[section][category] and "]: " in issues_by_category[section][category][0] %}
95+
{%- for issue in issues_by_category[section][category] %}
96+
{{- issue ~ newline }}
97+
{%- endfor %}
98+
{{- newline }}
99+
{%- endif %}
100+
101+
{#- No changes in this category #}
102+
{%- if sections[section][category]|length == 0 %}
103+
{{- newline }}
104+
{{- "No significant changes." ~ newline * 2 }}
105+
{%- endif %}
106+
{%- endfor %}
107+
{%- else %}
108+
{#- No changes in this section #}
109+
{{- "No significant changes." ~ newline * 2 }}
110+
{%- endif %}
111+
{%- endfor %}
112+
{#-
113+
Newline at the end of the rendered newsfile content.
57114
In this way the there are 2 newlines between the latest release and the previous release content.
58-
#}
115+
-#}
116+
{{- newline -}}

src/towncrier/test/test_build.py

Lines changed: 18 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1135,7 +1135,9 @@ def test_markdown_injected_after_header(self, runner):
11351135
dedent=True,
11361136
)
11371137

1138-
result = runner.invoke(_main, ["--date", "01-01-2001"], catch_exceptions=False)
1138+
result = runner.invoke(
1139+
_main, ["--date", "01-01-2001", "--yes"], catch_exceptions=False
1140+
)
11391141

11401142
with open("foo/newsfragments/123.feature", "w") as f:
11411143
f.write("Adds levitation")
@@ -1311,7 +1313,9 @@ def test_default_start_string(self, runner):
13111313
dedent=True,
13121314
)
13131315

1314-
result = runner.invoke(_main, ["--date", "01-01-2001"], catch_exceptions=False)
1316+
result = runner.invoke(
1317+
_main, ["--date", "01-01-2001", "--yes"], catch_exceptions=False
1318+
)
13151319
self.assertEqual(0, result.exit_code, result.output)
13161320
output = read("NEWS.rst")
13171321

@@ -1365,7 +1369,9 @@ def test_default_start_string_markdown(self, runner):
13651369
dedent=True,
13661370
)
13671371

1368-
result = runner.invoke(_main, ["--date", "01-01-2001"], catch_exceptions=False)
1372+
result = runner.invoke(
1373+
_main, ["--date", "01-01-2001", "--yes"], catch_exceptions=False
1374+
)
13691375
self.assertEqual(0, result.exit_code, result.output)
13701376
output = read("NEWS.md")
13711377

@@ -1417,7 +1423,9 @@ def test_markdown_no_name_title(self, runner):
14171423
dedent=True,
14181424
)
14191425

1420-
result = runner.invoke(_main, ["--date", "01-01-2001"], catch_exceptions=False)
1426+
result = runner.invoke(
1427+
_main, ["--date", "01-01-2001", "--yes"], catch_exceptions=False
1428+
)
14211429
self.assertEqual(0, result.exit_code, result.output)
14221430
output = read("NEWS.md")
14231431

@@ -1781,7 +1789,9 @@ def test_showcontent_default_toml_array(self, runner):
17811789
a missing `showcontent` defaults to `true`.
17821790
"""
17831791
write("foo/newsfragments/+new_feature.feature.md", "An exciting new feature!")
1784-
result = runner.invoke(_main, ["--date", "01-01-2001", "--version", "1.0.0"])
1792+
result = runner.invoke(
1793+
_main, ["--date", "01-01-2001", "--version", "1.0.0", "--yes"]
1794+
)
17851795
news = read("NEWS.rst")
17861796
expected = textwrap.dedent(
17871797
"""\
@@ -1821,7 +1831,9 @@ def test_directory_default_toml_array(self, runner):
18211831
"""
18221832
write("foo/newsfragments/+new_feature.feature.md", "An exciting new feature!")
18231833
write("foo/newsfragments/+bump_deps.deps.md", "We bumped our dependencies.")
1824-
result = runner.invoke(_main, ["--date", "01-01-2001", "--version", "1.0.0"])
1834+
result = runner.invoke(
1835+
_main, ["--date", "01-01-2001", "--version", "1.0.0", "--yes"]
1836+
)
18251837
news = read("NEWS.rst")
18261838
expected = textwrap.dedent(
18271839
"""\

src/towncrier/test/test_check.py

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -287,7 +287,12 @@ def test_none_stdout_encoding_works(self):
287287
"""
288288
No failure when output is piped causing None encoding for stdout.
289289
"""
290-
runner = CliRunner()
290+
try:
291+
runner = CliRunner(mix_stderr=False)
292+
except TypeError:
293+
# Fallback for older Click versions (or unexpected signature)
294+
print("TypeError with mix_stderr=False, falling back to echo_stdin=True")
295+
runner = CliRunner(echo_stdin=True)
291296

292297
with runner.isolated_filesystem():
293298
create_project("pyproject.toml", main_branch="master")
@@ -299,7 +304,6 @@ def test_none_stdout_encoding_works(self):
299304
check_call(["git", "add", fragment_path])
300305
check_call(["git", "commit", "-m", "add a newsfragment"])
301306

302-
runner = CliRunner(mix_stderr=False)
303307
result = runner.invoke(towncrier_check, ["--compare-with", "master"])
304308

305309
self.assertEqual(0, result.exit_code)

0 commit comments

Comments
 (0)