add an internal sphinx extension to show the configuration defaults

Rather than relying on contributors to add configuration data to the
usage page by hand, extract the information from the help text for the
configuration options.

Merge some of the existing documentation text with the old comments
that were turned into help text (and remove the comment markers left
behind in the last patch).

Change-Id: Ic89e6f0a083648d332ae81d20b2adbf59e4207ce
Signed-off-by: Doug Hellmann <doug@doughellmann.com>
This commit is contained in:
Doug Hellmann 2017-11-21 15:36:11 -05:00
parent fab39dfcc8
commit 7d419d4df6
6 changed files with 267 additions and 154 deletions

View File

@ -25,7 +25,6 @@ else:
has_theme = True
sys.path.insert(0, os.path.abspath('../..'))
# -- General configuration ----------------------------------------------------
# Add any Sphinx extension module names here, as strings. They can be
@ -34,6 +33,7 @@ extensions = [
'sphinx.ext.autodoc',
# 'sphinx.ext.intersphinx',
'reno.sphinxext',
'reno._exts.show_reno_config',
]
if has_theme:

View File

@ -220,102 +220,7 @@ using command-line switches. For example:
The following options are configurable:
`notesdir`
The notes subdirectory within the `relnotesdir` where the notes live.
Defaults to ``notes``.
`collapse_pre_releases`
Should pre-release versions be merged into the final release of the same
number (`1.0.0.0a1` notes appear under `1.0.0`).
Defaults to ``True``.
`stop_at_branch_base`
Should the scanner stop at the base of a branch (True) or go ahead and scan
the entire history (False)?
Defaults to ``True``.
`branch`
The git branch to scan. If a stable branch is specified but does not exist,
reno attempts to automatically convert that to an "end-of-life" tag. For
example, ``origin/stable/liberty`` would be converted to ``liberty-eol``.
Defaults to the "current" branch checked out.
`earliest_version`
The earliest version to be included. This is usually the lowest version
number, and is meant to be the oldest version. If unset, all versions will be
scanned.
Defaults to ``None``.
`template`
The template used by reno new to create a note.
`release_tag_re`
The regex pattern used to match the repo tags representing a valid release
version. The pattern is compiled with the verbose and unicode flags enabled.
Defaults to ``((?:[\d.ab]|rc)+)``.
`pre_release_tag_re`
The regex pattern used to check if a valid release version tag is also a
valid pre-release version. The pattern is compiled with the verbose and
unicode flags enabled. The pattern must define a group called `pre_release`
that matches the pre-release part of the tag and any separator, e.g for
pre-release version `12.0.0.0rc1` the default RE pattern will identify
`.0rc1` as the value of the group 'pre_release'.
Defaults to ``(?P<pre_release>\.\d+(?:[ab]|rc)+\d*)$``.
`branch_name_re`
The pattern for names for branches that are relevant when scanning history to
determine where to stop, to find the "base" of a branch. Other branches are
ignored.
Defaults to ``stable/.+``.
`sections`
The identifiers and names of permitted sections in the release notes, in the
order in which the final report will be generated. A prelude section will
always be automatically inserted before the first element of this list.
`prelude_section_name`
The name of the prelude section in the note template. Note that the
value for this must be a single word, but can have underscores. The
value is displayed in titlecase in the report after replacing
underscores with spaces.
Defaults to ``prelude``
`ignore_null_merges`
OpenStack used to use null-merges to bring final release tags from
stable branches back into the master branch. This confuses the
regular traversal because it makes that stable branch appear to be
part of master and/or the later stable branch. This option allows us
to ignore those.
When this option is set to True, any merge commits with no changes
and in which the second or later parent is tagged are considered
"null-merges" that bring the tag information into the current branch
but nothing else.
Defaults to ``True``.
`ignore_notes`
A list of filenames or UIDs for notes that should be ignored by the
reno scanner. It is most useful to set this when a note is edited on
the wrong branch, making it appear to be part of a release that it
is not.
.. warning::
Setting the option in the main configuration file makes it apply
to all branches. To ignore a note in the HTML build, use the
``ignore-notes`` parameter to the ``release-notes`` sphinx
directive.
.. show-reno-config::
Debugging
=========

0
reno/_exts/__init__.py Normal file
View File

View File

@ -0,0 +1,75 @@
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
from docutils import nodes
from docutils.parsers import rst
from docutils.statemachine import ViewList
from sphinx.util.nodes import nested_parse_with_titles
from reno import config
import six
def _multi_line_string(s, indent=''):
output_lines = s.splitlines()
if not output_lines[0].strip():
output_lines = output_lines[1:]
for l in output_lines:
yield indent + l
def _format_option_help(options):
"Produce RST lines for the configuration options."
for opt in options:
yield '``{}``'.format(opt.name)
for l in _multi_line_string(opt.help, ' '):
yield l
yield ''
if isinstance(opt.default, six.string_types) and '\n' in opt.default:
# Multi-line string
yield ' Defaults to'
yield ''
yield ' ::'
yield ''
for l in _multi_line_string(opt.default, ' '):
yield l
else:
yield ' Defaults to ``{!r}``'.format(opt.default)
yield ''
class ShowConfigDirective(rst.Directive):
option_spec = {}
has_content = True
def run(self):
env = self.state.document.settings.env
app = env.app
result = ViewList()
source_name = '<' + __name__ + '>'
for line in _format_option_help(config._OPTIONS):
app.info(line)
result.append(line, source_name)
node = nodes.section()
node.document = self.state.document
nested_parse_with_titles(self.state, result, node)
return node.children
def setup(app):
app.add_directive('show-reno-config', ShowConfigDirective)

