summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJason Gerard DeRose <jderose@redhat.com>2008-12-11 18:07:54 -0700
committerJason Gerard DeRose <jderose@redhat.com>2008-12-11 18:07:54 -0700
commit5c47b56d14d56b825cbfe6a06e056bb98fbb2378 (patch)
tree01fb2b62eba670150aa5c1715173b0c688efa0a2
parent22209a0f0333526fe953a66f6ea4dd1be18dddc6 (diff)
downloadfreeipa-5c47b56d14d56b825cbfe6a06e056bb98fbb2378.tar.gz
freeipa-5c47b56d14d56b825cbfe6a06e056bb98fbb2378.tar.xz
freeipa-5c47b56d14d56b825cbfe6a06e056bb98fbb2378.zip
Finished kwarg validation and extension mechanism in parameter.Param
-rw-r--r--TODO2
-rw-r--r--ipalib/constants.py6
-rw-r--r--ipalib/parameter.py36
-rwxr-xr-xmake-test1
-rw-r--r--tests/test_ipalib/test_parameter.py48
5 files changed, 85 insertions, 8 deletions
diff --git a/TODO b/TODO
index 62e2a63d..23c46244 100644
--- a/TODO
+++ b/TODO
@@ -25,6 +25,8 @@ API chages before January 2009 simi-freeze:
* Implement gettext service.
+ * Add ability to register pre-op, post-op plugins per command.
+
CLI
- Prompt for password using getpass
- Passed the param dict to output_for_cli()
diff --git a/ipalib/constants.py b/ipalib/constants.py
index 06ff99d5..d028a001 100644
--- a/ipalib/constants.py
+++ b/ipalib/constants.py
@@ -26,6 +26,12 @@ All constants centralised in one file.
NULLS = (None, '', u'', tuple(), [])
+TYPE_ERROR = '%s: need a %r; got %r (a %r)'
+
+
+CALLABLE_ERROR = '%s: need a callable; got %r (a %r)'
+
+
# Used for a tab (or indentation level) when formatting for CLI:
CLI_TAB = ' ' # Two spaces
diff --git a/ipalib/parameter.py b/ipalib/parameter.py
index b75b8fbe..d67eb595 100644
--- a/ipalib/parameter.py
+++ b/ipalib/parameter.py
@@ -22,7 +22,7 @@ Parameter system for command plugins.
"""
from plugable import ReadOnly, lock, check_name
-from constants import NULLS
+from constants import NULLS, TYPE_ERROR, CALLABLE_ERROR
def parse_param_spec(spec):
@@ -83,15 +83,42 @@ class Param(ReadOnly):
required=(bool, True),
multivalue=(bool, False),
primary_key=(bool, False),
- normalize=(callable, None),
+ normalizer=(callable, None),
default=(None, None),
default_from=(callable, None),
flags=(frozenset, frozenset()),
)
- def __init__(self, name, **overrides):
+ def __init__(self, name, kwargs, **overrides):
self.param_spec = name
self.name = check_name(name)
+ kwargs = dict(kwargs)
+ assert set(self.__kwargs).intersection(kwargs) == set()
+ kwargs.update(self.__kwargs)
+ for (key, (kind, default)) in kwargs.iteritems():
+ value = overrides.get(key, default)
+ if value is None:
+ if kind is bool:
+ raise TypeError(
+ TYPE_ERROR % (key, bool, value, type(value))
+ )
+ else:
+ if (
+ type(kind) is type and type(value) is not kind or
+ type(kind) is tuple and not isinstance(value, kind)
+ ):
+ raise TypeError(
+ TYPE_ERROR % (key, kind, value, type(value))
+ )
+ elif kind is callable and not callable(value):
+ raise TypeError(
+ CALLABLE_ERROR % (key, value, type(value))
+ )
+ if hasattr(self, key):
+ raise ValueError('kwarg %r conflicts with attribute on %s' % (
+ key, self.__class__.__name__)
+ )
+ setattr(self, key, value)
lock(self)
def normalize(self, value):
@@ -120,7 +147,6 @@ class Param(ReadOnly):
except StandardError:
return value
-
def convert(self, value):
if value in NULLS:
return
@@ -178,7 +204,7 @@ class Str(Param):
def __init__(self, name, **overrides):
self.type = unicode
- super(Str, self).__init__(name, **overrides)
+ super(Str, self).__init__(name, {}, **overrides)
def _convert_scalar(self, value, index=None):
if type(value) in (self.type, int, float, bool):
diff --git a/make-test b/make-test
index 00ea676a..738a6106 100755
--- a/make-test
+++ b/make-test
@@ -25,6 +25,7 @@ done
if [ $failures ]; then
echo "[ Ran under $runs version(s); FAILED under $failures version(s) ]"
+ echo "FAIL!"
exit $failures
else
echo "[ Ran under $runs version(s); all OK ]"
diff --git a/tests/test_ipalib/test_parameter.py b/tests/test_ipalib/test_parameter.py
index 837bdfef..725ce60a 100644
--- a/tests/test_ipalib/test_parameter.py
+++ b/tests/test_ipalib/test_parameter.py
@@ -25,6 +25,7 @@ Test the `ipalib.parameter` module.
from tests.util import raises, ClassChecker
from tests.data import binary_bytes, utf8_bytes, unicode_str
from ipalib import parameter
+from ipalib.constants import TYPE_ERROR, CALLABLE_ERROR
def test_parse_param_spec():
@@ -49,24 +50,65 @@ class test_Param(ClassChecker):
Test the `ipalib.parameter.Param.__init__` method.
"""
name = 'my_param'
- o = self.cls(name)
+ o = self.cls(name, {})
assert o.name is name
+ # assert o.cli_name is name
+ assert o.doc == ''
+ assert o.required is True
+ assert o.multivalue is False
+ assert o.primary_key is False
+ assert o.normalizer is None
+ assert o.default is None
+ assert o.default_from is None
+ assert o.flags == frozenset()
assert o.__islocked__() is True
+ kwarg = dict(convert=(callable, None))
+ e = raises(ValueError, self.cls, name, kwarg)
+ assert str(e) == "kwarg 'convert' conflicts with attribute on Param"
+ class Subclass(self.cls):
+ pass
+ e = raises(ValueError, Subclass, name, kwarg)
+ assert str(e) == "kwarg 'convert' conflicts with attribute on Subclass"
+ kwargs = dict(
+ extra1=(bool, True),
+ extra2=(str, 'Hello'),
+ extra3=((int, float), 42),
+ extra4=(callable, lambda whatever: whatever + 7),
+ )
+ # Check that we don't accept None if kind is bool:
+ e = raises(TypeError, self.cls, 'my_param', kwargs, extra1=None)
+ assert str(e) == TYPE_ERROR % ('extra1', bool, None, type(None))
+ for (key, (kind, default)) in kwargs.items():
+ o = self.cls('my_param', kwargs)
+ # Test with a type invalid for all:
+ value = object()
+ overrides = {key: value}
+ e = raises(TypeError, self.cls, 'my_param', kwargs, **overrides)
+ if kind is callable:
+ assert str(e) == CALLABLE_ERROR % (key, value, type(value))
+ else:
+ assert str(e) == TYPE_ERROR % (key, kind, value, type(value))
+ if kind is bool:
+ continue
+ # Test with None:
+ overrides = {key: None}
+ o = self.cls('my_param', kwargs, **overrides)
def test_convert_scalar(self):
"""
Test the `ipalib.parameter.Param._convert_scalar` method.
"""
- o = self.cls('my_param')
+ o = self.cls('my_param', {})
e = raises(NotImplementedError, o._convert_scalar, 'some value')
assert str(e) == 'Param._convert_scalar()'
class Subclass(self.cls):
pass
- o = Subclass('my_param')
+ o = Subclass('my_param', {})
e = raises(NotImplementedError, o._convert_scalar, 'some value')
assert str(e) == 'Subclass._convert_scalar()'
+
class test_Str(ClassChecker):
"""
Test the `ipalib.parameter.Str` class.