Skip to content

Commit

Permalink
Merge branch 'master' into config_loader
Browse files Browse the repository at this point in the history
  • Loading branch information
hawkowl authored Sep 6, 2018
2 parents d348869 + 9e1b11e commit ea9e7ba
Show file tree
Hide file tree
Showing 22 changed files with 760 additions and 418 deletions.
16 changes: 11 additions & 5 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,9 @@ sudo: false
install:
- pip install tox codecov

env:
- TOX_ENV=flake8
- TOX_ENV=check-manifest
- TOX_ENV=py27-tests
- TOX_ENV=py34-tests
before_script:
- git remote set-branches --add origin master
- git fetch origin master

script:
- tox -c tox.ini -e $TOX_ENV
Expand All @@ -19,6 +17,14 @@ after_script:
matrix:
fast_finish: true
include:
- python: 3.6
env: TOX_ENV=flake8
- python: 3.6
env: TOX_ENV=check-manifest
- python: 3.6
env: TOX_ENV=check-newsfragment
- python: 2.7
env: TOX_ENV=py27-tests
- python: 3.5
env: TOX_ENV=py35-tests
- python: 3.6
Expand Down
17 changes: 17 additions & 0 deletions NEWS.rst
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,23 @@

.. towncrier release notes start
towncrier 18.6.0 (2018-07-05)
=============================

Features
--------

- ``python -m towncrier.check``, which will check a Git branch for the presence of added newsfiles, to be used in a CI system. (`#75 <https://github.com/hawkowl/towncrier/issues/75>`_)
- wrap is now an optional configuration option (which is False by default) which controls line wrapping of news files. Towncrier will now also not attempt to normalise (wiping newlines) from the input, but will strip leading and ending whitespace. (`#80 <https://github.com/hawkowl/towncrier/issues/80>`_)
- Towncrier can now be invoked by ``python -m towncrier``. (`#115 <https://github.com/hawkowl/towncrier/issues/115>`_)


Deprecations and Removals
-------------------------

- Towncrier now supports Python 3.5+ as a script runtime. Python 2.7 will not function. (`#80 <https://github.com/hawkowl/towncrier/issues/80>`_)


towncrier 18.5.0 (2018-05-16)
=============================

Expand Down
4 changes: 1 addition & 3 deletions README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -27,12 +27,10 @@ Quick Start
Install from PyPI::

python3 -m pip install towncrier
# OR:
python2 -m pip install towncrier

.. note::

``towncrier``, as a command line tool, works on Python 2.7/3.4+ only.
``towncrier``, as a command line tool, works on Python 3.5+ only.
It is usable by projects written in other languages, provided you give it the version of the project when invoking it.
For Python 2/3 compatible projects, the version can be discovered automatically.

Expand Down
3 changes: 0 additions & 3 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,6 @@

from __future__ import absolute_import, division, print_function

import os, sys

from setuptools import setup, find_packages

setup(
Expand All @@ -17,7 +15,6 @@
"Programming Language :: Python :: 2",
"Programming Language :: Python :: 2.7",
"Programming Language :: Python :: 3",
"Programming Language :: Python :: 3.4",
"Programming Language :: Python :: 3.5",
"Programming Language :: Python :: 3.6",
],
Expand Down
109 changes: 58 additions & 51 deletions src/towncrier/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,33 +26,36 @@ def _get_date():


@click.command()
@click.option('--draft', 'draft', default=False, flag_value=True,
help=("Render the news fragments, don't write to files, "
"don't check versions."))
@click.option('--dir', 'directory', default='.')
@click.option('--name', 'project_name', default=None)
@click.option('--version', 'project_version', default=None,
help="Render the news fragments using given version.")
@click.option('--date', 'project_date', default=None)
@click.option('--yes', 'answer_yes', default=False, flag_value=True,
help="Do not ask for confirmation to remove news fragments.")
def _main(
draft, directory,
project_name, project_version, project_date,
answer_yes
):
@click.option(
"--draft",
"draft",
default=False,
flag_value=True,
help=("Render the news fragments, don't write to files, " "don't check versions."),
)
@click.option("--dir", "directory", default=".")
@click.option("--name", "project_name", default=None)
@click.option(
"--version",
"project_version",
default=None,
help="Render the news fragments using given version.",
)
@click.option("--date", "project_date", default=None)
@click.option(
"--yes",
"answer_yes",
default=False,
flag_value=True,
help="Do not ask for confirmation to remove news fragments.",
)
def _main(draft, directory, project_name, project_version, project_date, answer_yes):
return __main(
draft, directory,
project_name, project_version, project_date,
answer_yes
draft, directory, project_name, project_version, project_date, answer_yes
)


def __main(
draft, directory,
project_name, project_version, project_date,
answer_yes
):
def __main(draft, directory, project_name, project_version, project_date, answer_yes):
"""
The main entry point.
"""
Expand All @@ -61,62 +64,65 @@ def __main(
to_err = draft

click.echo("Loading template...", err=to_err)
if config['template'] is None:
if config["template"] is None:
template = pkg_resources.resource_string(
__name__,
"templates/template.rst").decode('utf8')
__name__, "templates/template.rst"
).decode("utf8")
else:
with open(config['template'], 'rb') as tmpl:
template = tmpl.read().decode('utf8')
with open(config["template"], "rb") as tmpl:
template = tmpl.read().decode("utf8")

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

definitions = config['types']
definitions = config["types"]

