summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--ipalib/parameters.py51
-rw-r--r--tests/test_ipalib/test_parameters.py53
2 files changed, 89 insertions, 15 deletions
diff --git a/ipalib/parameters.py b/ipalib/parameters.py
index bfe601a93..da01bfcf6 100644
--- a/ipalib/parameters.py
+++ b/ipalib/parameters.py
@@ -19,6 +19,16 @@
"""
Parameter system for command plugins.
+
+TODO:
+
+ * Change rule call signature to rule(_, value, **kw) so that rules can also
+ validate relative to other parameter values (e.g., login name as it relates
+ to first name and last name)
+
+ * Add the _rule_pattern() methods to `Bytes` and `Str`
+
+ * Add maxvalue, minvalue kwargs and rules to `Int` and `Float`
"""
from types import NoneType
@@ -28,7 +38,6 @@ from plugable import ReadOnly, lock, check_name
from errors2 import ConversionError, RequirementError, ValidationError
from constants import NULLS, TYPE_ERROR, CALLABLE_ERROR
-
class DefaultFrom(ReadOnly):
"""
Derive a default value from other supplied values.
@@ -695,29 +704,23 @@ class Float(Param):
type_error = _('must be a decimal number')
-class Bytes(Param):
+class Data(Param):
"""
- A parameter for binary data (stored in the ``str`` type).
-
- This class is named *Bytes* instead of *Str* so it's aligned with the
- Python v3 ``(str, unicode) => (bytes, str)`` clean-up. See:
+ Base class for the `Bytes` and `Str` parameters.
- http://docs.python.org/3.0/whatsnew/3.0.html
+ Previously `Str` was as subclass of `Bytes`. Now the common functionality
+ has been split into this base class so that ``isinstance(foo, Bytes)`` wont
+ be ``True`` when ``foo`` is actually an `Str` instance (which is confusing).
"""
- type = str
- type_error = _('must be binary data')
-
kwargs = Param.kwargs + (
('minlength', int, None),
('maxlength', int, None),
('length', int, None),
- ('pattern', str, None),
-
)
def __init__(self, name, *rules, **kw):
- super(Bytes, self).__init__(name, *rules, **kw)
+ super(Data, self).__init__(name, *rules, **kw)
if not (
self.length is None or
@@ -749,6 +752,24 @@ class Bytes(Param):
self.nice, self.minlength)
)
+
+class Bytes(Data):
+ """
+ A parameter for binary data (stored in the ``str`` type).
+
+ This class is named *Bytes* instead of *Str* so it's aligned with the
+ Python v3 ``(str, unicode) => (bytes, str)`` clean-up. See:
+
+ http://docs.python.org/3.0/whatsnew/3.0.html
+ """
+
+ type = str
+ type_error = _('must be binary data')
+
+ kwargs = Data.kwargs + (
+ ('pattern', str, None),
+ )
+
def _rule_minlength(self, _, value):
"""
Check minlength constraint.
@@ -780,7 +801,7 @@ class Bytes(Param):
)
-class Str(Bytes):
+class Str(Data):
"""
A parameter for Unicode text (stored in the ``unicode`` type).
@@ -793,7 +814,7 @@ class Str(Bytes):
type = unicode
type_error = _('must be Unicode text')
- kwargs = Bytes.kwargs[:-1] + (
+ kwargs = Data.kwargs + (
('pattern', unicode, None),
)
diff --git a/tests/test_ipalib/test_parameters.py b/tests/test_ipalib/test_parameters.py
index f7ee2cac2..aeaed2a42 100644
--- a/tests/test_ipalib/test_parameters.py
+++ b/tests/test_ipalib/test_parameters.py
@@ -587,6 +587,59 @@ class test_Flag(ClassChecker):
assert orig.clone(default=False).default is False
+class test_Data(ClassChecker):
+ """
+ Test the `ipalib.parameters.Data` class.
+ """
+ _cls = parameters.Data
+
+ def test_init(self):
+ """
+ Test the `ipalib.parameters.Data.__init__` method.
+ """
+ o = self.cls('my_data')
+ assert o.type is NoneType
+ assert o.rules == tuple()
+ assert o.class_rules == tuple()
+ assert o.all_rules == tuple()
+ assert o.minlength is None
+ assert o.maxlength is None
+ assert o.length is None
+ assert not hasattr(o, 'pattern')
+
+ # Test mixing length with minlength or maxlength:
+ o = self.cls('my_data', length=5)
+ assert o.length == 5
+ permutations = [
+ dict(minlength=3),
+ dict(maxlength=7),
+ dict(minlength=3, maxlength=7),
+ ]
+ for kw in permutations:
+ o = self.cls('my_data', **kw)
+ for (key, value) in kw.iteritems():
+ assert getattr(o, key) == value
+ e = raises(ValueError, self.cls, 'my_data', length=5, **kw)
+ assert str(e) == \
+ "Data('my_data'): cannot mix length with minlength or maxlength"
+
+ # Test when minlength or maxlength are less than 1:
+ e = raises(ValueError, self.cls, 'my_data', minlength=0)
+ assert str(e) == "Data('my_data'): minlength must be >= 1; got 0"
+ e = raises(ValueError, self.cls, 'my_data', maxlength=0)
+ assert str(e) == "Data('my_data'): maxlength must be >= 1; got 0"
+
+ # Test when minlength > maxlength:
+ e = raises(ValueError, self.cls, 'my_data', minlength=22, maxlength=15)
+ assert str(e) == \
+ "Data('my_data'): minlength > maxlength (minlength=22, maxlength=15)"
+
+ # Test when minlength == maxlength
+ e = raises(ValueError, self.cls, 'my_data', minlength=7, maxlength=7)
+ assert str(e) == \
+ "Data('my_data'): minlength == maxlength; use length=7 instead"
+
+
class test_Bytes(ClassChecker):
"""
Test the `ipalib.parameters.Bytes` class.