diff options
-rw-r--r-- | ipalib/errors.py | 20 | ||||
-rw-r--r-- | ipalib/public.py | 30 | ||||
-rw-r--r-- | ipalib/tests/test_public.py | 36 | ||||
-rw-r--r-- | ipalib/tests/tstutil.py | 9 |
4 files changed, 93 insertions, 2 deletions
diff --git a/ipalib/errors.py b/ipalib/errors.py index b86ffcdb..6b1a898a 100644 --- a/ipalib/errors.py +++ b/ipalib/errors.py @@ -45,6 +45,26 @@ class IPAError(Exception): return self.msg % self.kw +class ValidationError(IPAError): + msg = 'invalid %r value %r: %s' + + def __init__(self, name, value, error): + self.name = name + self.value = value + self.error = error + super(ValidationError, self).__init__(name, value, error) + +class NormalizationError(ValidationError): + def __init__(self, name, value, type): + self.type = type + super(NormalizationError, self).__init__(name, value, + 'not %r' % type + ) + + + +class ValidationRuleError(ValidationError): + msg = '%r is invalid %r: %s' diff --git a/ipalib/public.py b/ipalib/public.py index e6cd278a..5806d0eb 100644 --- a/ipalib/public.py +++ b/ipalib/public.py @@ -24,10 +24,35 @@ and UI all use. import re import plugable +import errors + + +class opt(plugable.ReadOnly): + __public__ = frozenset(( + 'normalize', + 'validate', + 'default', + 'required', + 'type', + )) + + def normalize(self, value): + try: + return self.type(value) + except (TypeError, ValueError): + raise errors.NormalizationError( + self.__class__.__name__, value, self.type + ) + + + + class cmd(plugable.Plugin): __public__ = frozenset(( + 'normalize', + 'autofill', '__call__', 'get_doc', 'opt', @@ -63,7 +88,10 @@ class cmd(plugable.Plugin): opt = property(__get_opt) def __call__(self, *args, **kw): - print repr(self) + (args, kw) = self.normalize(*args, **kw) + (args, kw) = self.autofill(*args, **kw) + self.validate(*args, **kw) + class obj(plugable.Plugin): diff --git a/ipalib/tests/test_public.py b/ipalib/tests/test_public.py index 0985658e..faffd02e 100644 --- a/ipalib/tests/test_public.py +++ b/ipalib/tests/test_public.py @@ -25,6 +25,41 @@ from tstutil import raises, getitem, no_set, no_del, read_only from ipalib import public, plugable, errors +def test_opt(): + cls = public.opt + assert issubclass(cls, plugable.ReadOnly) + + class int_opt(cls): + type = int + + i = int_opt() + + # Test with values that can't be converted: + nope = ( + '7.0' + 'whatever', + object, + None, + ) + for val in nope: + e = raises(errors.NormalizationError, i.normalize, val) + assert isinstance(e, errors.ValidationError) + assert e.name == 'int_opt' + assert e.value == val + assert e.error == "not <type 'int'>" + assert e.type is int + # Test with values that can be converted: + okay = ( + 7, + 7.0, + 7.2, + 7L, + '7', + ' 7 ', + ) + for val in okay: + assert i.normalize(val) == 7 + def test_cmd(): cls = public.cmd assert issubclass(cls, plugable.Plugin) @@ -35,6 +70,7 @@ def test_obj(): assert issubclass(cls, plugable.Plugin) + def test_attr(): cls = public.attr assert issubclass(cls, plugable.Plugin) diff --git a/ipalib/tests/tstutil.py b/ipalib/tests/tstutil.py index 12ca119d..cdac4547 100644 --- a/ipalib/tests/tstutil.py +++ b/ipalib/tests/tstutil.py @@ -43,10 +43,11 @@ def raises(exception, callback, *args, **kw): raised = False try: callback(*args, **kw) - except exception: + except exception, e: raised = True if not raised: raise ExceptionNotRaised(exception) + return e def getitem(obj, key): @@ -83,3 +84,9 @@ def read_only(obj, name, value='some_new_obj'): # Return the attribute return getattr(obj, name) + + +class ClassChecker(object): + + def new(self, *args, **kw): + return self.cls(*args, **kw) |