diff options
author | Jason Gerard DeRose <jderose@redhat.com> | 2009-01-21 23:39:17 -0700 |
---|---|---|
committer | Rob Crittenden <rcritten@redhat.com> | 2009-02-03 15:29:00 -0500 |
commit | ae39dece1386dbc3e9a07977a538d9b87acb5e30 (patch) | |
tree | 3f1ca344309578120b2afb56debc76a0d41df20c | |
parent | 4febb4dd1417de8961b2ab092b0c530ca088b72a (diff) | |
download | freeipa-ae39dece1386dbc3e9a07977a538d9b87acb5e30.tar.gz freeipa-ae39dece1386dbc3e9a07977a538d9b87acb5e30.tar.xz freeipa-ae39dece1386dbc3e9a07977a538d9b87acb5e30.zip |
Added Command.args_options_2_params() method and its unit tests
-rw-r--r-- | ipalib/errors2.py | 70 | ||||
-rw-r--r-- | ipalib/frontend.py | 52 | ||||
-rw-r--r-- | ipalib/parameters.py | 1 | ||||
-rw-r--r-- | tests/test_ipalib/test_frontend.py | 51 | ||||
-rw-r--r-- | tests/test_ipalib/test_parameters.py | 2 | ||||
-rw-r--r-- | tests/test_ipalib/test_rpc.py | 2 |
6 files changed, 160 insertions, 18 deletions
diff --git a/ipalib/errors2.py b/ipalib/errors2.py index 7e2eea058..dccdbe6a3 100644 --- a/ipalib/errors2.py +++ b/ipalib/errors2.py @@ -463,25 +463,75 @@ class BinaryEncodingError(InvocationError): errno = 3002 -class ArgumentError(InvocationError): +class ZeroArgumentError(InvocationError): """ - **3003** Raised when a command is called with wrong number of arguments. + **3003** Raised when a command is called with arguments but takes none. + + For example: + + >>> raise ZeroArgumentError(name='ping') + Traceback (most recent call last): + ... + ZeroArgumentError: command 'ping' takes no arguments """ errno = 3003 + format = _('command %(name)r takes no arguments') -class OptionError(InvocationError): +class MaxArgumentError(InvocationError): """ - **3004** Raised when a command is called with unknown options. + **3004** Raised when a command is called with too many arguments. + + For example: + + >>> raise MaxArgumentError(name='user_add', count=2) + Traceback (most recent call last): + ... + MaxArgumentError: command 'user_add' takes at most 2 arguments """ errno = 3004 + def __init__(self, message=None, **kw): + if message is None: + format = ungettext( + 'command %(name)r takes at most %(count)d argument', + 'command %(name)r takes at most %(count)d arguments', + kw['count'] + ) + else: + format = None + InvocationError.__init__(self, format, message, **kw) + + +class OptionError(InvocationError): + """ + **3005** Raised when a command is called with unknown options. + """ + + errno = 3005 + + +class OverlapError(InvocationError): + """ + **3006** Raised when arguments and options overlap. + + For example: + + >>> raise OverlapError(names=['givenname', 'login']) + Traceback (most recent call last): + ... + OverlapError: overlapping arguments and options: ['givenname', 'login'] + """ + + errno = 3006 + format = _('overlapping arguments and options: %(names)r') + class RequirementError(InvocationError): """ - **3005** Raised when a required parameter is not provided. + **3007** Raised when a required parameter is not provided. For example: @@ -491,13 +541,13 @@ class RequirementError(InvocationError): RequirementError: 'givenname' is required """ - errno = 3005 + errno = 3007 format = _('%(name)r is required') class ConversionError(InvocationError): """ - **3006** Raised when parameter value can't be converted to correct type. + **3008** Raised when parameter value can't be converted to correct type. For example: @@ -507,13 +557,13 @@ class ConversionError(InvocationError): ConversionError: invalid 'age': must be an integer """ - errno = 3006 + errno = 3008 format = _('invalid %(name)r: %(error)s') class ValidationError(InvocationError): """ - **3007** Raised when a parameter value fails a validation rule. + **3009** Raised when a parameter value fails a validation rule. For example: @@ -523,7 +573,7 @@ class ValidationError(InvocationError): ValidationError: invalid 'sn': can be at most 128 characters """ - errno = 3007 + errno = 3009 format = _('invalid %(name)r: %(error)s') diff --git a/ipalib/frontend.py b/ipalib/frontend.py index 77a49d8bc..b38d376db 100644 --- a/ipalib/frontend.py +++ b/ipalib/frontend.py @@ -30,6 +30,9 @@ from errors import check_type, check_isinstance, raise_TypeError from parameters import create_param, Param, Str, Flag from util import make_repr +from errors2 import ZeroArgumentError, MaxArgumentError, OverlapError +from constants import TYPE_ERROR + RULE_FLAG = 'validation_rule' @@ -77,6 +80,7 @@ class Command(plugable.Plugin): 'params', 'args_to_kw', 'params_2_args_options', + 'args_options_2_params', 'output_for_cli', )) takes_options = tuple() @@ -140,19 +144,57 @@ class Command(plugable.Plugin): else: break - def args_options_2_params(self, args, options): - pass + def args_options_2_params(self, *args, **options): + """ + Merge (args, options) into params. + """ + if self.max_args is not None and len(args) > self.max_args: + if self.max_args == 0: + raise ZeroArgumentError(name=self.name) + raise MaxArgumentError(name=self.name, count=self.max_args) + params = dict(self.__options_2_params(options)) + if len(args) > 0: + arg_kw = dict(self.__args_2_params(args)) + intersection = set(arg_kw).intersection(params) + if len(intersection) > 0: + raise OverlapError(names=sorted(intersection)) + params.update(arg_kw) + return params + + def __args_2_params(self, values): + multivalue = False + for (i, arg) in enumerate(self.args()): + assert not multivalue + if len(values) > i: + if arg.multivalue: + multivalue = True + if len(values) == i + 1 and type(values[i]) in (list, tuple): + yield (arg.name, values[i]) + else: + yield (arg.name, values[i:]) + else: + yield (arg.name, values[i]) + else: + break + + def __options_2_params(self, options): + for name in self.params: + if name in options: + yield (name, options[name]) def params_2_args_options(self, params): """ Split params into (args, kw). """ args = tuple(params.get(name, None) for name in self.args) - options = dict( - (name, params.get(name, None)) for name in self.options - ) + options = dict(self.__params_2_options(params)) return (args, options) + def __params_2_options(self, params): + for name in self.options: + if name in params: + yield(name, params[name]) + def normalize(self, **kw): """ Return a dictionary of normalized values. diff --git a/ipalib/parameters.py b/ipalib/parameters.py index 8928df16e..fc6880747 100644 --- a/ipalib/parameters.py +++ b/ipalib/parameters.py @@ -231,6 +231,7 @@ class Param(ReadOnly): ('autofill', bool, False), ('query', bool, False), ('attribute', bool, False), + ('limit_to', frozenset, None), ('flags', frozenset, frozenset()), # The 'default' kwarg gets appended in Param.__init__(): diff --git a/tests/test_ipalib/test_frontend.py b/tests/test_ipalib/test_frontend.py index df61453ab..25e225135 100644 --- a/tests/test_ipalib/test_frontend.py +++ b/tests/test_ipalib/test_frontend.py @@ -329,14 +329,61 @@ class test_Command(ClassChecker): e = raises(errors.ArgumentError, o.args_to_kw, 1, 2, 3) assert str(e) == 'example takes at most 2 arguments' + def test_args_options_2_params(self): + """ + Test the `ipalib.frontend.Command.args_options_2_params` method. + """ + assert 'args_options_2_params' in self.cls.__public__ # Public + + # Test that ZeroArgumentError is raised: + o = self.get_instance() + e = raises(errors2.ZeroArgumentError, o.args_options_2_params, 1) + assert e.name == 'example' + + # Test that MaxArgumentError is raised (count=1) + o = self.get_instance(args=('one?',)) + e = raises(errors2.MaxArgumentError, o.args_options_2_params, 1, 2) + assert e.name == 'example' + assert e.count == 1 + assert str(e) == "command 'example' takes at most 1 argument" + + # Test that MaxArgumentError is raised (count=2) + o = self.get_instance(args=('one', 'two?')) + e = raises(errors2.MaxArgumentError, o.args_options_2_params, 1, 2, 3) + assert e.name == 'example' + assert e.count == 2 + assert str(e) == "command 'example' takes at most 2 arguments" + + # Test that OverlapError is raised: + o = self.get_instance(args=('one', 'two'), options=('three', 'four')) + e = raises(errors2.OverlapError, o.args_options_2_params, + 1, 2, three=3, two=2, four=4, one=1) + assert e.names == ['one', 'two'] + + # Test the permutations: + o = self.get_instance(args=('one', 'two*'), options=('three', 'four')) + mthd = o.args_options_2_params + assert mthd() == dict() + assert mthd(1) == dict(one=1) + assert mthd(1, 2) == dict(one=1, two=(2,)) + assert mthd(1, 21, 22, 23) == dict(one=1, two=(21, 22, 23)) + assert mthd(1, (21, 22, 23)) == dict(one=1, two=(21, 22, 23)) + assert mthd(three=3, four=4) == dict(three=3, four=4) + assert mthd(three=3, four=4, one=1, two=2) == \ + dict(one=1, two=2, three=3, four=4) + assert mthd(1, 21, 22, 23, three=3, four=4) == \ + dict(one=1, two=(21, 22, 23), three=3, four=4) + assert mthd(1, (21, 22, 23), three=3, four=4) == \ + dict(one=1, two=(21, 22, 23), three=3, four=4) + def test_params_2_args_options(self): """ Test the `ipalib.frontend.Command.params_2_args_options` method. """ assert 'params_2_args_options' in self.cls.__public__ # Public o = self.get_instance(args=['one'], options=['two']) - assert o.params_2_args_options({}) == ((None,), dict(two=None)) - assert o.params_2_args_options(dict(one=1)) == ((1,), dict(two=None)) + assert o.params_2_args_options({}) == ((None,), {}) + assert o.params_2_args_options(dict(one=1)) == ((1,), {}) assert o.params_2_args_options(dict(two=2)) == ((None,), dict(two=2)) assert o.params_2_args_options(dict(two=2, one=1)) == \ ((1,), dict(two=2)) diff --git a/tests/test_ipalib/test_parameters.py b/tests/test_ipalib/test_parameters.py index 261e14811..e9e514eb1 100644 --- a/tests/test_ipalib/test_parameters.py +++ b/tests/test_ipalib/test_parameters.py @@ -165,6 +165,8 @@ class test_Param(ClassChecker): assert o._get_default is None assert o.autofill is False assert o.query is False + assert o.attribute is False + assert o.limit_to is None assert o.flags == frozenset() # Test that ValueError is raised when a kwarg from a subclass diff --git a/tests/test_ipalib/test_rpc.py b/tests/test_ipalib/test_rpc.py index 296e9bc1c..bc8936ab6 100644 --- a/tests/test_ipalib/test_rpc.py +++ b/tests/test_ipalib/test_rpc.py @@ -221,7 +221,7 @@ class test_xmlclient(PluginTester): 'user_add', (rpc.xml_wrap(params),), {}, - Fault(3005, u"'four' is required"), # RequirementError + Fault(3007, u"'four' is required"), # RequirementError ), ( 'user_add', |