define options with help text
Expand the way options are defined to include help text so we can eventually include that in generated configuration files and in online documentation. Change-Id: I0636f5e2fb9b21519f6cdda25a1ac546a3ffe174 Signed-off-by: Doug Hellmann <doug@doughellmann.com>
This commit is contained in:
parent
9d058ae097
commit
fab39dfcc8
115
reno/config.py
115
reno/config.py
@ -10,8 +10,10 @@
|
|||||||
# License for the specific language governing permissions and limitations
|
# License for the specific language governing permissions and limitations
|
||||||
# under the License.
|
# under the License.
|
||||||
|
|
||||||
|
import collections
|
||||||
import logging
|
import logging
|
||||||
import os.path
|
import os.path
|
||||||
|
import textwrap
|
||||||
|
|
||||||
import yaml
|
import yaml
|
||||||
|
|
||||||
@ -20,41 +22,61 @@ from reno import defaults
|
|||||||
LOG = logging.getLogger(__name__)
|
LOG = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
class Config(object):
|
Opt = collections.namedtuple('Opt', 'name default help')
|
||||||
|
|
||||||
_OPTS = {
|
_OPTIONS = [
|
||||||
# The notes subdirectory within the relnotesdir where the
|
Opt('notesdir', defaults.NOTES_SUBDIR,
|
||||||
# notes live.
|
textwrap.dedent("""
|
||||||
'notesdir': defaults.NOTES_SUBDIR,
|
The notes subdirectory within the relnotesdir where the
|
||||||
|
notes live.
|
||||||
|
""")),
|
||||||
|
|
||||||
# Should pre-release versions be merged into the final release
|
Opt('collapse_pre_releases', True,
|
||||||
# of the same number (1.0.0.0a1 notes appear under 1.0.0).
|
textwrap.dedent("""
|
||||||
'collapse_pre_releases': True,
|
Should pre-release versions be merged into the final release
|
||||||
|
of the same number (1.0.0.0a1 notes appear under 1.0.0).
|
||||||
|
""")),
|
||||||
|
|
||||||
# Should the scanner stop at the base of a branch (True) or go
|
Opt('stop_at_branch_base', True,
|
||||||
# ahead and scan the entire history (False)?
|
textwrap.dedent("""
|
||||||
'stop_at_branch_base': True,
|
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
|
# The git branch to scan. Defaults to the "current" branch
|
||||||
# checked out.
|
# checked out.
|
||||||
'branch': None,
|
""")),
|
||||||
|
|
||||||
|
Opt('earliest_version', None,
|
||||||
|
textwrap.dedent("""
|
||||||
# The earliest version to be included. This is usually the
|
# The earliest version to be included. This is usually the
|
||||||
# lowest version number, and is meant to be the oldest
|
# lowest version number, and is meant to be the oldest
|
||||||
# version.
|
# version.
|
||||||
'earliest_version': None,
|
""")),
|
||||||
|
|
||||||
|
Opt('template', defaults.TEMPLATE.format(defaults.PRELUDE_SECTION_NAME),
|
||||||
|
textwrap.dedent("""
|
||||||
# The template used by reno new to create a note.
|
# The template used by reno new to create a note.
|
||||||
'template': defaults.TEMPLATE.format(defaults.PRELUDE_SECTION_NAME),
|
""")),
|
||||||
|
|
||||||
|
Opt('release_tag_re',
|
||||||
|
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
|
# The RE pattern used to match the repo tags representing a valid
|
||||||
# release version. The pattern is compiled with the verbose and unicode
|
# release version. The pattern is compiled with the verbose and unicode
|
||||||
# flags enabled.
|
# flags enabled.
|
||||||
'release_tag_re': '''
|
""")),
|
||||||
((?:[\d.ab]|rc)+) # digits, a, b, and rc cover regular and
|
|
||||||
# pre-releases
|
|
||||||
''',
|
|
||||||
|
|
||||||
|
Opt('pre_release_tag_re',
|
||||||
|
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
|
# 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
|
# valid pre-release version. The pattern is compiled with the verbose
|
||||||
# and unicode flags enabled. The pattern must define a group called
|
# and unicode flags enabled. The pattern must define a group called
|
||||||
@ -62,20 +84,17 @@ class Config(object):
|
|||||||
# separator, e.g for pre-release version '12.0.0.0rc1' the default RE
|
# separator, e.g for pre-release version '12.0.0.0rc1' the default RE
|
||||||
# pattern will identify '.0rc1' as the value of the group
|
# pattern will identify '.0rc1' as the value of the group
|
||||||
# 'pre_release'.
|
# 'pre_release'.
|
||||||
'pre_release_tag_re': '''
|
""")),
|
||||||
(?P<pre_release>\.\d+(?:[ab]|rc)+\d*)$
|
|
||||||
''',
|
|
||||||
|
|
||||||
|
Opt('branch_name_re', 'stable/.+',
|
||||||
|
textwrap.dedent("""
|
||||||
# The pattern for names for branches that are relevant when
|
# The pattern for names for branches that are relevant when
|
||||||
# scanning history to determine where to stop, to find the
|
# scanning history to determine where to stop, to find the
|
||||||
# "base" of a branch. Other branches are ignored.
|
# "base" of a branch. Other branches are ignored.
|
||||||
'branch_name_re': 'stable/.+',
|
""")),
|
||||||
|
|
||||||
# The identifiers and names of permitted sections in the
|
Opt('sections',
|
||||||
# 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.
|
|
||||||
'sections': [
|
|
||||||
['features', 'New Features'],
|
['features', 'New Features'],
|
||||||
['issues', 'Known Issues'],
|
['issues', 'Known Issues'],
|
||||||
['upgrade', 'Upgrade Notes'],
|
['upgrade', 'Upgrade Notes'],
|
||||||
@ -85,14 +104,24 @@ class Config(object):
|
|||||||
['fixes', 'Bug Fixes'],
|
['fixes', 'Bug Fixes'],
|
||||||
['other', 'Other Notes'],
|
['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.
|
||||||
|
""")),
|
||||||
|
|
||||||
|
Opt('prelude_section_name', defaults.PRELUDE_SECTION_NAME,
|
||||||
|
textwrap.dedent("""
|
||||||
# The name of the prelude section in the note template. This
|
# The name of the prelude section in the note template. This
|
||||||
# allows users to rename the section to, for example,
|
# allows users to rename the section to, for example,
|
||||||
# 'release_summary' or 'project_wide_general_announcements',
|
# 'release_summary' or 'project_wide_general_announcements',
|
||||||
# which is displayed in titlecase in the report after
|
# which is displayed in titlecase in the report after
|
||||||
# replacing underscores with spaces.
|
# replacing underscores with spaces.
|
||||||
'prelude_section_name': defaults.PRELUDE_SECTION_NAME,
|
""")),
|
||||||
|
|
||||||
|
Opt('ignore_null_merges', True,
|
||||||
|
textwrap.dedent("""
|
||||||
# When this option is set to True, any merge commits with no
|
# When this option is set to True, any merge commits with no
|
||||||
# changes and in which the second or later parent is tagged
|
# changes and in which the second or later parent is tagged
|
||||||
# are considered "null-merges" that bring the tag information
|
# are considered "null-merges" that bring the tag information
|
||||||
@ -103,20 +132,27 @@ class Config(object):
|
|||||||
# confuses the regular traversal because it makes that stable
|
# confuses the regular traversal because it makes that stable
|
||||||
# branch appear to be part of master and/or the later stable
|
# branch appear to be part of master and/or the later stable
|
||||||
# branch. This option allows us to ignore those.
|
# branch. This option allows us to ignore those.
|
||||||
'ignore_null_merges': True,
|
""")),
|
||||||
|
|
||||||
|
Opt('ignore_notes', [],
|
||||||
|
textwrap.dedent("""
|
||||||
# Note files to be ignored. It's useful to be able to ignore a
|
# 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
|
# file if it is edited on the wrong branch. Notes should be
|
||||||
# specified by their filename or UID. Setting the value in the
|
# specified by their filename or UID. Setting the value in the
|
||||||
# configuration file makes it apply to all branches.
|
# configuration file makes it apply to all branches.
|
||||||
'ignore_notes': [],
|
""")),
|
||||||
}
|
]
|
||||||
|
|
||||||
|
|
||||||
|
class Config(object):
|
||||||
|
|
||||||
|
_OPTS = {o.name: o for o in _OPTIONS}
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def get_default(cls, opt):
|
def get_default(cls, opt):
|
||||||
"Return the default for an option."
|
"Return the default for an option."
|
||||||
try:
|
try:
|
||||||
return cls._OPTS[opt]
|
return cls._OPTS[opt].default
|
||||||
except KeyError:
|
except KeyError:
|
||||||
raise ValueError('unknown option name %r' % (opt,))
|
raise ValueError('unknown option name %r' % (opt,))
|
||||||
|
|
||||||
@ -134,7 +170,7 @@ class Config(object):
|
|||||||
relnotesdir = defaults.RELEASE_NOTES_SUBDIR
|
relnotesdir = defaults.RELEASE_NOTES_SUBDIR
|
||||||
self.relnotesdir = relnotesdir
|
self.relnotesdir = relnotesdir
|
||||||
# Initialize attributes from the defaults.
|
# Initialize attributes from the defaults.
|
||||||
self.override(**self._OPTS)
|
self.override(**{o.name: o.default for o in _OPTIONS})
|
||||||
|
|
||||||
self._contents = {}
|
self._contents = {}
|
||||||
self._load_file()
|
self._load_file()
|
||||||
@ -161,7 +197,7 @@ class Config(object):
|
|||||||
|
|
||||||
def _rename_prelude_section(self, **kwargs):
|
def _rename_prelude_section(self, **kwargs):
|
||||||
key = 'prelude_section_name'
|
key = 'prelude_section_name'
|
||||||
if key in kwargs and kwargs[key] != self._OPTS[key]:
|
if key in kwargs and kwargs[key] != self._OPTS[key].default:
|
||||||
new_prelude_name = kwargs[key]
|
new_prelude_name = kwargs[key]
|
||||||
|
|
||||||
self.template = defaults.TEMPLATE.format(new_prelude_name)
|
self.template = defaults.TEMPLATE.format(new_prelude_name)
|
||||||
@ -192,9 +228,9 @@ class Config(object):
|
|||||||
|
|
||||||
"""
|
"""
|
||||||
arg_values = {
|
arg_values = {
|
||||||
o: getattr(parsed_args, o)
|
o.name: getattr(parsed_args, o.name)
|
||||||
for o in self._OPTS.keys()
|
for o in _OPTIONS
|
||||||
if hasattr(parsed_args, o)
|
if hasattr(parsed_args, o.name)
|
||||||
}
|
}
|
||||||
self.override(**arg_values)
|
self.override(**arg_values)
|
||||||
|
|
||||||
@ -224,7 +260,10 @@ class Config(object):
|
|||||||
|
|
||||||
Returns the actual configuration options after overrides.
|
Returns the actual configuration options after overrides.
|
||||||
"""
|
"""
|
||||||
options = {o: getattr(self, o) for o in self._OPTS}
|
options = {
|
||||||
|
o.name: getattr(self, o.name)
|
||||||
|
for o in _OPTIONS
|
||||||
|
}
|
||||||
return options
|
return options
|
||||||
|
|
||||||
# def parse_config_into(parsed_arguments):
|
# def parse_config_into(parsed_arguments):
|
||||||
|
@ -37,7 +37,11 @@ collapse_pre_releases: false
|
|||||||
def test_defaults(self):
|
def test_defaults(self):
|
||||||
c = config.Config(self.tempdir.path)
|
c = config.Config(self.tempdir.path)
|
||||||
actual = c.options
|
actual = c.options
|
||||||
self.assertEqual(config.Config._OPTS, actual)
|
expected = {
|
||||||
|
o.name: o.default
|
||||||
|
for o in config._OPTIONS
|
||||||
|
}
|
||||||
|
self.assertEqual(expected, actual)
|
||||||
|
|
||||||
def test_override(self):
|
def test_override(self):
|
||||||
c = config.Config(self.tempdir.path)
|
c = config.Config(self.tempdir.path)
|
||||||
@ -45,8 +49,10 @@ collapse_pre_releases: false
|
|||||||
collapse_pre_releases=False,
|
collapse_pre_releases=False,
|
||||||
)
|
)
|
||||||
actual = c.options
|
actual = c.options
|
||||||
expected = {}
|
expected = {
|
||||||
expected.update(config.Config._OPTS)
|
o.name: o.default
|
||||||
|
for o in config._OPTIONS
|
||||||
|
}
|
||||||
expected['collapse_pre_releases'] = False
|
expected['collapse_pre_releases'] = False
|
||||||
self.assertEqual(expected, actual)
|
self.assertEqual(expected, actual)
|
||||||
|
|
||||||
@ -59,8 +65,10 @@ collapse_pre_releases: false
|
|||||||
notesdir='value2',
|
notesdir='value2',
|
||||||
)
|
)
|
||||||
actual = c.options
|
actual = c.options
|
||||||
expected = {}
|
expected = {
|
||||||
expected.update(config.Config._OPTS)
|
o.name: o.default
|
||||||
|
for o in config._OPTIONS
|
||||||
|
}
|
||||||
expected['notesdir'] = 'value2'
|
expected['notesdir'] = 'value2'
|
||||||
self.assertEqual(expected, actual)
|
self.assertEqual(expected, actual)
|
||||||
|
|
||||||
@ -108,18 +116,24 @@ collapse_pre_releases: false
|
|||||||
def test_override_from_parsed_args_empty(self):
|
def test_override_from_parsed_args_empty(self):
|
||||||
c = self._run_override_from_parsed_args([])
|
c = self._run_override_from_parsed_args([])
|
||||||
actual = {
|
actual = {
|
||||||
o: getattr(c, o)
|
o.name: getattr(c, o.name)
|
||||||
for o in config.Config._OPTS.keys()
|
for o in config._OPTIONS
|
||||||
}
|
}
|
||||||
self.assertEqual(config.Config._OPTS, actual)
|
expected = {
|
||||||
|
o.name: o.default
|
||||||
|
for o in config._OPTIONS
|
||||||
|
}
|
||||||
|
self.assertEqual(expected, actual)
|
||||||
|
|
||||||
def test_override_from_parsed_args(self):
|
def test_override_from_parsed_args(self):
|
||||||
c = self._run_override_from_parsed_args([
|
c = self._run_override_from_parsed_args([
|
||||||
'--no-collapse-pre-releases',
|
'--no-collapse-pre-releases',
|
||||||
])
|
])
|
||||||
actual = c.options
|
actual = c.options
|
||||||
expected = {}
|
expected = {
|
||||||
expected.update(config.Config._OPTS)
|
o.name: o.default
|
||||||
|
for o in config._OPTIONS
|
||||||
|
}
|
||||||
expected['collapse_pre_releases'] = False
|
expected['collapse_pre_releases'] = False
|
||||||
self.assertEqual(expected, actual)
|
self.assertEqual(expected, actual)
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user