diff -Nru python-oslo.config-5.1.0/AUTHORS python-oslo.config-5.2.0/AUTHORS --- python-oslo.config-5.1.0/AUTHORS 2017-11-20 16:28:15.000000000 +0000 +++ python-oslo.config-5.2.0/AUTHORS 2018-01-08 14:15:24.000000000 +0000 @@ -164,6 +164,7 @@ lzyeval melissaml ricolin +shangxiaobj skudriashev sonu.kumar ting.wang diff -Nru python-oslo.config-5.1.0/ChangeLog python-oslo.config-5.2.0/ChangeLog --- python-oslo.config-5.1.0/ChangeLog 2017-11-20 16:28:15.000000000 +0000 +++ python-oslo.config-5.2.0/ChangeLog 2018-01-08 14:15:24.000000000 +0000 @@ -1,6 +1,16 @@ CHANGES ======= +5.2.0 +----- + +* Fix the invalid links for doc file in oslo.config +* sphinxext: Don't sometimes emit trailing newlines +* Provide descriptions for choices +* Remove -U from pip install +* Avoid tox\_install.sh for constraints support +* Clean up enforce\_type related test method's name + 5.1.0 ----- diff -Nru python-oslo.config-5.1.0/debian/changelog python-oslo.config-5.2.0/debian/changelog --- python-oslo.config-5.1.0/debian/changelog 2017-12-08 11:21:56.000000000 +0000 +++ python-oslo.config-5.2.0/debian/changelog 2018-01-26 10:47:17.000000000 +0000 @@ -1,3 +1,9 @@ +python-oslo.config (1:5.2.0-0ubuntu1) bionic; urgency=medium + + * New upstream release for OpenStack Queens. + + -- James Page Fri, 26 Jan 2018 10:47:17 +0000 + python-oslo.config (1:5.1.0-0ubuntu1) bionic; urgency=medium * New upstream release for OpenStack Queens. diff -Nru python-oslo.config-5.1.0/doc/source/reference/styleguide.rst python-oslo.config-5.2.0/doc/source/reference/styleguide.rst --- python-oslo.config-5.1.0/doc/source/reference/styleguide.rst 2017-11-20 16:25:42.000000000 +0000 +++ python-oslo.config-5.2.0/doc/source/reference/styleguide.rst 2018-01-08 14:12:21.000000000 +0000 @@ -9,7 +9,7 @@ configuration files, such as ``etc/cinder/cinder.conf`` in the cinder repository. They are also displayed in the `OpenStack Configuration Reference -`_. +`_. Examples:: @@ -29,7 +29,7 @@ 2. Only use single spaces, no double spaces. -3. Properly capitalize words. If in doubt check the `OpenStack Glossary `_. +3. Properly capitalize words. If in doubt check the `OpenStack Glossary `_. 4. End each segment with a period and write complete sentences if possible. Examples:: diff -Nru python-oslo.config-5.1.0/oslo_config/cfg.py python-oslo.config-5.2.0/oslo_config/cfg.py --- python-oslo.config-5.1.0/oslo_config/cfg.py 2017-11-20 16:25:42.000000000 +0000 +++ python-oslo.config-5.2.0/oslo_config/cfg.py 2018-01-08 14:12:21.000000000 +0000 @@ -1225,7 +1225,8 @@ Option with ``type`` :class:`oslo_config.types.String` :param name: the option's name - :param choices: Optional sequence of valid values. + :param choices: Optional sequence of either valid values or tuples of valid + values with descriptions. :param quotes: If True and string is enclosed with single or double quotes, will strip those quotes. :param regex: Optional regular expression (string or compiled @@ -1248,6 +1249,10 @@ .. versionchanged:: 2.7 Added *max_length* parameter + + .. versionchanged:: 5.2 + The *choices* parameter will now accept a sequence of tuples, where each + tuple is of form (*choice*, *description*) """ def __init__(self, name, choices=None, quotes=None, @@ -1275,10 +1280,11 @@ if getattr(self.type, 'choices', None): choices_text = ', '.join([self._get_choice_text(choice) for choice in self.type.choices]) - if kwargs['help'] is not None: - kwargs['help'] += (' Allowed values: %s\n' % choices_text) - else: - kwargs['help'] = (' Allowed values: %s\n' % choices_text) + if kwargs['help'] is None: + kwargs['help'] = '' + + kwargs['help'].rstrip('\n') + kwargs['help'] += '\n Allowed values: %s\n' % choices_text return kwargs @@ -1448,7 +1454,8 @@ :param name: the option's name :param min: minimum value the port can take :param max: maximum value the port can take - :param choices: Optional sequence of valid values. + :param choices: Optional sequence of either valid values or tuples of valid + values with descriptions. :param \*\*kwargs: arbitrary keyword arguments passed to :class:`Opt` .. versionadded:: 2.6 @@ -1458,6 +1465,9 @@ Allow port number with 0. .. versionchanged:: 3.16 Added *min* and *max* parameters. + .. versionchanged:: 5.2 + The *choices* parameter will now accept a sequence of tuples, where each + tuple is of form (*choice*, *description*) """ def __init__(self, name, min=None, max=None, choices=None, **kwargs): diff -Nru python-oslo.config-5.1.0/oslo_config/generator.py python-oslo.config-5.2.0/oslo_config/generator.py --- python-oslo.config-5.1.0/oslo_config/generator.py 2017-11-20 16:25:42.000000000 +0000 +++ python-oslo.config-5.2.0/oslo_config/generator.py 2018-01-08 14:12:21.000000000 +0000 @@ -66,14 +66,18 @@ 'longer help text for Sphinx documents.'), cfg.StrOpt( 'format', - help='Desired format for the output. "ini" is the only one which can ' - 'be used directly with oslo.config. "json" and "yaml" are ' - 'intended for third-party tools that want to write config files ' - 'based on the sample config data. "rst" can be used to dump ' - 'the text given to sphinx when building documentation using ' - 'the sphinx extension, for debugging.', + help='Desired format for the output.', default='ini', - choices=['ini', 'json', 'yaml', 'rst'], + choices=[ + ('ini', 'The only format that can be used directly with ' + 'oslo.config.'), + ('json', 'Intended for third-party tools that want to write ' + 'config files based on the sample config data.'), + ('yaml', 'Same as json'), + ('rst', 'Can be used to dump the text given to Sphinx when ' + 'building documentation using the Sphinx extension. ' + 'Useful for debugging,') + ], dest='format_'), ] @@ -257,9 +261,12 @@ lines.append('# Maximum value: %d\n' % opt.type.max) if getattr(opt.type, 'choices', None): - choices_text = ', '.join([self._get_choice_text(choice) - for choice in opt.type.choices]) - lines.append('# Allowed values: %s\n' % choices_text) + lines.append('# Possible values:\n') + for choice in opt.type.choices: + help_text = '%s - %s' % ( + self._get_choice_text(choice), + opt.type.choices[choice] or '') + lines.extend(self._format_help(help_text)) try: if opt.mutable: @@ -581,9 +588,14 @@ entry = {key: value for key, value in opt.__dict__.items() if not key.startswith('_')} entry['namespace'] = namespace - # In some types, choices is explicitly set to None. Force it to [] so it - # is always an iterable type. - entry['choices'] = getattr(entry['type'], 'choices', []) or [] + # Where present, we store choices as an OrderedDict. The default repr for + # this is not very machine readable, thus, it is switched to a list of + # tuples here. In addition, in some types, choices is explicitly set to + # None. Force these cases to [] so it is always an iterable type. + if getattr(entry['type'], 'choices', None): + entry['choices'] = list(entry['type'].choices.items()) + else: + entry['choices'] = [] entry['min'] = getattr(entry['type'], 'min', None) entry['max'] = getattr(entry['type'], 'max', None) entry['type'] = _format_type_name(entry['type']) diff -Nru python-oslo.config-5.1.0/oslo_config/sphinxext.py python-oslo.config-5.2.0/oslo_config/sphinxext.py --- python-oslo.config-5.1.0/oslo_config/sphinxext.py 2017-11-20 16:25:42.000000000 +0000 +++ python-oslo.config-5.2.0/oslo_config/sphinxext.py 2018-01-08 14:12:21.000000000 +0000 @@ -49,7 +49,6 @@ yield ' - * %s' % row[0] for r in row[1:]: yield ' * %s' % r - yield '' def _indent(text, n=2): @@ -152,7 +151,6 @@ 'by the majority of users, and might have a significant', 6) yield _indent( 'effect on stability and/or performance.', 6) - yield '' try: help_text = opt.help % {'default': 'the value above'} @@ -162,10 +160,23 @@ # invalid formatting characters help_text = opt.help if help_text: + yield '' yield _indent(help_text) + + # We don't bother outputting this if not using new-style choices with + # inline descriptions + if getattr(opt.type, 'choices', None) and not all( + x is None for x in opt.type.choices.values()): yield '' + yield _indent('.. rubric:: Possible values') + for choice in opt.type.choices: + yield '' + yield _indent(_get_choice_text(choice)) + yield _indent(_indent( + opt.type.choices[choice] or '')) if opt.deprecated_opts: + yield '' for line in _list_table( ['Group', 'Name'], ((d.group or group_name, @@ -173,7 +184,9 @@ for d in opt.deprecated_opts), title='Deprecated Variations'): yield _indent(line) + if opt.deprecated_for_removal: + yield '' yield _indent('.. warning::') if opt.deprecated_since: yield _indent(' This option is deprecated for removal ' @@ -182,10 +195,9 @@ yield _indent(' This option is deprecated for removal.') yield _indent(' Its value may be silently ignored ') yield _indent(' in the future.') - yield '' if opt.deprecated_reason: + yield '' yield _indent(' :Reason: ' + opt.deprecated_reason) - yield '' yield '' diff -Nru python-oslo.config-5.1.0/oslo_config/tests/test_cfg.py python-oslo.config-5.2.0/oslo_config/tests/test_cfg.py --- python-oslo.config-5.1.0/oslo_config/tests/test_cfg.py 2017-11-20 16:25:42.000000000 +0000 +++ python-oslo.config-5.2.0/oslo_config/tests/test_cfg.py 2018-01-08 14:11:54.000000000 +0000 @@ -3237,16 +3237,7 @@ self.assertRaises(ValueError, self.conf.set_default, 'oo', 'c', 'f') - def test_enforce_type_default_override(self): - self.conf.register_opt(cfg.StrOpt('foo', default='foo')) - self.conf([]) - self.assertEqual('foo', self.conf.foo) - self.conf.set_default('foo', 'bar') - self.assertEqual('bar', self.conf.foo) - self.conf.clear_default('foo') - self.assertEqual('foo', self.conf.foo) - - def test_enforce_type_wrong_type_default(self): + def test_wrong_type_default_override(self): self.conf.register_opt(cfg.IntOpt('foo', default=1)) self.conf([]) self.assertEqual(1, self.conf.foo) @@ -3322,7 +3313,7 @@ self.conf.clear_override('foo') self.assertIsNone(self.conf.foo) - def test_enforce_type_str_override(self): + def test__str_override(self): self.conf.register_opt(cfg.StrOpt('foo')) self.conf.set_override('foo', True) self.conf([]) @@ -3330,7 +3321,7 @@ self.conf.clear_override('foo') self.assertIsNone(self.conf.foo) - def test_enforce_type_wrong_type_override(self): + def test__wrong_type_override(self): self.conf.register_opt(cfg.IntOpt('foo')) self.assertRaises(ValueError, self.conf.set_override, 'foo', "not_really_a_int") @@ -3349,7 +3340,7 @@ self.assertRaises(ValueError, self.conf.set_override, 'oo', 'c', 'f') - def test_enforce_type_bool_override(self): + def test_bool_override(self): self.conf.register_opt(cfg.BoolOpt('foo')) self.conf.set_override('foo', 'True') self.conf([]) @@ -3357,7 +3348,7 @@ self.conf.clear_override('foo') self.assertIsNone(self.conf.foo) - def test_enforce_type_int_override_with_None(self): + def test_int_override_with_None(self): self.conf.register_opt(cfg.IntOpt('foo')) self.conf.set_override('foo', None) self.conf([]) @@ -3365,7 +3356,7 @@ self.conf.clear_override('foo') self.assertIsNone(self.conf.foo) - def test_enforce_type_str_override_with_None(self): + def test_str_override_with_None(self): self.conf.register_opt(cfg.StrOpt('foo')) self.conf.set_override('foo', None) self.conf([]) @@ -3373,7 +3364,7 @@ self.conf.clear_override('foo') self.assertIsNone(self.conf.foo) - def test_enforce_type_List_override(self): + def test_List_override(self): self.conf.register_opt(cfg.ListOpt('foo')) self.conf.set_override('foo', ['aa', 'bb']) self.conf([]) diff -Nru python-oslo.config-5.1.0/oslo_config/tests/test_fixture.py python-oslo.config-5.2.0/oslo_config/tests/test_fixture.py --- python-oslo.config-5.1.0/oslo_config/tests/test_fixture.py 2017-11-20 16:25:42.000000000 +0000 +++ python-oslo.config-5.2.0/oslo_config/tests/test_fixture.py 2018-01-08 14:11:54.000000000 +0000 @@ -42,7 +42,7 @@ self.assertEqual('changed_value', f.conf.get('testing_option')) - def test_overridden_value_with_enforce_type(self): + def test_overridden_value_with_wrong_type(self): f = self._make_fixture() self.assertEqual(5, f.conf.get('test2')) self.assertEqual('a', f.conf.get('test3')) diff -Nru python-oslo.config-5.1.0/oslo_config/tests/test_generator.py python-oslo.config-5.2.0/oslo_config/tests/test_generator.py --- python-oslo.config-5.1.0/oslo_config/tests/test_generator.py 2017-11-20 16:25:42.000000000 +0000 +++ python-oslo.config-5.2.0/oslo_config/tests/test_generator.py 2018-01-08 14:12:21.000000000 +0000 @@ -431,7 +431,12 @@ # # a string with choices (string value) -# Allowed values: , '', a, b, c +# Possible values: +# - +# '' - +# a - +# b - +# c - #choices_opt = a ''')), ('deprecated opt without deprecated group', @@ -1219,7 +1224,13 @@ 'help': '', 'standard_opts': ['choices_opt'], 'opts': [{'advanced': False, - 'choices': (None, '', 'a', 'b', 'c'), + 'choices': [ + (None, None), + ('', None), + ('a', None), + ('b', None), + ('c', None) + ], 'default': 'a', 'deprecated_for_removal': False, 'deprecated_opts': [], diff -Nru python-oslo.config-5.1.0/oslo_config/tests/test_sphinxext.py python-oslo.config-5.2.0/oslo_config/tests/test_sphinxext.py --- python-oslo.config-5.1.0/oslo_config/tests/test_sphinxext.py 2017-11-20 16:25:42.000000000 +0000 +++ python-oslo.config-5.2.0/oslo_config/tests/test_sphinxext.py 2018-01-08 14:12:21.000000000 +0000 @@ -42,7 +42,6 @@ :Default: ```` this appears in the default group - ''').lstrip(), results) def test_with_default_value(self): @@ -66,7 +65,6 @@ :Default: ``this is the default`` this appears in the default group - ''').lstrip(), results) def test_with_min(self): @@ -88,7 +86,6 @@ :Type: integer :Default: ```` :Minimum Value: 1 - ''').lstrip(), results) def test_with_min_0(self): @@ -110,7 +107,6 @@ :Type: integer :Default: ```` :Minimum Value: 0 - ''').lstrip(), results) def test_with_max(self): @@ -132,7 +128,6 @@ :Type: integer :Default: ```` :Maximum Value: 1 - ''').lstrip(), results) def test_with_max_0(self): @@ -154,7 +149,6 @@ :Type: integer :Default: ```` :Maximum Value: 0 - ''').lstrip(), results) def test_with_choices(self): @@ -176,7 +170,50 @@ :Type: string :Default: ```` :Valid Values: a, b, c, , '' + ''').lstrip(), results) + def test_with_choices_with_descriptions(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', + choices=[ + ('a', 'a is the best'), + ('b', 'Actually, may-b I am better'), + ('c', 'c, I am clearly the greatest'), + (None, 'I am having none of this'), + ('', '')]), + ], + ))) + self.assertEqual(textwrap.dedent(''' + .. oslo.config:group:: DEFAULT + + .. oslo.config:option:: opt_name + + :Type: string + :Default: ```` + :Valid Values: a, b, c, , '' + + .. rubric:: Possible values + + a + a is the best + + b + Actually, may-b I am better + + c + c, I am clearly the greatest + + + I am having none of this + + '' + ''').lstrip(), results) def test_group_obj_without_help(self): @@ -195,7 +232,6 @@ :Type: string :Default: ```` - ''').lstrip(), results) def test_group_obj_with_help(self): @@ -216,7 +252,6 @@ :Type: string :Default: ```` - ''').lstrip(), results) def test_deprecated_opts_without_deprecated_group(self): @@ -246,7 +281,6 @@ * Name - * DEFAULT * deprecated_name - ''').lstrip(), results) def test_deprecated_opts_with_deprecated_group(self): @@ -277,7 +311,6 @@ * Name - * deprecated_group * deprecated_name - ''').lstrip(), results) def test_deprecated_for_removal(self): @@ -317,7 +350,6 @@ :Type: integer :Default: ```` :Mutable: This option can be changed without restarting. - ''').lstrip(), results) def test_not_mutable(self): @@ -338,7 +370,6 @@ :Type: integer :Default: ```` - ''').lstrip(), results) def test_advanced(self): @@ -362,7 +393,6 @@ :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): @@ -383,7 +413,6 @@ :Type: string :Default: ```` - ''').lstrip(), results) diff -Nru python-oslo.config-5.1.0/oslo_config/tests/test_types.py python-oslo.config-5.2.0/oslo_config/tests/test_types.py --- python-oslo.config-5.1.0/oslo_config/tests/test_types.py 2017-11-20 16:25:42.000000000 +0000 +++ python-oslo.config-5.2.0/oslo_config/tests/test_types.py 2018-01-08 14:12:21.000000000 +0000 @@ -67,6 +67,11 @@ self.type_instance = types.String(choices=('foo', 'bar')) self.assertConvertedValue('foo', 'foo') + def test_listed_value_dict(self): + self.type_instance = types.String(choices=[ + ('foo', 'ab'), ('bar', 'xy')]) + self.assertConvertedValue('foo', 'foo') + def test_unlisted_value(self): self.type_instance = types.String(choices=['foo', 'bar']) self.assertInvalid('baz') @@ -98,7 +103,11 @@ def test_repr_with_choices_tuple(self): t = types.String(choices=('foo', 'bar')) - self.assertEqual('String(choices=(\'foo\', \'bar\'))', repr(t)) + self.assertEqual('String(choices=[\'foo\', \'bar\'])', repr(t)) + + def test_repr_with_choices_dict(self): + t = types.String(choices=[('foo', 'ab'), ('bar', 'xy')]) + self.assertEqual('String(choices=[\'foo\', \'bar\'])', repr(t)) def test_equal(self): self.assertTrue(types.String() == types.String()) @@ -108,9 +117,8 @@ t2 = types.String(choices=['foo', 'bar']) t3 = types.String(choices=('foo', 'bar')) t4 = types.String(choices=['bar', 'foo']) - self.assertTrue(t1 == t2) - self.assertTrue(t1 == t3) - self.assertTrue(t1 == t4) + t5 = types.String(choices=[('foo', 'ab'), ('bar', 'xy')]) + self.assertTrue(t1 == t2 == t3 == t4 == t5) def test_not_equal_with_different_choices(self): t1 = types.String(choices=['foo', 'bar']) @@ -282,7 +290,11 @@ def test_repr_with_choices_tuple(self): t = types.Integer(choices=(80, 457)) - self.assertEqual('Integer(choices=(80, 457))', repr(t)) + self.assertEqual('Integer(choices=[80, 457])', repr(t)) + + def test_repr_with_choices_dict(self): + t = types.Integer(choices=[(80, 'ab'), (457, 'xy')]) + self.assertEqual('Integer(choices=[80, 457])', repr(t)) def test_equal(self): self.assertTrue(types.Integer() == types.Integer()) @@ -302,8 +314,8 @@ t1 = types.Integer(choices=[80, 457]) t2 = types.Integer(choices=[457, 80]) t3 = types.Integer(choices=(457, 80)) - self.assertTrue(t1 == t2) - self.assertTrue(t1 == t3) + t4 = types.Integer(choices=[(80, 'ab'), (457, 'xy')]) + self.assertTrue(t1 == t2 == t3 == t4) def test_not_equal(self): self.assertFalse(types.Integer(min=123) == types.Integer(min=456)) @@ -369,21 +381,24 @@ self.assertRaises(ValueError, t, 201) self.assertRaises(ValueError, t, -457) - def test_with_choices_list(self): - t = types.Integer(choices=[80, 457]) + def _test_with_choices(self, t): self.assertRaises(ValueError, t, 1) self.assertRaises(ValueError, t, 200) self.assertRaises(ValueError, t, -457) t(80) t(457) + def test_with_choices_list(self): + t = types.Integer(choices=[80, 457]) + self._test_with_choices(t) + def test_with_choices_tuple(self): t = types.Integer(choices=(80, 457)) - self.assertRaises(ValueError, t, 1) - self.assertRaises(ValueError, t, 200) - self.assertRaises(ValueError, t, -457) - t(80) - t(457) + self._test_with_choices(t) + + def test_with_choices_dict(self): + t = types.Integer(choices=[(80, 'ab'), (457, 'xy')]) + self._test_with_choices(t) class FloatTypeTests(TypeTestHelper, unittest.TestCase): @@ -865,16 +880,29 @@ def test_repr_with_choices_tuple(self): t = types.Port(choices=(80, 457)) - self.assertEqual('Port(choices=(80, 457))', repr(t)) + self.assertEqual('Port(choices=[80, 457])', repr(t)) - def test_choices(self): - t = types.Port(choices=[80, 457]) + def _test_with_choices(self, t): self.assertRaises(ValueError, t, 1) self.assertRaises(ValueError, t, 200) + self.assertRaises(ValueError, t, -457) t(80) t(457) + def test_with_choices_list(self): + t = types.Port(choices=[80, 457]) + self._test_with_choices(t) + + def test_with_choices_tuple(self): + t = types.Port(choices=(80, 457)) + self._test_with_choices(t) + + def test_with_choices_dict(self): + t = types.Port(choices=[(80, 'ab'), (457, 'xy')]) + self._test_with_choices(t) + def test_invalid_choices(self): + """Check for choices that are specifically invalid for ports.""" self.assertRaises(ValueError, types.Port, choices=[-1, 457]) self.assertRaises(ValueError, types.Port, choices=[1, 2, 3, 65536]) @@ -896,8 +924,8 @@ t1 = types.Port(choices=[80, 457]) t2 = types.Port(choices=[457, 80]) t3 = types.Port(choices=(457, 80)) - self.assertTrue(t1 == t2) - self.assertTrue(t1 == t3) + t4 = types.Port(choices=[(457, 'ab'), (80, 'xy')]) + self.assertTrue(t1 == t2 == t3 == t4) def test_not_equal(self): self.assertFalse(types.Port(min=123) == types.Port(min=456)) @@ -973,19 +1001,3 @@ t = types.Port(max=0) self.assertRaises(ValueError, t, 1) t(0) - - def test_with_choices_list(self): - t = types.Port(choices=[80, 457]) - self.assertRaises(ValueError, t, 1) - self.assertRaises(ValueError, t, 200) - self.assertRaises(ValueError, t, -457) - t(80) - t(457) - - def test_with_choices_tuple(self): - t = types.Port(choices=(80, 457)) - self.assertRaises(ValueError, t, 1) - self.assertRaises(ValueError, t, 200) - self.assertRaises(ValueError, t, -457) - t(80) - t(457) diff -Nru python-oslo.config-5.1.0/oslo_config/types.py python-oslo.config-5.2.0/oslo_config/types.py --- python-oslo.config-5.1.0/oslo_config/types.py 2017-11-20 16:25:42.000000000 +0000 +++ python-oslo.config-5.2.0/oslo_config/types.py 2018-01-08 14:12:21.000000000 +0000 @@ -19,6 +19,7 @@ .. versionadded:: 1.3 """ +import collections import operator import re import warnings @@ -68,8 +69,8 @@ String values do not get transformed and are returned as str objects. - :param choices: Optional sequence of valid values. Mutually - exclusive with 'regex'. + :param choices: Optional sequence of either valid values or tuples of valid + values with descriptions. Mutually exclusive with 'regex'. :param quotes: If True and string is enclosed with single or double quotes, will strip those quotes. Will signal error if string have quote at the beginning and no quote at @@ -96,6 +97,10 @@ .. versionchanged:: 2.7 Added *max_length* parameter. Added *type_name* parameter. + + .. versionchanged:: 5.2 + The *choices* parameter will now accept a sequence of tuples, where each + tuple is of form (*choice*, *description*) """ def __init__(self, choices=None, quotes=False, regex=None, @@ -109,10 +114,17 @@ self.quotes = quotes self.max_length = max_length or 0 - self.choices = choices + if choices is not None: + if not all(isinstance(choice, tuple) for choice in choices): + choices = [(choice, None) for choice in choices] + + self.choices = collections.OrderedDict(choices) + else: + self.choices = None + self.lower_case_choices = None if self.choices is not None and self.ignore_case: - self.lower_case_choices = [c.lower() for c in choices] + self.lower_case_choices = [c.lower() for c in self.choices] self.regex = regex if self.regex is not None: @@ -146,7 +158,7 @@ # Check for case insensitive processed_value, choices = ((value.lower(), self.lower_case_choices) if self.ignore_case else - (value, self.choices)) + (value, self.choices.keys())) if processed_value in choices: return value @@ -158,7 +170,7 @@ def __repr__(self): details = [] if self.choices is not None: - details.append("choices={!r}".format(self.choices)) + details.append("choices={!r}".format(list(self.choices.keys()))) if self.regex: details.append("regex=%r" % self.regex.pattern) if details: @@ -168,11 +180,12 @@ def __eq__(self, other): return ( (self.__class__ == other.__class__) and - (set(self.choices) == set(other.choices) if - self.choices and other.choices else - self.choices == other.choices) and (self.quotes == other.quotes) and - (self.regex == other.regex) + (self.regex == other.regex) and + (set([x for x in self.choices or []]) == + set([x for x in other.choices or []]) if + self.choices and other.choices else + self.choices == other.choices) ) def _formatter(self, value): @@ -252,9 +265,14 @@ :param type_name: Type name to be used in the sample config file. :param min: Optional check that value is greater than or equal to min. :param max: Optional check that value is less than or equal to max. - :param choices: Optional sequence of valid values. + :param choices: Optional sequence of either valid values or tuples of valid + values with descriptions. .. versionadded:: 3.14 + + .. versionchanged:: 5.2 + The *choices* parameter will now accept a sequence of tuples, where each + tuple is of form (*choice*, *description*) """ def __init__(self, num_type, type_name, @@ -263,15 +281,24 @@ if min is not None and max is not None and max < min: raise ValueError('Max value is less than min value') - invalid_choices = [c for c in choices or [] + + if choices is not None: + if not all(isinstance(choice, tuple) for choice in choices): + choices = [(choice, None) for choice in choices] + + self.choices = collections.OrderedDict(choices) + else: + self.choices = None + + invalid_choices = [c for c in self.choices or [] if (min is not None and min > c) or (max is not None and max < c)] if invalid_choices: raise ValueError("Choices %s are out of bounds [%s..%s]" % (invalid_choices, min, max)) + self.min = min self.max = max - self.choices = choices self.num_type = num_type def __call__(self, value): @@ -297,7 +324,7 @@ def __repr__(self): props = [] if self.choices is not None: - props.append("choices={!r}".format(self.choices)) + props.append("choices={!r}".format(list(self.choices.keys()))) else: if self.min is not None: props.append('min=%g' % self.min) @@ -313,7 +340,8 @@ (self.__class__ == other.__class__) and (self.min == other.min) and (self.max == other.max) and - (set(self.choices) == set(other.choices) if + (set([x for x in self.choices or []]) == + set([x for x in other.choices or []]) if self.choices and other.choices else self.choices == other.choices) ) @@ -332,7 +360,8 @@ :param min: Optional check that value is greater than or equal to min. :param max: Optional check that value is less than or equal to max. :param type_name: Type name to be used in the sample config file. - :param choices: Optional sequence of valid values. + :param choices: Optional sequence of either valid values or tuples of valid + values with descriptions. .. versionchanged:: 2.4 The class now honors zero for *min* and *max* parameters. @@ -346,6 +375,10 @@ .. versionchanged:: 3.16 *choices* is no longer mutually exclusive with *min*/*max*. If those are supplied, all choices are verified to be within the range. + + .. versionchanged:: 5.2 + The *choices* parameter will now accept a sequence of tuples, where each + tuple is of form (*choice*, *description*) """ def __init__(self, min=None, max=None, type_name='integer value', @@ -385,9 +418,14 @@ :param min: Optional check that value is greater than or equal to min. :param max: Optional check that value is less than or equal to max. :param type_name: Type name to be used in the sample config file. - :param choices: Optional sequence of valid values. + :param choices: Optional sequence of either valid values or tuples of valid + values with descriptions. .. versionadded:: 3.16 + + .. versionchanged:: 5.2 + The *choices* parameter will now accept a sequence of tuples, where each + tuple is of form (*choice*, *description*) """ PORT_MIN = 0 diff -Nru python-oslo.config-5.1.0/oslo.config.egg-info/pbr.json python-oslo.config-5.2.0/oslo.config.egg-info/pbr.json --- python-oslo.config-5.1.0/oslo.config.egg-info/pbr.json 2017-11-20 16:28:15.000000000 +0000 +++ python-oslo.config-5.2.0/oslo.config.egg-info/pbr.json 2018-01-08 14:15:24.000000000 +0000 @@ -1 +1 @@ -{"git_version": "eb6ff02", "is_release": true} \ No newline at end of file +{"git_version": "5578616", "is_release": true} \ No newline at end of file diff -Nru python-oslo.config-5.1.0/oslo.config.egg-info/PKG-INFO python-oslo.config-5.2.0/oslo.config.egg-info/PKG-INFO --- python-oslo.config-5.1.0/oslo.config.egg-info/PKG-INFO 2017-11-20 16:28:15.000000000 +0000 +++ python-oslo.config-5.2.0/oslo.config.egg-info/PKG-INFO 2018-01-08 14:15:24.000000000 +0000 @@ -1,6 +1,6 @@ Metadata-Version: 1.1 Name: oslo.config -Version: 5.1.0 +Version: 5.2.0 Summary: Oslo Configuration API Home-page: https://docs.openstack.org/oslo.config/latest/ Author: OpenStack diff -Nru python-oslo.config-5.1.0/oslo.config.egg-info/SOURCES.txt python-oslo.config-5.2.0/oslo.config.egg-info/SOURCES.txt --- python-oslo.config-5.1.0/oslo.config.egg-info/SOURCES.txt 2017-11-20 16:28:16.000000000 +0000 +++ python-oslo.config-5.2.0/oslo.config.egg-info/SOURCES.txt 2018-01-08 14:15:24.000000000 +0000 @@ -82,6 +82,7 @@ releasenotes/notes/add-reno-71dc832ce29b962f.yaml releasenotes/notes/machine-readable-sample-config-e8f8ba43ababcf99.yaml releasenotes/notes/show-deprecated-reason-361a8eb31e05c97e.yaml +releasenotes/notes/support-choice-descriptions-8b2d0c14fbd16b2a.yaml releasenotes/source/conf.py releasenotes/source/index.rst releasenotes/source/liberty.rst @@ -91,5 +92,4 @@ releasenotes/source/pike.rst releasenotes/source/unreleased.rst releasenotes/source/_static/.placeholder -releasenotes/source/_templates/.placeholder -tools/tox_install.sh \ No newline at end of file +releasenotes/source/_templates/.placeholder \ No newline at end of file diff -Nru python-oslo.config-5.1.0/PKG-INFO python-oslo.config-5.2.0/PKG-INFO --- python-oslo.config-5.1.0/PKG-INFO 2017-11-20 16:28:16.000000000 +0000 +++ python-oslo.config-5.2.0/PKG-INFO 2018-01-08 14:15:24.000000000 +0000 @@ -1,6 +1,6 @@ Metadata-Version: 1.1 Name: oslo.config -Version: 5.1.0 +Version: 5.2.0 Summary: Oslo Configuration API Home-page: https://docs.openstack.org/oslo.config/latest/ Author: OpenStack diff -Nru python-oslo.config-5.1.0/releasenotes/notes/support-choice-descriptions-8b2d0c14fbd16b2a.yaml python-oslo.config-5.2.0/releasenotes/notes/support-choice-descriptions-8b2d0c14fbd16b2a.yaml --- python-oslo.config-5.1.0/releasenotes/notes/support-choice-descriptions-8b2d0c14fbd16b2a.yaml 1970-01-01 00:00:00.000000000 +0000 +++ python-oslo.config-5.2.0/releasenotes/notes/support-choice-descriptions-8b2d0c14fbd16b2a.yaml 2018-01-08 14:12:21.000000000 +0000 @@ -0,0 +1,14 @@ +--- +features: + - | + String, Number, Integer, Float and Port now support value-description + tuples in the interable provided for the *choice* parameter. Support for + value-only definitions is retained. + - | + StringOpt and PortOpt now support a value-description tuples in the + iterable provided for the *choice* parameter. Support for value-only + definitions is retained. + - | + *oslo-config-generator* and the Sphinx extension will now output + descriptions for option choices where provided. This will impact tooling + that relies on the *yaml* and *json* output of the former. diff -Nru python-oslo.config-5.1.0/tools/tox_install.sh python-oslo.config-5.2.0/tools/tox_install.sh --- python-oslo.config-5.1.0/tools/tox_install.sh 2017-11-20 16:25:42.000000000 +0000 +++ python-oslo.config-5.2.0/tools/tox_install.sh 1970-01-01 00:00:00.000000000 +0000 @@ -1,30 +0,0 @@ -#!/usr/bin/env bash - -# Client constraint file contains this client version pin that is in conflict -# with installing the client from source. We should remove the version pin in -# the constraints file before applying it for from-source installation. - -CONSTRAINTS_FILE="$1" -shift 1 - -set -e - -# NOTE(tonyb): Place this in the tox enviroment's log dir so it will get -# published to logs.openstack.org for easy debugging. -localfile="$VIRTUAL_ENV/log/upper-constraints.txt" - -if [[ "$CONSTRAINTS_FILE" != http* ]]; then - CONSTRAINTS_FILE="file://$CONSTRAINTS_FILE" -fi -# NOTE(tonyb): need to add curl to bindep.txt if the project supports bindep -curl "$CONSTRAINTS_FILE" --insecure --progress-bar --output "$localfile" - -pip install -c"$localfile" openstack-requirements - -# This is the main purpose of the script: Allow local installation of -# the current repo. It is listed in constraints file and thus any -# install will be constrained and we need to unconstrain it. -edit-constraints "$localfile" -- "$CLIENT_NAME" - -pip install -c"$localfile" -U "$@" -exit $? diff -Nru python-oslo.config-5.1.0/tox.ini python-oslo.config-5.2.0/tox.ini --- python-oslo.config-5.1.0/tox.ini 2017-11-20 16:25:42.000000000 +0000 +++ python-oslo.config-5.2.0/tox.ini 2018-01-08 14:11:54.000000000 +0000 @@ -4,12 +4,11 @@ envlist = py35,py27,pep8 [testenv] -setenv = - VIRTUAL_ENV={envdir} - BRANCH_NAME=master - CLIENT_NAME=oslo.config -install_command = {toxinidir}/tools/tox_install.sh {env:UPPER_CONSTRAINTS_FILE:https://git.openstack.org/cgit/openstack/requirements/plain/upper-constraints.txt} {opts} {packages} -deps = -r{toxinidir}/test-requirements.txt +install_command = pip install {opts} {packages} +deps = + -c{env:UPPER_CONSTRAINTS_FILE:https://git.openstack.org/cgit/openstack/requirements/plain/upper-constraints.txt} + -r{toxinidir}/test-requirements.txt + -r{toxinidir}/requirements.txt commands = python setup.py test --coverage --coverage-package-name=oslo_config --slowest --testr-args='{posargs}' coverage report --show-missing @@ -32,7 +31,6 @@ commands = python setup.py build_sphinx [testenv:bandit] -deps = -r{toxinidir}/test-requirements.txt commands = bandit -r oslo_config -x tests -n5 [flake8]