Merge "Advanced Option"
This commit is contained in:
commit
2449197425
@ -358,6 +358,43 @@ command line arguments using the SubCommandOpt class:
|
|||||||
>>> conf.action.name, conf.action.id
|
>>> conf.action.name, conf.action.id
|
||||||
('list', '10')
|
('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
|
import argparse
|
||||||
@ -666,6 +703,8 @@ class Opt(object):
|
|||||||
strings are encouraged. Silently ignored if
|
strings are encouraged. Silently ignored if
|
||||||
deprecated_for_removal is False
|
deprecated_for_removal is False
|
||||||
:param mutable: True if this option may be reloaded
|
: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:
|
An Opt object has no public methods, but has a number of public properties:
|
||||||
|
|
||||||
@ -708,6 +747,10 @@ class Opt(object):
|
|||||||
|
|
||||||
a string explaining how the option's value is used
|
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
|
.. versionchanged:: 1.2
|
||||||
Added *deprecated_opts* parameter.
|
Added *deprecated_opts* parameter.
|
||||||
|
|
||||||
@ -728,6 +771,9 @@ class Opt(object):
|
|||||||
|
|
||||||
.. versionchanged:: 3.12
|
.. versionchanged:: 3.12
|
||||||
Added *deprecated_since* parameter.
|
Added *deprecated_since* parameter.
|
||||||
|
|
||||||
|
.. versionchanged:: 3.15
|
||||||
|
Added *advanced* parameter and attribute.
|
||||||
"""
|
"""
|
||||||
multi = False
|
multi = False
|
||||||
|
|
||||||
@ -737,7 +783,7 @@ class Opt(object):
|
|||||||
deprecated_name=None, deprecated_group=None,
|
deprecated_name=None, deprecated_group=None,
|
||||||
deprecated_opts=None, sample_default=None,
|
deprecated_opts=None, sample_default=None,
|
||||||
deprecated_for_removal=False, deprecated_reason=None,
|
deprecated_for_removal=False, deprecated_reason=None,
|
||||||
deprecated_since=None, mutable=False):
|
deprecated_since=None, mutable=False, advanced=False):
|
||||||
if name.startswith('_'):
|
if name.startswith('_'):
|
||||||
raise ValueError('illegal name %s with prefix _' % (name,))
|
raise ValueError('illegal name %s with prefix _' % (name,))
|
||||||
self.name = name
|
self.name = name
|
||||||
@ -775,6 +821,7 @@ class Opt(object):
|
|||||||
self._check_default()
|
self._check_default()
|
||||||
|
|
||||||
self.mutable = mutable
|
self.mutable = mutable
|
||||||
|
self.advanced = advanced
|
||||||
|
|
||||||
def _default_is_ref(self):
|
def _default_is_ref(self):
|
||||||
"""Check if default is a reference to another var."""
|
"""Check if default is a reference to another var."""
|
||||||
|
@ -259,6 +259,13 @@ class _OptFormatter(object):
|
|||||||
lines.extend(
|
lines.extend(
|
||||||
self._format_help('Reason: ' + opt.deprecated_reason))
|
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'):
|
if hasattr(opt.type, 'format_defaults'):
|
||||||
defaults = opt.type.format_defaults(opt.default,
|
defaults = opt.type.format_defaults(opt.default,
|
||||||
opt.sample_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'],
|
for (namespace, opts) in sorted(group_data['namespaces'],
|
||||||
key=operator.itemgetter(0)):
|
key=operator.itemgetter(0)):
|
||||||
f.write('\n#\n# From %s\n#\n' % namespace)
|
f.write('\n#\n# From %s\n#\n' % namespace)
|
||||||
for opt in opts:
|
for opt in sorted(opts, key=operator.attrgetter('advanced')):
|
||||||
try:
|
try:
|
||||||
if minimal and not opt.required:
|
if minimal and not opt.required:
|
||||||
pass
|
pass
|
||||||
|
@ -140,6 +140,13 @@ def _format_group(app, namespace, group_name, group_obj, opt_list):
|
|||||||
else:
|
else:
|
||||||
warnings.warn('Failed to fully format sample for %s: %s' %
|
warnings.warn('Failed to fully format sample for %s: %s' %
|
||||||
(opt.dest, err))
|
(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 ''
|
yield ''
|
||||||
|
|
||||||
try:
|
try:
|
||||||
|
@ -1219,4 +1219,50 @@ bars = <None>
|
|||||||
self.assertEqual(expected, actual)
|
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()
|
GeneratorTestCase.generate_scenarios()
|
||||||
|
@ -341,6 +341,51 @@ class FormatGroupTest(base.BaseTestCase):
|
|||||||
|
|
||||||
''').lstrip(), results)
|
''').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):
|
class FormatOptionHelpTest(base.BaseTestCase):
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user