diff --git a/src/towncrier/__init__.py b/src/towncrier/__init__.py index 06c4057a..68a7b698 100644 --- a/src/towncrier/__init__.py +++ b/src/towncrier/__init__.py @@ -69,7 +69,7 @@ def __main(draft, directory, config_file, project_name, project_version, project to_err = draft click.echo("Loading template...", err=to_err) - if config["template"] is None: + if config.get("template") is None: template = pkg_resources.resource_string( __name__, "templates/template.rst" ).decode("utf8") @@ -96,20 +96,11 @@ def __main(draft, directory, config_file, project_name, project_version, project click.echo("Rendering news fragments...", err=to_err) fragments = split_fragments(fragments, definitions) - rendered = render_fragments( - # The 0th underline is used for the top line - template, - config["issue_format"], - fragments, - definitions, - config["underlines"][1:], - config["wrap"], - ) if project_version is None: project_version = get_version( os.path.join(directory, config["package_dir"]), config["package"] - ) + ).strip() if project_name is None: package = config.get("package") @@ -122,12 +113,27 @@ def __main(draft, directory, config_file, project_name, project_version, project project_name = "" if project_date is None: - project_date = _get_date() + project_date = _get_date().strip() + + if config["title_format"]: + top_line = config["title_format"].format( + name=project_name, version=project_version, project_date=project_date + ) + top_line += u"\n" + (config["underlines"][0] * len(top_line)) + u"\n" + else: + top_line = "" - top_line = config["title_format"].format( - name=project_name, version=project_version, project_date=project_date + rendered = render_fragments( + # The 0th underline is used for the top line + template, + config["issue_format"], + fragments, + definitions, + config["underlines"][1:], + config["wrap"], + {"name": project_name, "version": project_version, "date": project_date}, + top_underline=config["underlines"][0], ) - top_line += u"\n" + (config["underlines"][0] * len(top_line)) + u"\n" if draft: click.echo( @@ -135,7 +141,10 @@ def __main(draft, directory, config_file, project_name, project_version, project "What is seen below is what would be written.\n", err=to_err, ) - click.echo("%s\n%s" % (top_line, rendered)) + if top_line: + click.echo("\n%s\n%s" % (top_line, rendered)) + else: + click.echo(rendered) else: click.echo("Writing to newsfile...", err=to_err) start_line = config["start_line"] diff --git a/src/towncrier/_builder.py b/src/towncrier/_builder.py index 35a3832e..89950d0e 100644 --- a/src/towncrier/_builder.py +++ b/src/towncrier/_builder.py @@ -109,6 +109,7 @@ def indent(text, prefix): def prefixed_lines(): for line in text.splitlines(True): yield (prefix + line if line.strip() else line) + return u"".join(prefixed_lines()) @@ -170,7 +171,16 @@ def render_issue(issue_format, issue): return issue_format.format(issue=issue) -def render_fragments(template, issue_format, fragments, definitions, underlines, wrap): +def render_fragments( + template, + issue_format, + fragments, + definitions, + underlines, + wrap, + versiondata, + top_underline="=", +): """ Render the fragments into a news file. """ @@ -215,7 +225,11 @@ def render_fragments(template, issue_format, fragments, definitions, underlines, done = [] res = jinja_template.render( - sections=data, definitions=definitions, underlines=underlines + sections=data, + definitions=definitions, + underlines=underlines, + versiondata=versiondata, + top_underline=top_underline, ) for line in res.split(u"\n"): diff --git a/src/towncrier/_project.py b/src/towncrier/_project.py index bd21084e..7b337982 100644 --- a/src/towncrier/_project.py +++ b/src/towncrier/_project.py @@ -50,13 +50,13 @@ def get_version(package_dir, package): raise Exception("No __version__, I don't know how else to look") if isinstance(version, str): - return version + return version.strip() if isinstance(version, Version): - return version.base() + return version.base().strip() if isinstance(version, tuple): - return ".".join(map(str, version)) + return ".".join(map(str, version)).strip() raise Exception( ( diff --git a/src/towncrier/_settings.py b/src/towncrier/_settings.py index 9913134e..83d88b73 100644 --- a/src/towncrier/_settings.py +++ b/src/towncrier/_settings.py @@ -8,7 +8,7 @@ _start_string = u".. towncrier release notes start\n" -_title_format = u"{name} {version} ({project_date})" +_title_format = None _template_fname = None _default_types = OrderedDict( [ @@ -28,9 +28,10 @@ def load_config(directory): def load_config_from_file(from_file): if not os.path.exists(from_file): - return None - with open(from_file, "r") as conffile: - config = toml.load(conffile) + config = {"tool": {"towncrier": {}}} + else: + with open(from_file, "r") as conffile: + config = toml.load(conffile) return parse_toml(config) diff --git a/src/towncrier/_writer.py b/src/towncrier/_writer.py index a42e9fa4..d0da0317 100644 --- a/src/towncrier/_writer.py +++ b/src/towncrier/_writer.py @@ -23,14 +23,15 @@ def append_to_newsfile(directory, filename, start_line, top_line, content): existing_content = existing_content.split(start_line, 1) - if top_line in existing_content: + if top_line and top_line in existing_content: raise ValueError("It seems you've already produced newsfiles for this version?") with open(os.path.join(directory, filename), "wb") as f: if len(existing_content) > 1: f.write(existing_content.pop(0).rstrip().encode("utf8")) - f.write((u"\n\n" + start_line + u"\n").encode("utf8")) + if start_line: + f.write((u"\n\n" + start_line + u"\n").encode("utf8")) f.write(top_line.encode("utf8")) f.write(content.encode("utf8")) diff --git a/src/towncrier/check.py b/src/towncrier/check.py index f10e2721..bdb140fe 100644 --- a/src/towncrier/check.py +++ b/src/towncrier/check.py @@ -8,11 +8,7 @@ import click -from subprocess import ( - CalledProcessError, - check_output, - STDOUT, -) +from subprocess import CalledProcessError, check_output, STDOUT from ._settings import load_config, load_config_from_file from ._builder import find_fragments @@ -40,7 +36,9 @@ def __main(comparewith, directory, pyproject): try: files_changed = ( - _run(["git", "diff", "--name-only", comparewith + "..."], cwd=base_directory) + _run( + ["git", "diff", "--name-only", comparewith + "..."], cwd=base_directory + ) .decode(getattr(sys.stdout, "encoding", "utf8")) .strip() ) diff --git a/src/towncrier/newsfragments/147.feature b/src/towncrier/newsfragments/147.feature new file mode 100644 index 00000000..8655cab7 --- /dev/null +++ b/src/towncrier/newsfragments/147.feature @@ -0,0 +1 @@ +Towncrier's templating now allows configuration of the version header. \ No newline at end of file diff --git a/src/towncrier/templates/template.rst b/src/towncrier/templates/template.rst index d77e1c10..362d8e17 100644 --- a/src/towncrier/templates/template.rst +++ b/src/towncrier/templates/template.rst @@ -1,3 +1,10 @@ +{% if versiondata.name %} +{{ versiondata.name }} {{ versiondata.version }} ({{ versiondata.date }}) +{{ top_underline * ((versiondata.name + versiondata.version + versiondata.date)|length + 4)}} +{% else %} +{{ versiondata.version }} ({{ versiondata.date }}) +{{ top_underline * ((versiondata.version + versiondata.date)|length + 3)}} +{% endif %} {% for section, _ in sections.items() %} {% set underline = underlines[0] %}{% if section %}{{section}} {{ underline * section|length }}{% set underline = underlines[1] %} diff --git a/src/towncrier/test/test_check.py b/src/towncrier/test/test_check.py index d4d7c39d..6cf01d16 100644 --- a/src/towncrier/test/test_check.py +++ b/src/towncrier/test/test_check.py @@ -42,19 +42,12 @@ def test_git_fails(self): create_project("pyproject.toml") result = runner.invoke(_main, ["--compare-with", "hblaugh"]) - self.assertIn( - "git produced output while failing", - result.output, - ) - self.assertIn( - "hblaugh", - result.output, - ) + self.assertIn("git produced output while failing", result.output) + self.assertIn("hblaugh", result.output) def test_no_changes_made(self): self._test_no_changes_made( - "pyproject.toml", - lambda runner, main, argv: runner.invoke(main, argv), + "pyproject.toml", lambda runner, main, argv: runner.invoke(main, argv) ) def test_no_changes_made_pyproject_path(self): @@ -62,8 +55,7 @@ def test_no_changes_made_pyproject_path(self): self._test_no_changes_made( pyproject, lambda runner, main, argv: runner.invoke( - main, - argv + ["--pyproject", pyproject], + main, argv + ["--pyproject", pyproject] ), ) @@ -108,11 +100,7 @@ def test_fragment_exists(self): ), result, ) - self.assertEqual( - 0, - result.exit_code, - result, - ) + self.assertEqual(0, result.exit_code, result) def test_fragment_missing(self): runner = CliRunner() diff --git a/src/towncrier/test/test_cli.py b/src/towncrier/test/test_cli.py index 27971dc3..cfbee479 100644 --- a/src/towncrier/test/test_cli.py +++ b/src/towncrier/test/test_cli.py @@ -61,7 +61,6 @@ def test_happy_path(self): Foo 1.2.3 (01-01-2001) ====================== - Features -------- @@ -161,7 +160,7 @@ def run_order_scenario(sections, types): u"Loading template...\nFinding news fragments...\nRendering news " u"fragments...\nDraft only -- nothing has been written.\nWhat is " u"seen below is what would be written.\n\nFoo 1.2.3 (01-01-2001)" - u"\n======================\n" + u"\n======================" + dedent( """ section-a @@ -204,7 +203,7 @@ def run_order_scenario(sections, types): u"Loading template...\nFinding news fragments...\nRendering news " u"fragments...\nDraft only -- nothing has been written.\nWhat is " u"seen below is what would be written.\n\nFoo 1.2.3 (01-01-2001)" - u"\n======================\n" + u"\n======================" + dedent( """ section-b @@ -312,7 +311,6 @@ def test_projectless_changelog(self): FooBarBaz 7.8.9 (01-01-2001) ============================ - Features -------- @@ -335,10 +333,6 @@ def test_no_package_changelog(self): runner = CliRunner() with runner.isolated_filesystem(): - with open("pyproject.toml", "w") as f: - f.write( - "[tool.towncrier]\n" 'title_format = "{version} ({project_date})"\n' - ) os.mkdir("newsfragments") with open("newsfragments/123.feature", "w") as f: f.write("Adds levitation") @@ -361,7 +355,6 @@ def test_no_package_changelog(self): 7.8.9 (01-01-2001) ================== - Features -------- diff --git a/src/towncrier/test/test_format.py b/src/towncrier/test/test_format.py index 3b95f972..8d5ecc27 100644 --- a/src/towncrier/test/test_format.py +++ b/src/towncrier/test/test_format.py @@ -89,8 +89,9 @@ def test_basic(self): ] ) - expected_output = ( - u""" + expected_output = u"""MyProject 1.0 (never) +===================== + Features -------- @@ -119,7 +120,6 @@ def test_basic(self): - Web fixed. (#3) """ - ) template = pkg_resources.resource_string( "towncrier", "templates/template.rst" @@ -127,13 +127,20 @@ def test_basic(self): fragments = split_fragments(fragments, definitions) output = render_fragments( - template, None, fragments, definitions, ["-", "~"], wrap=True + template, + None, + fragments, + definitions, + ["-", "~"], + wrap=True, + versiondata={"name": "MyProject", "version": "1.0", "date": "never"}, ) self.assertEqual(output, expected_output) # Check again with non-default underlines - expected_output_weird_underlines = ( - u""" + expected_output_weird_underlines = u"""MyProject 1.0 (never) +===================== + Features ******** @@ -162,10 +169,15 @@ def test_basic(self): - Web fixed. (#3) """ - ) output = render_fragments( - template, None, fragments, definitions, ["*", "^"], wrap=True + template, + None, + fragments, + definitions, + ["*", "^"], + wrap=True, + versiondata={"name": "MyProject", "version": "1.0", "date": "never"}, ) self.assertEqual(output, expected_output_weird_underlines) @@ -189,14 +201,14 @@ def test_issue_format(self): definitions = OrderedDict([("misc", {"name": "Misc", "showcontent": False})]) - expected_output = ( - u""" + expected_output = u"""MyProject 1.0 (never) +===================== + Misc ---- - xxbar, xx1, xx9, xx142 """ - ) template = pkg_resources.resource_string( "towncrier", "templates/template.rst" @@ -204,7 +216,13 @@ def test_issue_format(self): fragments = split_fragments(fragments, definitions) output = render_fragments( - template, u"xx{issue}", fragments, definitions, ["-", "~"], wrap=True + template, + u"xx{issue}", + fragments, + definitions, + ["-", "~"], + wrap=True, + versiondata={"name": "MyProject", "version": "1.0", "date": "never"}, ) self.assertEqual(output, expected_output) @@ -233,8 +251,9 @@ def test_line_wrapping(self): [("feature", {"name": "Features", "showcontent": True})] ) - expected_output = ( - u""" + expected_output = u"""MyProject 1.0 (never) +===================== + Features -------- @@ -248,7 +267,6 @@ def test_line_wrapping(self): a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a (#3) """ - ) template = pkg_resources.resource_string( "towncrier", "templates/template.rst" @@ -256,7 +274,13 @@ def test_line_wrapping(self): fragments = split_fragments(fragments, definitions) output = render_fragments( - template, None, fragments, definitions, ["-", "~"], wrap=True + template, + None, + fragments, + definitions, + ["-", "~"], + wrap=True, + versiondata={"name": "MyProject", "version": "1.0", "date": "never"}, ) self.assertEqual(output, expected_output) @@ -271,7 +295,7 @@ def test_line_wrapping_disabled(self): ( "1", "feature", - 0 + 0, ): u""" asdf asdf asdf asdf looooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooong newsfragment. """, # NOQA @@ -284,8 +308,9 @@ def test_line_wrapping_disabled(self): [("feature", {"name": "Features", "showcontent": True})] ) - expected_output = ( - u""" + expected_output = u"""MyProject 1.0 (never) +===================== + Features -------- @@ -293,7 +318,6 @@ def test_line_wrapping_disabled(self): - https://google.com/q=?---------------------------------------------------------------------------------------------------- (#2) - a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a (#3) """ # NOQA - ) template = pkg_resources.resource_string( "towncrier", "templates/template.rst" @@ -301,6 +325,12 @@ def test_line_wrapping_disabled(self): fragments = split_fragments(fragments, definitions) output = render_fragments( - template, None, fragments, definitions, ["-", "~"], wrap=False + template, + None, + fragments, + definitions, + ["-", "~"], + wrap=False, + versiondata={"name": "MyProject", "version": "1.0", "date": "never"}, ) self.assertEqual(output, expected_output) diff --git a/src/towncrier/test/test_write.py b/src/towncrier/test/test_write.py index 372bf0a1..cdad7423 100644 --- a/src/towncrier/test/test_write.py +++ b/src/towncrier/test/test_write.py @@ -43,8 +43,8 @@ def test_append_at_top(self): ] ) - expected_output = """MyProject 1.0 -============= + expected_output = """MyProject 1.0 (never) +===================== Features -------- @@ -94,9 +94,15 @@ def test_append_at_top(self): tempdir, "NEWS.rst", ".. towncrier release notes start\n", - "MyProject 1.0\n=============\n", + "", render_fragments( - template, None, fragments, definitions, ["-", "~"], wrap=True + template, + None, + fragments, + definitions, + ["-", "~"], + wrap=True, + versiondata={"name": "MyProject", "version": "1.0", "date": "never"}, ), ) @@ -140,8 +146,8 @@ def test_append_at_top_with_hint(self): .. towncrier release notes start -MyProject 1.0 -============= +MyProject 1.0 (never) +===================== Features -------- @@ -202,9 +208,15 @@ def test_append_at_top_with_hint(self): tempdir, "NEWS.rst", ".. towncrier release notes start\n", - "MyProject 1.0\n=============\n", + "", render_fragments( - template, None, fragments, definitions, ["-", "~"], wrap=True + template, + None, + fragments, + definitions, + ["-", "~"], + wrap=True, + versiondata={"name": "MyProject", "version": "1.0", "date": "never"}, ), )