diff options
author | Jason Gerard DeRose <jderose@redhat.com> | 2008-08-27 20:09:19 +0000 |
---|---|---|
committer | Jason Gerard DeRose <jderose@redhat.com> | 2008-08-27 20:09:19 +0000 |
commit | 6b214cbccf5211078e3c2b3386c036d50c262f94 (patch) | |
tree | 4d99773ec2572c95789c5fd0785c868dc6b76583 | |
parent | 330c17730c056428c70bd4612799cac44306f513 (diff) | |
download | freeipa.git-6b214cbccf5211078e3c2b3386c036d50c262f94.tar.gz freeipa.git-6b214cbccf5211078e3c2b3386c036d50c262f94.tar.xz freeipa.git-6b214cbccf5211078e3c2b3386c036d50c262f94.zip |
202: Started work on type classes in ipa_types module; added corresponding unit tests
-rw-r--r-- | ipalib/ipa_types.py | 103 | ||||
-rw-r--r-- | ipalib/tests/test_ipa_types.py | 110 |
2 files changed, 213 insertions, 0 deletions
diff --git a/ipalib/ipa_types.py b/ipalib/ipa_types.py new file mode 100644 index 00000000..9a34a6b6 --- /dev/null +++ b/ipalib/ipa_types.py @@ -0,0 +1,103 @@ +# Authors: +# Jason Gerard DeRose <jderose@redhat.com> +# +# Copyright (C) 2008 Red Hat +# see file 'COPYING' for use and warranty information +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License as +# published by the Free Software Foundation; version 2 only +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +""" +Type system for coercing and normalizing input values. +""" + +from plugable import ReadOnly, lock +import errors + + +class Type(ReadOnly): + """ + Base class for all IPA types. + """ + + type = None # Override in base class + + def convert(self, value): + return self.type(value) + + def __get_name(self): + """ + Convenience property to return the class name. + """ + return self.__class__.__name__ + name = property(__get_name) + + +class Int(Type): + type = int + + def __init__(self, min_value=None, max_value=None): + args = (min_value, max_value) + for arg in args: + if not (arg is None or type(arg) is int): + raise TypeError('Must be an int or None: %r' % arg) + if None not in args and min_value >= max_value: + raise ValueError( + 'min_value not less than max_value: %r, %r' % ( + min_value, max_value + ) + ) + self.min_value = min_value + self.max_value = max_value + lock(self) + + def validate(self, value): + if type(value) is not self.type: + return 'Must be an integer' + if self.min_value is not None and value < self.min_value: + return 'Cannot be smaller than %d' % self.min_value + if self.max_value is not None and value > self.max_value: + return 'Cannot be larger than %d' % self.max_value + + +def check_min_max(min_name, min_value, max_name, max_value): + assert type(min_name) is str, 'min_name must be an str' + assert type(max_name) is str, 'max_name must be an str' + for (name, value) in [(min_name, min_value), (max_name, max_value)]: + if not (value is None or type(value) is int): + raise TypeError( + '%s must be an int or None, got: %r' % (name, value) + ) +# if None not in (min_value, max_value) and min_value >= max_value: +# raise ValueError( +# 'min_value not less than max_value: %r, %r' % ( +# min_value, max_value +# ) +# ) + + +class Unicode(Type): + def __init__(self, min_length=None, max_length=None, pattern=None): + integers = (min_length, max_length) + for i in integers: + if not (i is None or type(i) is int): + raise TypeError('Must be an int or None: %r' % i) + if None not in integers and min_value >= max_value: + raise ValueError( + 'min_value not less than max_value: %r, %r' % ( + min_value, max_value + ) + ) + self.min_length = min_length + self.max_length = max_length + self.pattern = pattern diff --git a/ipalib/tests/test_ipa_types.py b/ipalib/tests/test_ipa_types.py new file mode 100644 index 00000000..6ecef2a5 --- /dev/null +++ b/ipalib/tests/test_ipa_types.py @@ -0,0 +1,110 @@ +# Authors: +# Jason Gerard DeRose <jderose@redhat.com> +# +# Copyright (C) 2008 Red Hat +# see file 'COPYING' for use and warranty information +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License as +# published by the Free Software Foundation; version 2 only +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +""" +Unit tests for `ipalib.ipa_types` module. +""" + +from tstutil import raises, getitem, no_set, no_del, read_only, ClassChecker +from ipalib import ipa_types, errors, plugable + + +def test_check_min_max(): + """ + Tests the `ipa_types.check_min_max` function. + """ + f = ipa_types.check_min_max + fail_type = [ + '10', + 10.0, + 10L, + True, + False, + object, + ] + for value in fail_type: + e = raises(TypeError, f, 'low', value, 'high', None) + assert str(e) == 'low must be an int or None, got: %r' % value + e = raises(TypeError, f, 'low', None, 'high', value) + assert str(e) == 'high must be an int or None, got: %r' % value + + + +class test_Type(ClassChecker): + """ + Tests the `ipa_types.Type` class. + """ + _cls = ipa_types.Type + + def test_class(self): + assert self.cls.__bases__ == (plugable.ReadOnly,) + + + +class test_Int(ClassChecker): + _cls = ipa_types.Int + + def test_init(self): + o = self.cls() + assert o.min_value is None + assert o.max_value is None + okay = [ + (None, -5), + (-20, None), + (-20, -5), + ] + fail_type = [ + (None, 10L), + (5L, None), + (None, '10'), + ('5', None), + ] + fail_value = [ + (10, 5), + (5, -5), + (-5, -10), + ] + for (l, h) in okay: + o = self.cls(min_value=l, max_value=h) + assert o.min_value is l + assert o.max_value is h + for (l, h) in fail_type: + raises(TypeError, self.cls, min_value=l, max_value=h) + for (l, h) in fail_value: + raises(ValueError, self.cls, min_value=l, max_value=h) + + def test_validate(self): + o = self.cls(min_value=2, max_value=7) + assert o.validate(2) is None + assert o.validate(5) is None + assert o.validate(7) is None + assert o.validate(1) == 'Cannot be smaller than 2' + assert o.validate(8) == 'Cannot be larger than 7' + for val in ['5', 5.0, 5L, None, True, False, object]: + assert o.validate(val) == 'Must be an integer' + + +class test_Unicode(ClassChecker): + _cls = ipa_types.Unicode + + def test_init(self): + o = self.cls() + assert o.min_length is None + assert o.max_length is None + assert o.pattern is None |