Advanced Option
Allows for an option to be tagged with advanced true/ false, notifying the end user about uncommon usage. Such an option is not normally used, and will be moved to the bottom of the respective namespace. Only options set to true (default: false) will be tagged as such in generated sample files. Change-Id: I2689aba283e473bb293bf0cf0c1c4bcb5979d115 Implements: blueprint advanced-option
This commit is contained in:
parent
d4b7650ee0
commit
4cf28c7e3b
@ -357,6 +357,43 @@ command line arguments using the SubCommandOpt class:
|
||||
>>> conf.action.name, conf.action.id
|
||||
('list', '10')
|
||||
|
||||
Advanced Option
|
||||
---------------
|
||||
|
||||
Use if you need to label an option as advanced in sample files, indicating the
|
||||
option is not normally used by the majority of users and might have a
|
||||
significant effect on stability and/or performance::
|
||||
|
||||
from oslo_config import cfg
|
||||
|
||||
opts = [
|
||||
cfg.StrOpt('option1', default='default_value',
|
||||
advanced=True, help='This is help '
|
||||
'text.'),
|
||||
cfg.PortOpt('option2', default='default_value',
|
||||
help='This is help text.'),
|
||||
]
|
||||
|
||||
CONF = cfg.CONF
|
||||
CONF.register_opts(opts)
|
||||
|
||||
This will result in the option being pushed to the bottom of the
|
||||
namespace and labeled as advanced in the sample files, with a notation
|
||||
about possible effects::
|
||||
|
||||
[DEFAULT]
|
||||
...
|
||||
# This is help text. (string value)
|
||||
# option2 = default_value
|
||||
...
|
||||
<pushed to bottom of section>
|
||||
...
|
||||
# This is help text. (string value)
|
||||
# Advanced Option: intended for advanced users and not used
|
||||
# by the majority of users, and might have a significant
|
||||
# effect on stability and/or performance.
|
||||
# option1 = default_value
|
||||
|
||||
"""
|
||||
|
||||
import argparse
|
||||
@ -667,6 +704,8 @@ class Opt(object):
|
||||
strings are encouraged. Silently ignored if
|
||||
deprecated_for_removal is False
|
||||
:param mutable: True if this option may be reloaded
|
||||
:param advanced: a bool True/False value if this option has advanced usage
|
||||
and is not normally used by the majority of users
|
||||
|
||||
An Opt object has no public methods, but has a number of public properties:
|
||||
|
||||
@ -709,6 +748,10 @@ class Opt(object):
|
||||
|
||||
a string explaining how the option's value is used
|
||||
|
||||
.. py:attribute:: advanced
|
||||
|
||||
in sample files, a bool value indicating the option is advanced
|
||||
|
||||
.. versionchanged:: 1.2
|
||||
Added *deprecated_opts* parameter.
|
||||
|
||||
@ -729,6 +772,9 @@ class Opt(object):
|
||||
|
||||
.. versionchanged:: 3.12
|
||||
Added *deprecated_since* parameter.
|
||||
|
||||
.. versionchanged:: 3.15
|
||||
Added *advanced* parameter and attribute.
|
||||
"""
|
||||
multi = False
|
||||
|
||||
@ -738,7 +784,7 @@ class Opt(object):
|
||||
deprecated_name=None, deprecated_group=None,
|
||||
deprecated_opts=None, sample_default=None,
|
||||
deprecated_for_removal=False, deprecated_reason=None,
|
||||
deprecated_since=None, mutable=False):
|
||||
deprecated_since=None, mutable=False, advanced=False):
|
||||
if name.startswith('_'):
|
||||
raise ValueError('illegal name %s with prefix _' % (name,))
|
||||
self.name = name
|
||||
@ -776,6 +822,7 @@ class Opt(object):
|
||||
self._check_default()
|
||||
|
||||
self.mutable = mutable
|
||||
self.advanced = advanced
|
||||
|
||||
def _default_is_ref(self):
|
||||
"""Check if default is a reference to another var."""
|
||||
|
@ -259,6 +259,13 @@ class _OptFormatter(object):
|
||||
lines.extend(
|
||||
self._format_help('Reason: ' + opt.deprecated_reason))
|
||||
|
||||
if opt.advanced:
|
||||
lines.append(
|
||||
'# Advanced Option: intended for advanced users and not used\n'
|
||||
'# by the majority of users, and might have a significant\n'
|
||||
'# effect on stability and/or performance.\n'
|
||||
)
|
||||
|
||||
if hasattr(opt.type, 'format_defaults'):
|
||||
defaults = opt.type.format_defaults(opt.default,
|
||||
opt.sample_default)
|
||||
@ -389,7 +396,7 @@ def _output_opts(f, group, group_data, minimal=False):
|
||||
for (namespace, opts) in sorted(group_data['namespaces'],
|
||||
key=operator.itemgetter(0)):
|
||||
f.write('\n#\n# From %s\n#\n' % namespace)
|
||||
for opt in opts:
|
||||
for opt in sorted(opts, key=operator.attrgetter('advanced')):
|
||||
try:
|
||||
if minimal and not opt.required:
|
||||
pass
|
||||
|
@ -139,6 +139,13 @@ def _format_group(app, namespace, group_name, group_obj, opt_list):
|
||||
else:
|
||||
warnings.warn('Failed to fully format sample for %s: %s' %
|
||||
(opt.dest, err))
|
||||
if opt.advanced:
|
||||
yield _indent(
|
||||
':Advanced Option: intended for advanced users and not used',)
|
||||
yield _indent(
|
||||
':by the majority of users, and might have a significant',)
|
||||
yield _indent(
|
||||
':effect on stability and/or performance.',)
|
||||
yield ''
|
||||
|
||||
try:
|
||||
|
@ -1219,4 +1219,50 @@ bars = <None>
|
||||
self.assertEqual(expected, actual)
|
||||
|
||||
|
||||
class AdvancedOptionsTestCase(base.BaseTestCase):
|
||||
|
||||
opts = [cfg.StrOpt('foo', help='foo option', default='fred'),
|
||||
cfg.StrOpt('bar', help='bar option', advanced=True),
|
||||
cfg.StrOpt('foo_bar', help='foobar'),
|
||||
cfg.BoolOpt('bars', help='bars foo', default=True, advanced=True)]
|
||||
|
||||
def test_advanced_option_order_single_ns(self):
|
||||
|
||||
config = [("namespace1", [
|
||||
("alpha", self.opts)])]
|
||||
groups = generator._get_groups(config)
|
||||
|
||||
fd, tmp_file = tempfile.mkstemp()
|
||||
with open(tmp_file, 'w+') as f:
|
||||
formatter = generator._OptFormatter(output_file=f)
|
||||
generator._output_opts(formatter, 'alpha', groups.pop('alpha'))
|
||||
expected = '''[alpha]
|
||||
|
||||
#
|
||||
# From namespace1
|
||||
#
|
||||
|
||||
# foo option (string value)
|
||||
#foo = fred
|
||||
|
||||
# foobar (string value)
|
||||
#foo_bar = <None>
|
||||
|
||||
# bar option (string value)
|
||||
# Advanced Option: intended for advanced users and not used
|
||||
# by the majority of users, and might have a significant
|
||||
# effect on stability and/or performance.
|
||||
#bar = <None>
|
||||
|
||||
# bars foo (boolean value)
|
||||
# Advanced Option: intended for advanced users and not used
|
||||
# by the majority of users, and might have a significant
|
||||
# effect on stability and/or performance.
|
||||
#bars = true
|
||||
'''
|
||||
with open(tmp_file, 'r') as f:
|
||||
actual = f.read()
|
||||
self.assertEqual(expected, actual)
|
||||
|
||||
|
||||
GeneratorTestCase.generate_scenarios()
|
||||
|
@ -341,6 +341,51 @@ class FormatGroupTest(base.BaseTestCase):
|
||||
|
||||
''').lstrip(), results)
|
||||
|
||||
def test_advanced(self):
|
||||
results = '\n'.join(list(sphinxext._format_group(
|
||||
app=mock.Mock(),
|
||||
namespace=None,
|
||||
group_name=None,
|
||||
group_obj=None,
|
||||
opt_list=[
|
||||
cfg.StrOpt('opt_name',
|
||||
advanced=True),
|
||||
],
|
||||
)))
|
||||
self.assertEqual(textwrap.dedent('''
|
||||
.. oslo.config:group:: DEFAULT
|
||||
|
||||
.. oslo.config:option:: opt_name
|
||||
|
||||
:Type: string
|
||||
:Default: ``<None>``
|
||||
:Advanced Option: intended for advanced users and not used
|
||||
:by the majority of users, and might have a significant
|
||||
:effect on stability and/or performance.
|
||||
|
||||
''').lstrip(), results)
|
||||
|
||||
def test_not_advanced(self):
|
||||
results = '\n'.join(list(sphinxext._format_group(
|
||||
app=mock.Mock(),
|
||||
namespace=None,
|
||||
group_name=None,
|
||||
group_obj=None,
|
||||
opt_list=[
|
||||
cfg.StrOpt('opt_name',
|
||||
advanced=False),
|
||||
],
|
||||
)))
|
||||
self.assertEqual(textwrap.dedent('''
|
||||
.. oslo.config:group:: DEFAULT
|
||||
|
||||
.. oslo.config:option:: opt_name
|
||||
|
||||
:Type: string
|
||||
:Default: ``<None>``
|
||||
|
||||
''').lstrip(), results)
|
||||
|
||||
|
||||
class FormatOptionHelpTest(base.BaseTestCase):
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user