Expose version error message generically

The change in https://review.openstack.org/#/c/652100/ has improved
the error message for the required placement api version::

 Operation or argument is not supported with version 1.0

had become::

 Operation or argument is not supported with version 1.0; \
  requires at least version 1.1

However, this was only applied for aggregate use cases. This patch
follows it up to apply the improved error message generically to other
commands than aggregate and other version compare function than
"greater than or equal to".

Change-Id: I28226a1dbc003a8b6366e528469152c141b40593
Story: #2005448
Task: #30498
This commit is contained in:
Tetsuro Nakamura 2019-04-17 04:54:51 +00:00 committed by Chris Dent
parent da8cd4d68b
commit 250a8d18cd
3 changed files with 48 additions and 28 deletions

View File

@ -62,7 +62,7 @@ class SetAggregate(command.Lister, version.CheckerMixin):
return parser
@version.check(version.ge('1.1'), min_version='1.1')
@version.check(version.ge('1.1'))
def take_action(self, parsed_args):
http = self.app.client_manager.placement
@ -106,7 +106,7 @@ class ListAggregate(command.Lister):
return parser
@version.check(version.ge('1.1'), min_version='1.1')
@version.check(version.ge('1.1'))
def take_action(self, parsed_args):
http = self.app.client_manager.placement

View File

@ -32,23 +32,38 @@ class TestVersion(base.BaseTestCase):
self.assertTrue(
version._compare(
'0.3', version.eq('0.2'), version.eq('0.3'), op=any))
self.assertFalse(version._compare('1.0', version.gt('1.0')))
self.assertFalse(version._compare('1.0', version.ge('1.1')))
self.assertFalse(version._compare('1.0', version.eq('1.1')))
self.assertFalse(version._compare('1.0', version.le('0.9')))
self.assertFalse(version._compare('1.0', version.lt('0.9')))
# Test error message
msg = 'Operation or argument is not supported with version 1.0; '
self.assertEqual((msg + 'requires version greater than 1.0'),
version._compare('1.0', version.gt('1.0')))
self.assertEqual((msg + 'requires at least version 1.1'),
version._compare('1.0', version.ge('1.1')))
self.assertEqual((msg + 'requires version 1.1'),
version._compare('1.0', version.eq('1.1')))
self.assertEqual((msg + 'requires at most version 0.9'),
version._compare('1.0', version.le('0.9')))
self.assertEqual((msg + 'requires version less than 0.9'),
version._compare('1.0', version.lt('0.9')))
self.assertRaises(
ValueError, version._compare, 'abc', version.le('1.1'))
self.assertRaises(
ValueError, version._compare, '1.0', version.le('.0'))
self.assertRaises(
ValueError, version._compare, '1', version.le('2'))
ex = self.assertRaises(
ValueError, version.compare, '1.0', version.ge('1.1'),
min_version='1.1')
ValueError, version.compare, '1.0', version.ge('1.1'))
self.assertEqual(
'Operation or argument is not supported with version 1.0; '
'requires at least version 1.1', six.text_type(ex))
ex = self.assertRaises(
ValueError, version.compare, '1.0',
version.eq('1.1'), version.eq('1.5'), op=any)
self.assertEqual(
'Operation or argument is not supported with version 1.0; '
'requires version 1.1, or requires version 1.5', six.text_type(ex))
def test_compare_with_exc(self):
self.assertTrue(version.compare('1.05', version.gt('1.4')))

View File

@ -40,37 +40,49 @@ SUPPORTED_VERSIONS = [
]
def _op(func, b):
return lambda a: func(StrictVersion(a), StrictVersion(b))
def _op(func, b, msg):
return lambda a: func(StrictVersion(a), StrictVersion(b)) or msg
def lt(b):
return _op(operator.lt, b)
msg = 'requires version less than %s' % b
return _op(operator.lt, b, msg)
def le(b):
return _op(operator.le, b)
msg = 'requires at most version %s' % b
return _op(operator.le, b, msg)
def eq(b):
return _op(operator.eq, b)
msg = 'requires version %s' % b
return _op(operator.eq, b, msg)
def ne(b):
return _op(operator.ne, b)
msg = 'can not use version %s' % b
return _op(operator.ne, b, msg)
def ge(b):
return _op(operator.ge, b)
msg = 'requires at least version %s' % b
return _op(operator.ge, b, msg)
def gt(b):
return _op(operator.gt, b)
msg = 'requires version greater than %s' % b
return _op(operator.gt, b, msg)
def _compare(ver, *predicates, **kwargs):
func = kwargs.get('op', all)
return func(p(ver) for p in predicates)
if func(p(ver) is True for p in predicates):
return True
# construct an error message if the requirement not satisfied
err_msg = 'Operation or argument is not supported with version %s; ' % ver
err_detail = [p(ver) for p in predicates if p(ver) is not True]
logic = ', and ' if func is all else ', or '
return err_msg + logic.join(err_detail)
def compare(ver, *predicates, **kwargs):
@ -78,24 +90,17 @@ def compare(ver, *predicates, **kwargs):
kwargs['exc'] - boolean whether exception should be raised
kwargs['op'] - (all, any) how predicates should be checked
kwargs['min_version'] - optional; used to aid in the error message that is
given, for example, to specify a minimum version to run a command.
Examples:
compare('1.1', version.gt('1.2'), exc=False) - False
compare('1.1', version.eq('1.0'), version.eq('1.1'), op=any) - True
compare('1.0', version.ge('1.1'), min_version='1.1') - raise ValueError
"""
exc = kwargs.get('exc', True)
if not _compare(ver, *predicates, **kwargs):
result = _compare(ver, *predicates, **kwargs)
if result is not True:
if exc:
msg = ('Operation or argument is not supported with version %s' %
ver)
if 'min_version' in kwargs:
msg = ('%s; requires at least version %s' %
(msg, kwargs['min_version']))
raise ValueError(msg)
raise ValueError(result)
return False
return True