summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJason Gerard DeRose <jderose@redhat.com>2008-08-27 20:09:19 +0000
committerJason Gerard DeRose <jderose@redhat.com>2008-08-27 20:09:19 +0000
commit6b214cbccf5211078e3c2b3386c036d50c262f94 (patch)
tree4d99773ec2572c95789c5fd0785c868dc6b76583
parent330c17730c056428c70bd4612799cac44306f513 (diff)
downloadfreeipa.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.py103
-rw-r--r--ipalib/tests/test_ipa_types.py110
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