if config.get("directory"):
base_directory = os.path.abspath(config["directory"])
fragment_directory = None
else:
base_directory = os.path.abspath(os.path.join(
directory, config['package_dir'], config['package']))
base_directory = os.path.abspath(
os.path.join(directory, config["package_dir"], config["package"])
)
fragment_directory = "newsfragments"

fragments, fragment_filenames = find_fragments(
base_directory, config['sections'], fragment_directory, definitions)
base_directory, config["sections"], fragment_directory, definitions
)

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:])
template,
config["issue_format"],
fragments,
definitions,
config["underlines"][1:],
config["wrap"],
)

if project_version is None:
project_version = get_version(
os.path.abspath(os.path.join(directory, config['package_dir'])),
config['package'])
os.path.join(directory, config["package_dir"]), config["package"]
)

if project_name is None:
package = config.get('package')
package = config.get("package")
if package:
project_name = get_project_name(
os.path.abspath(
os.path.join(directory, config['package_dir'])),
package)
os.path.abspath(os.path.join(directory, config["package_dir"])), package
)
else:
# Can't determine a project_name, but maybe it is not needed.
project_name = ''
project_name = ""

if project_date is None:
project_date = _get_date()

top_line = config['title_format'].format(
name=project_name,
version=project_version,
project_date=project_date
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"
top_line += u"\n" + (config["underlines"][0] * len(top_line)) + u"\n"

if draft:
click.echo(
Expand All @@ -127,12 +133,13 @@ def __main(
click.echo("%s\n%s" % (top_line, rendered))
else:
click.echo("Writing to newsfile...", err=to_err)
start_line = config['start_line']
append_to_newsfile(directory, config['filename'],
start_line, top_line, rendered)
start_line = config["start_line"]
append_to_newsfile(
directory, config["filename"], start_line, top_line, rendered
)

click.echo("Staging newsfile...", err=to_err)
stage_newsfile(directory, config['filename'])
stage_newsfile(directory, config["filename"])

click.echo("Removing news fragments...", err=to_err)
remove_files(fragment_filenames, answer_yes)
Expand Down
4 changes: 4 additions & 0 deletions src/towncrier/__main__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
import towncrier


__name__ == "__main__" and towncrier._main()
62 changes: 31 additions & 31 deletions src/towncrier/_builder.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,25 +11,6 @@
from jinja2 import Template


def normalise(text):

# Blitz newlines
text = text.replace(u"\r\n", u"\n")
text = text.replace(u"\n", u" ")

# No tabs!
text = text.replace(u"\t", u" ")

# Remove double spaces
while u" " in text:
text = text.replace(u" ", u" ")

# Remove left/right whitespace
text = text.strip()

return text


# Returns a structure like:
#
# OrderedDict([
Expand Down Expand Up @@ -77,11 +58,12 @@ def find_fragments(base_directory, sections, fragment_directory, definitions):
full_filename = os.path.join(section_dir, basename)
fragment_filenames.append(full_filename)
with open(full_filename, "rb") as f:
data = f.read().decode('utf8', 'replace')
data = f.read().decode("utf8", "replace")
if (ticket, category) in file_content:
raise ValueError(
"multiple files for {}.{} in {}"
.format(ticket, category, section_dir)
"multiple files for {}.{} in {}".format(
ticket, category, section_dir
)
)
file_content[ticket, category] = data

Expand All @@ -90,6 +72,17 @@ def find_fragments(base_directory, sections, fragment_directory, definitions):
return content, fragment_filenames


def indent(text, prefix):
"""
Adds `prefix` to the beginning of non-empty lines in `text`.
"""
# Based on Python 3's textwrap.indent
def prefixed_lines():
for line in text.splitlines(True):
yield (prefix + line if line.strip() else line)
return u"".join(prefixed_lines())


# Takes the output from find_fragments above. Probably it would be useful to
# add an example output here. Next time someone digs deep enough to figure it
# out, please do so...
Expand All @@ -102,7 +95,7 @@ def split_fragments(fragments, definitions):

for (ticket, category), content in section_fragments.items():

content = normalise(content)
content = indent(content.strip(), u" ")[2:]

if definitions[category]["showcontent"] is False:
content = u""
Expand Down Expand Up @@ -148,9 +141,7 @@ def render_issue(issue_format, issue):
return issue_format.format(issue=issue)


def render_fragments(
template, issue_format, fragments, definitions, underlines,
):
def render_fragments(template, issue_format, fragments, definitions, underlines, wrap):
"""
Render the fragments into a news file.
"""
Expand Down Expand Up @@ -195,12 +186,21 @@ def render_fragments(
done = []

res = jinja_template.render(
sections=data, definitions=definitions, underlines=underlines)
sections=data, definitions=definitions, underlines=underlines
)

for line in res.split(u"\n"):
done.append(textwrap.fill(
line, width=79, subsequent_indent=u" ",
break_long_words=False, break_on_hyphens=False,
))
if wrap:
done.append(
textwrap.fill(
line,
width=79,
subsequent_indent=u" ",
break_long_words=False,
break_on_hyphens=False,
)
)
else:
done.append(line)

return u"\n".join(done).rstrip() + u"\n"
3 changes: 1 addition & 2 deletions src/towncrier/_git.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,7 @@ def remove_files(fragment_filenames, answer_yes):
for filename in fragment_filenames:
click.echo(filename)

if answer_yes or click.confirm('Is it okay if I remove those files?',
default=True):
if answer_yes or click.confirm("Is it okay if I remove those files?", default=True):
call(["git", "rm", "--quiet"] + fragment_filenames)


Expand Down
Loading

0 comments on commit ea9e7ba

Please sign in to comment.