diff --git a/oslo_config/cfg.py b/oslo_config/cfg.py index d3ecd15f..0a26b9d6 100644 --- a/oslo_config/cfg.py +++ b/oslo_config/cfg.py @@ -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 + ... + + ... + # 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.""" diff --git a/oslo_config/generator.py b/oslo_config/generator.py index 550e0d9b..22a9f288 100644 --- a/oslo_config/generator.py +++ b/oslo_config/generator.py @@ -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 diff --git a/oslo_config/sphinxext.py b/oslo_config/sphinxext.py index a7efb87c..f1d082b6 100644 --- a/oslo_config/sphinxext.py +++ b/oslo_config/sphinxext.py @@ -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: diff --git a/oslo_config/tests/test_generator.py b/oslo_config/tests/test_generator.py index b38df545..cdbca48c 100644 --- a/oslo_config/tests/test_generator.py +++ b/oslo_config/tests/test_generator.py @@ -1219,4 +1219,50 @@ bars = 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 = + +# 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 = + +# 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() diff --git a/oslo_config/tests/test_sphinxext.py b/oslo_config/tests/test_sphinxext.py index 8db5e776..03e7cbb2 100644 --- a/oslo_config/tests/test_sphinxext.py +++ b/oslo_config/tests/test_sphinxext.py @@ -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: ```` + :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: ```` + + ''').lstrip(), results) + class FormatOptionHelpTest(base.BaseTestCase):