summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJason Gerard DeRose <jderose@redhat.com>2008-12-12 04:48:25 -0700
committerJason Gerard DeRose <jderose@redhat.com>2008-12-12 04:48:25 -0700
commite05fd7ab03eb05c26e880ae04fbf095198b58027 (patch)
treeb054c789c0d4eb4118d1679cf9ea4b9443326326
parent66faffdfb09ee2bcee1b405c78b37e340bc043aa (diff)
downloadfreeipa-e05fd7ab03eb05c26e880ae04fbf095198b58027.tar.gz
freeipa-e05fd7ab03eb05c26e880ae04fbf095198b58027.tar.xz
freeipa-e05fd7ab03eb05c26e880ae04fbf095198b58027.zip
New Param: added basic rule logic
-rw-r--r--ipalib/parameter.py55
-rw-r--r--tests/test_ipalib/test_parameter.py10
2 files changed, 57 insertions, 8 deletions
diff --git a/ipalib/parameter.py b/ipalib/parameter.py
index 2d94138cb..e4268825c 100644
--- a/ipalib/parameter.py
+++ b/ipalib/parameter.py
@@ -183,9 +183,9 @@ class Param(ReadOnly):
"""
# This is a dummy type so that most of the functionality of Param can be
- # unit tested directly without always creating a subclass; however, real
- # (direct) subclasses should *always* override this class attribute:
- type = NoneType # This isn't very useful in the real world!
+ # unit tested directly without always creating a subclass; however, a real
+ # (direct) subclass must *always* override this class attribute:
+ type = NoneType # Ouch, this wont be very useful in the real world!
kwargs = (
('cli_name', str, None),
@@ -201,7 +201,7 @@ class Param(ReadOnly):
# ('default', self.type, None),
)
- def __init__(self, name, **kw):
+ def __init__(self, name, *rules, **kw):
# We keep these values to use in __repr__():
self.param_spec = name
self.__kw = dict(kw)
@@ -233,10 +233,11 @@ class Param(ReadOnly):
if callable(df) and not isinstance(df, DefaultFrom):
kw['default_from'] = DefaultFrom(df)
- # Keep the copy with merged values also to use when cloning:
+ # We keep this copy with merged values also to use when cloning:
self.__clonekw = kw
- # Perform type validation on kw:
+ # Perform type validation on kw, add in class rules:
+ class_rules = []
for (key, kind, default) in self.kwargs:
value = kw.get(key, default)
if value is not None:
@@ -262,7 +263,20 @@ class Param(ReadOnly):
key, self.__class__.__name__)
)
setattr(self, key, value)
+ rule_name = 'rule_%s' % key
+ if value is not None and hasattr(self, rule_name):
+ class_rules.append(getattr(self, rule_name))
check_name(self.cli_name)
+
+ # Check that all the rules are callable
+ self.rules = tuple(class_rules) + rules
+ for rule in self.rules:
+ if not callable(rule):
+ raise TypeError(
+ '%s: rules must be callable; got %r' % (self.nice, rule)
+ )
+
+ # And we're done.
lock(self)
def normalize(self, value):
@@ -401,6 +415,35 @@ class Bytes(Param):
self.nice, self.minlength)
)
+ def rule_minlength(self, value):
+ """
+ Check minlength constraint.
+ """
+ if len(value) < self.minlength:
+ return 'Must be at least %(minlength)d bytes long.' % dict(
+ minlength=self.minlength,
+ )
+
+ def rule_maxlength(self, value):
+ """
+ Check maxlength constraint.
+ """
+ if len(value) > self.maxlength:
+ return 'Can be at most %(maxlength)d bytes long.' % dict(
+ maxlength=self.maxlength,
+ )
+
+ def rule_length(self, value):
+ """
+ Check length constraint.
+ """
+ if len(value) != self.length:
+ return 'Must be exactly %(length)d bytes long.' % dict(
+ length=self.length,
+ )
+
+
+
class Str(Bytes):
"""
diff --git a/tests/test_ipalib/test_parameter.py b/tests/test_ipalib/test_parameter.py
index 855e2cbc0..23b81f283 100644
--- a/tests/test_ipalib/test_parameter.py
+++ b/tests/test_ipalib/test_parameter.py
@@ -92,7 +92,7 @@ def test_parse_param_spec():
assert f('name?') == ('name', dict(required=False, multivalue=False))
assert f('name*') == ('name', dict(required=False, multivalue=True))
assert f('name+') == ('name', dict(required=True, multivalue=True))
- # Make sure other "funny" endings are treated special:
+ # Make sure other "funny" endings are *not* treated special:
assert f('name^') == ('name^', dict(required=True, multivalue=False))
@@ -111,6 +111,9 @@ class test_Param(ClassChecker):
assert o.name is name
assert o.__islocked__() is True
+ # Test default rules:
+ assert o.rules == tuple()
+
# Test default kwarg values:
assert o.cli_name is name
assert o.doc == ''
@@ -118,7 +121,7 @@ class test_Param(ClassChecker):
assert o.multivalue is False
assert o.primary_key is False
assert o.normalizer is None
- #assert o.default is None
+ assert o.default is None
assert o.default_from is None
assert o.flags == frozenset()
@@ -190,6 +193,7 @@ class test_Bytes(ClassChecker):
"""
o = self.cls('my_bytes')
assert o.type is str
+ assert o.rules == tuple()
assert o.minlength is None
assert o.maxlength is None
assert o.length is None
@@ -198,6 +202,7 @@ class test_Bytes(ClassChecker):
# Test mixing length with minlength or maxlength:
o = self.cls('my_bytes', length=5)
assert o.length == 5
+ assert len(o.rules) == 1
permutations = [
dict(minlength=3),
dict(maxlength=7),
@@ -205,6 +210,7 @@ class test_Bytes(ClassChecker):
]
for kw in permutations:
o = self.cls('my_bytes', **kw)
+ assert len(o.rules) == len(kw)
for (key, value) in kw.iteritems():
assert getattr(o, key) == value
e = raises(ValueError, self.cls, 'my_bytes', length=5, **kw)