View File

@ -26,71 +26,74 @@ Opt = collections.namedtuple('Opt', 'name default help')
_OPTIONS = [
Opt('notesdir', defaults.NOTES_SUBDIR,
textwrap.dedent("""
textwrap.dedent("""\
The notes subdirectory within the relnotesdir where the
notes live.
""")),
Opt('collapse_pre_releases', True,
textwrap.dedent("""
textwrap.dedent("""\
Should pre-release versions be merged into the final release
of the same number (1.0.0.0a1 notes appear under 1.0.0).
""")),
Opt('stop_at_branch_base', True,
textwrap.dedent("""
textwrap.dedent("""\
Should the scanner stop at the base of a branch (True) or go
ahead and scan the entire history (False)?
""")),
Opt('branch', None,
textwrap.dedent("""
# The git branch to scan. Defaults to the "current" branch
# checked out.
textwrap.dedent("""\
The git branch to scan. Defaults to the "current" branch
checked out. If a stable branch is specified but does not
exist, reno attempts to automatically convert that to an
"end-of-life" tag. For example, ``origin/stable/liberty``
would be converted to ``liberty-eol``.
""")),
Opt('earliest_version', None,
textwrap.dedent("""
# The earliest version to be included. This is usually the
# lowest version number, and is meant to be the oldest
# version.
textwrap.dedent("""\
The earliest version to be included. This is usually the
lowest version number, and is meant to be the oldest
version. If unset, all versions will be scanned.
""")),
Opt('template', defaults.TEMPLATE.format(defaults.PRELUDE_SECTION_NAME),
textwrap.dedent("""
# The template used by reno new to create a note.
textwrap.dedent("""\
The template used by reno new to create a note.
""")),
Opt('release_tag_re',
textwrap.dedent('''
textwrap.dedent('''\
((?:[\d.ab]|rc)+) # digits, a, b, and rc cover regular and
# pre-releases
'''),
textwrap.dedent("""
# The RE pattern used to match the repo tags representing a valid
# release version. The pattern is compiled with the verbose and unicode
# flags enabled.
textwrap.dedent("""\
The regex pattern used to match the repo tags representing a
valid release version. The pattern is compiled with the
verbose and unicode flags enabled.
""")),
Opt('pre_release_tag_re',
textwrap.dedent('''
textwrap.dedent('''\
(?P<pre_release>\.\d+(?:[ab]|rc)+\d*)$
'''),
textwrap.dedent("""
# The RE pattern used to check if a valid release version tag is also a
# valid pre-release version. The pattern is compiled with the verbose
# and unicode flags enabled. The pattern must define a group called
# 'pre_release' that matches the pre-release part of the tag and any
# separator, e.g for pre-release version '12.0.0.0rc1' the default RE
# pattern will identify '.0rc1' as the value of the group
# 'pre_release'.
textwrap.dedent("""\
The regex pattern used to check if a valid release version tag
is also a valid pre-release version. The pattern is compiled
with the verbose and unicode flags enabled. The pattern must
define a group called 'pre_release' that matches the
pre-release part of the tag and any separator, e.g for
pre-release version '12.0.0.0rc1' the default pattern will
identify '.0rc1' as the value of the group 'pre_release'.
""")),
Opt('branch_name_re', 'stable/.+',
textwrap.dedent("""
# The pattern for names for branches that are relevant when
# scanning history to determine where to stop, to find the
# "base" of a branch. Other branches are ignored.
textwrap.dedent("""\
The pattern for names for branches that are relevant when
scanning history to determine where to stop, to find the
"base" of a branch. Other branches are ignored.
""")),
Opt('sections',
@ -104,42 +107,46 @@ _OPTIONS = [
['fixes', 'Bug Fixes'],
['other', 'Other Notes'],
],
textwrap.dedent("""
# The identifiers and names of permitted sections in the
# release notes, in the order in which the final report will
# be generated. A prelude section will always be automatically
# inserted before the first element of this list.
textwrap.dedent("""\
The identifiers and names of permitted sections in the
release notes, in the order in which the final report will
be generated. A prelude section will always be automatically
inserted before the first element of this list.
""")),
Opt('prelude_section_name', defaults.PRELUDE_SECTION_NAME,
textwrap.dedent("""
# The name of the prelude section in the note template. This
# allows users to rename the section to, for example,
# 'release_summary' or 'project_wide_general_announcements',
# which is displayed in titlecase in the report after
# replacing underscores with spaces.
textwrap.dedent("""\
The name of the prelude section in the note template. This
allows users to rename the section to, for example,
'release_summary' or 'project_wide_general_announcements',
which is displayed in titlecase in the report after
replacing underscores with spaces.
""")),
Opt('ignore_null_merges', True,
textwrap.dedent("""
# When this option is set to True, any merge commits with no
# changes and in which the second or later parent is tagged
# are considered "null-merges" that bring the tag information
# into the current branch but nothing else.
#
# OpenStack used to use null-merges to bring final release
# tags from stable branches back into the master branch. This
# confuses the regular traversal because it makes that stable
# branch appear to be part of master and/or the later stable
# branch. This option allows us to ignore those.
textwrap.dedent("""\
When this option is set to True, any merge commits with no
changes and in which the second or later parent is tagged
are considered "null-merges" that bring the tag information
into the current branch but nothing else.
OpenStack used to use null-merges to bring final release
tags from stable branches back into the master branch. This
confuses the regular traversal because it makes that stable
branch appear to be part of master and/or the later stable
branch. This option allows us to ignore those.
""")),
Opt('ignore_notes', [],
textwrap.dedent("""
# Note files to be ignored. It's useful to be able to ignore a
# file if it is edited on the wrong branch. Notes should be
# specified by their filename or UID. Setting the value in the
# configuration file makes it apply to all branches.
textwrap.dedent("""\
Note files to be ignored. It's useful to be able to ignore a
file if it is edited on the wrong branch. Notes should be
specified by their filename or UID.
Setting the option in the main configuration file makes it
apply to all branches. To ignore a note in the HTML build, use
the ``ignore-notes`` parameter to the ``release-notes`` sphinx
directive.
""")),
]

126
reno/tests/test_exts.py Normal file
View File

@ -0,0 +1,126 @@
# -*- coding: utf-8 -*-
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
import textwrap
from reno._exts import show_reno_config
from reno import config
from reno.tests import base
class TestMultiLineString(base.TestCase):
def test_no_indent(self):
input = textwrap.dedent("""\
The notes subdirectory within the relnotesdir where the
notes live.
""")
expected = '\n'.join([
'The notes subdirectory within the relnotesdir where the',
'notes live.',
])
actual = '\n'.join(show_reno_config._multi_line_string(input))
self.assertEqual(expected, actual)
def test_with_indent(self):
input = textwrap.dedent("""\
The notes subdirectory within the relnotesdir where the
notes live.
""")
expected = '\n'.join([
' The notes subdirectory within the relnotesdir where the',
' notes live.',
])
actual = '\n'.join(show_reno_config._multi_line_string(input, ' '))
self.assertEqual(expected, actual)
def test_first_line_blank(self):
input = textwrap.dedent("""
The notes subdirectory within the relnotesdir where the
notes live.
""")
expected = '\n'.join([
' The notes subdirectory within the relnotesdir where the',
' notes live.',
])
actual = '\n'.join(show_reno_config._multi_line_string(input, ' '))
self.assertEqual(expected, actual)
class TestFormatOptionHelp(base.TestCase):
def test_simple_default(self):
opt = config.Opt(
'notesdir', 'path/to/notes',
textwrap.dedent("""\
The notes subdirectory within the relnotesdir where the
notes live.
"""),
)
actual = '\n'.join(show_reno_config._format_option_help([opt]))
expected = textwrap.dedent("""\
``notesdir``
The notes subdirectory within the relnotesdir where the
notes live.
Defaults to ``'path/to/notes'``
""")
self.assertEqual(expected, actual)
def test_bool_default(self):
opt = config.Opt(
'collapse_pre_releases', True,
textwrap.dedent("""\
Should pre-release versions be merged into the final release
of the same number (1.0.0.0a1 notes appear under 1.0.0).
"""),
)
actual = '\n'.join(show_reno_config._format_option_help([opt]))
expected = textwrap.dedent("""\
``collapse_pre_releases``
Should pre-release versions be merged into the final release
of the same number (1.0.0.0a1 notes appear under 1.0.0).
Defaults to ``True``
""")
self.assertEqual(expected, actual)
def test_multiline_default(self):
opt = config.Opt(
'release_tag_re',
textwrap.dedent('''\
((?:[\d.ab]|rc)+) # digits, a, b, and rc cover regular and
# pre-releases
'''),
textwrap.dedent("""\
The regex pattern used to match the repo tags representing a
valid release version. The pattern is compiled with the
verbose and unicode flags enabled.
"""),
)
actual = '\n'.join(show_reno_config._format_option_help([opt]))
expected = textwrap.dedent("""\
``release_tag_re``
The regex pattern used to match the repo tags representing a
valid release version. The pattern is compiled with the
verbose and unicode flags enabled.
Defaults to
::
((?:[\d.ab]|rc)+) # digits, a, b, and rc cover regular and
# pre-releases
""")
self.assertEqual(expected, actual)