diff options
author | Jason Gerard DeRose <jderose@redhat.com> | 2008-12-12 04:48:25 -0700 |
---|---|---|
committer | Jason Gerard DeRose <jderose@redhat.com> | 2008-12-12 04:48:25 -0700 |
commit | e05fd7ab03eb05c26e880ae04fbf095198b58027 (patch) | |
tree | b054c789c0d4eb4118d1679cf9ea4b9443326326 | |
parent | 66faffdfb09ee2bcee1b405c78b37e340bc043aa (diff) | |
download | freeipa-e05fd7ab03eb05c26e880ae04fbf095198b58027.tar.gz freeipa-e05fd7ab03eb05c26e880ae04fbf095198b58027.tar.xz freeipa-e05fd7ab03eb05c26e880ae04fbf095198b58027.zip |
New Param: added basic rule logic
-rw-r--r-- | ipalib/parameter.py | 55 | ||||
-rw-r--r-- | tests/test_ipalib/test_parameter.py | 10 |
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) |