diff --git a/glanceclient/common/utils.py b/glanceclient/common/utils.py index c0d16a56..fe7a51a5 100644 --- a/glanceclient/common/utils.py +++ b/glanceclient/common/utils.py @@ -81,6 +81,17 @@ def schema_args(schema_getter, omit=None): kwargs = {} type_str = property.get('type', 'string') + + if isinstance(type_str, list): + # NOTE(flaper87): This means the server has + # returned something like `['null', 'string']`, + # therfore we use the first non-`null` type as + # the valid type. + for t in type_str: + if t != 'null': + type_str = t + break + if type_str == 'array': items = property.get('items') kwargs['type'] = typemap.get(items.get('type')) @@ -97,8 +108,13 @@ def schema_args(schema_getter, omit=None): if 'enum' in property: if len(description): description += " " - description += ("Valid values: " + - ', '.join(property.get('enum'))) + + # NOTE(flaper87): Make sure all values are `str/unicode` + # for the `join` to succeed. Enum types can also be `None` + # therfore, join's call would fail without the following + # list comprehension + vals = [six.text_type(val) for val in property.get('enum')] + description += ('Valid values: ' + ', '.join(vals)) kwargs['help'] = description func.__dict__.setdefault('arguments', diff --git a/tests/test_utils.py b/tests/test_utils.py index 5f191b80..a8677c4b 100644 --- a/tests/test_utils.py +++ b/tests/test_utils.py @@ -117,3 +117,47 @@ class TestUtils(testtools.TestCase): ret = utils.exception_to_str(FakeException('\xa5 error message')) self.assertEqual("Caught '%(exception)s' exception." % {'exception': 'FakeException'}, ret) + + def test_schema_args_with_list_types(self): + # NOTE(flaper87): Regression for bug + # https://bugs.launchpad.net/python-glanceclient/+bug/1401032 + + def schema_getter(_type='string', enum=False): + prop = { + 'type': ['null', _type], + 'description': 'Test schema (READ-ONLY)', + } + + if enum: + prop['enum'] = [None, 'opt-1', 'opt-2'] + + def actual_getter(): + return { + 'additionalProperties': False, + 'required': ['name'], + 'name': 'test_schema', + 'properties': { + 'test': prop, + } + } + + return actual_getter + + def dummy_func(): + pass + + decorated = utils.schema_args(schema_getter())(dummy_func) + arg, opts = decorated.__dict__['arguments'][0] + self.assertIn('--test', arg) + self.assertEqual(str, opts['type']) + + decorated = utils.schema_args(schema_getter('integer'))(dummy_func) + arg, opts = decorated.__dict__['arguments'][0] + self.assertIn('--test', arg) + self.assertEqual(int, opts['type']) + + decorated = utils.schema_args(schema_getter(enum=True))(dummy_func) + arg, opts = decorated.__dict__['arguments'][0] + self.assertIn('--test', arg) + self.assertEqual(str, opts['type']) + self.assertIn('None, opt-1, opt-2', opts['help'])