summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJason Gerard DeRose <jderose@redhat.com>2009-01-21 23:39:17 -0700
committerRob Crittenden <rcritten@redhat.com>2009-02-03 15:29:00 -0500
commitae39dece1386dbc3e9a07977a538d9b87acb5e30 (patch)
tree3f1ca344309578120b2afb56debc76a0d41df20c
parent4febb4dd1417de8961b2ab092b0c530ca088b72a (diff)
downloadfreeipa-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.py70
-rw-r--r--ipalib/frontend.py52
-rw-r--r--ipalib/parameters.py1
-rw-r--r--tests/test_ipalib/test_frontend.py51
-rw-r--r--tests/test_ipalib/test_parameters.py2
-rw-r--r--tests/test_ipalib/test_rpc.py2
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',