On Mon, 2013-10-07 at 13:22 +0200, Petr Viktorin wrote:
> On 10/04/2013 07:33 PM, Nathaniel McCallum wrote:
> > This patch is preparatory for the OTP CLI patch.
> 
> 
>  > +    def _convert_scalar(self, value, index=None):
>  > +        return Int._convert_scalar(self, value, index=index)
> 
> That won't work. In Python 2 unbound methods (such as 
> Int._validate_scalar) must be passed the correct type as self; passing 
> an IntEnum instance like this will raise a TypeError.
> 
> You'll need to either use multiple inheritance (if you feel the 
> framework isn't complex enough), or make a convert_int function, and 
> then in both Int and IntEnum just call it and handle ValueError.
> 
> For validate_scalar it would probably be best to extend 
> Param._validate_scalar to allow the class to define extra allowed types, 
> and get rid of the reimplementation in Int.

Fixed.


>From 9820b610bc9877e06d5e6cfb40138f37fcc8ab3b Mon Sep 17 00:00:00 2001
From: Nathaniel McCallum <npmccal...@redhat.com>
Date: Mon, 30 Sep 2013 12:45:37 -0400
Subject: [PATCH] Add IntEnum parameter to ipalib

---
 ipalib/__init__.py                      |   2 +-
 ipalib/parameters.py                    | 130 ++++++++++++++++----------------
 ipatests/test_ipalib/test_parameters.py |   2 +-
 3 files changed, 69 insertions(+), 65 deletions(-)

diff --git a/ipalib/__init__.py b/ipalib/__init__.py
index d822ba5956d6afb6ef6d88063f8359926e47016b..ab89ab77ec94603d242e56436021c9b6ed8663cb 100644
--- a/ipalib/__init__.py
+++ b/ipalib/__init__.py
@@ -886,7 +886,7 @@ from frontend import Command, LocalOrRemote, Updater, Advice
 from frontend import Object, Method, Property
 from crud import Create, Retrieve, Update, Delete, Search
 from parameters import DefaultFrom, Bool, Flag, Int, Decimal, Bytes, Str, IA5Str, Password, DNParam, DeprecatedParam
-from parameters import BytesEnum, StrEnum, AccessTime, File
+from parameters import BytesEnum, StrEnum, IntEnum, AccessTime, File
 from errors import SkipPluginModule
 from text import _, ngettext, GettextFactory, NGettextFactory
 
diff --git a/ipalib/parameters.py b/ipalib/parameters.py
index f75057f1e0f87c5bd12a617def69c5c9cd267bd7..945cdedaae21a5c4e95bb7ffd1e96c60c6f08cf5 100644
--- a/ipalib/parameters.py
+++ b/ipalib/parameters.py
@@ -361,7 +361,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, a real
-    # (direct) subclass must *always* override this class attribute:
+    # (direct) subclass must *always* override this class attribute. If multiple
+    # types are permitted, they may be specified in a tuple. In this case, the
+    # canonical type is the first type listed.
     type = NoneType  # Ouch, this wont be very useful in the real world!
 
     # Subclasses should override this with something more specific:
@@ -396,6 +398,16 @@ class Param(ReadOnly):
         # ('default', self.type, None),
     )
 
+    def _get_types(self):
+        if type(self.type) is tuple:
+            return self.type
+
+        if type(self.type) is type:
+            return (self.type,)
+
+        assert False
+
+
     def __init__(self, name, *rules, **kw):
         # We keep these values to use in __repr__():
         self.param_spec = name
@@ -416,7 +428,7 @@ class Param(ReadOnly):
         self.nice = '%s(%r)' % (self.__class__.__name__, self.param_spec)
 
         # Add 'default' to self.kwargs and makes sure no unknown kw were given:
-        assert type(self.type) is type
+        assert not filter(lambda t: not type(t) is type, self._get_types())
         if kw.get('multivalue', True):
             self.kwargs += (('default', tuple, None),)
         else:
@@ -787,7 +799,7 @@ class Param(ReadOnly):
         """
         Convert a single scalar value.
         """
-        if type(value) is self.type:
+        if type(value) in self._get_types():
             return value
         raise ConversionError(name=self.name, index=index,
             error=ugettext(self.type_error),
@@ -821,7 +833,7 @@ class Param(ReadOnly):
             self._validate_scalar(value)
 
     def _validate_scalar(self, value, index=None):
-        if type(value) is not self.type:
+        if type(value) not in self._get_types():
             raise TypeError(
                 TYPE_ERROR % (self.name, self.type, value, type(value))
             )
@@ -924,7 +936,7 @@ class Param(ReadOnly):
                 json_dict[a] = json_serialize(val)
         json_dict['class'] = self.__class__.__name__
         json_dict['name'] = self.name
-        json_dict['type'] = self.type.__name__
+        json_dict['type'] = self._get_types()[0].__name__
         return json_dict
 
 
@@ -947,7 +959,7 @@ class Bool(Param):
         """
         Convert a single scalar value.
         """
-        if type(value) is self.type:
+        if type(value) in self._get_types():
             return value
         if isinstance(value, basestring):
             value = value.lower()
@@ -1014,11 +1026,11 @@ class Number(Param):
         """
         Convert a single scalar value.
         """
-        if type(value) is self.type:
+        if type(value) in self._get_types():
             return value
         if type(value) in (unicode, int, long, float):
             try:
-                return self.type(value)
+                return self._get_types()[0](value)
             except ValueError:
                 pass
         if type(value) in (tuple, list):
@@ -1034,7 +1046,7 @@ class Int(Number):
     A parameter for integer values (stored in the ``int`` type).
     """
 
-    type = int
+    type = (int, long)
     type_error = _('must be an integer')
 
     kwargs = Param.kwargs + (
@@ -1042,6 +1054,21 @@ class Int(Number):
         ('maxvalue', (int, long), int(MAXINT)),
     )
 
+    @staticmethod
+    def convert_int(value):
+        if type(value) in Int.type:
+            return value
+
+        if type(value) is float:
+            return int(value)
+
+        if type(value) is unicode:
+            if u'.' in value:
+                return int(float(value))
+            return int(value, 0)
+
+        raise ValueError()
+
     def __init__(self, name, *rules, **kw):
         super(Int, self).__init__(name, *rules, **kw)
 
@@ -1055,30 +1082,12 @@ class Int(Number):
         """
         Convert a single scalar value.
         """
-        if type(value) in (int, long):
-            return value
-        if type(value) is unicode:
-            # permit floating point strings
-            if value.find(u'.') >= 0:
-                try:
-                    return int(float(value))
-                except ValueError:
-                    pass
-            else:
-                try:
-                    # 2nd arg is radix base, 2nd arg only accepted for strings.
-                    # Zero means determine radix base from prefix (e.g. 0x for hex)
-                    return int(value, 0)
-                except ValueError:
-                    pass
-        if type(value) is float:
-            try:
-                return int(value)
-            except ValueError:
-                pass
-        raise ConversionError(name=self.get_param_name(), index=index,
-            error=ugettext(self.type_error),
-        )
+        try:
+            return Int.convert_int(value)
+        except ValueError:
+            raise ConversionError(name=self.get_param_name(),
+                                  index=index,
+                                  error=ugettext(self.type_error))
 
     def _rule_minvalue(self, _, value):
         """
@@ -1100,31 +1109,6 @@ class Int(Number):
                 maxvalue=self.maxvalue,
             )
 
-    def _validate_scalar(self, value, index=None):
-        """
-        This duplicates _validate_scalar in the Param class with
-        the exception that it allows both int and long types. The
-        min/max rules handle size enforcement.
-        """
-        if type(value) not in (int, long):
-            raise TypeError(
-                TYPE_ERROR % (self.name, self.type, value, type(value))
-            )
-        if index is not None and type(index) is not int:
-            raise TypeError(
-                TYPE_ERROR % ('index', int, index, type(index))
-            )
-        for rule in self.all_rules:
-            error = rule(ugettext, value)
-            if error is not None:
-                raise ValidationError(
-                    name=self.get_param_name(),
-                    value=value,
-                    index=index,
-                    error=error,
-                    rule=rule,
-                )
-
 
 class Decimal(Number):
     """
@@ -1320,7 +1304,7 @@ class Data(Param):
         """
         Check pattern (regex) contraint.
         """
-        assert type(value) is self.type
+        assert type(value) in self._get_types()
         if self.re.match(value) is None:
             if self.re_errmsg:
                 return self.re_errmsg % dict(pattern=self.pattern,)
@@ -1423,10 +1407,10 @@ class Str(Data):
         """
         Convert a single scalar value.
         """
-        if type(value) is self.type:
+        if type(value) in self._get_types():
             return value
         if type(value) in (int, long, float, decimal.Decimal):
-            return self.type(value)
+            return self._get_types()[0](value)
         if type(value) in (tuple, list):
             raise ConversionError(name=self.name, index=index,
             error=ugettext(self.scalar_error))
@@ -1525,7 +1509,7 @@ class Enum(Param):
     def __init__(self, name, *rules, **kw):
         super(Enum, self).__init__(name, *rules, **kw)
         for (i, v) in enumerate(self.values):
-            if type(v) is not self.type:
+            if type(v) not in self._get_types():
                 n = '%s values[%d]' % (self.nice, i)
                 raise TypeError(
                     TYPE_ERROR % (n, self.type, v, type(v))
@@ -1569,6 +1553,26 @@ class StrEnum(Enum):
     type = unicode
 
 
+class IntEnum(Enum):
+    """
+    Enumerable for integer data (stored in the ``int`` type).
+    """
+
+    type = Int.type
+    type_error = Int.type_error
+
+    def _convert_scalar(self, value, index=None):
+        """
+        Convert a single scalar value.
+        """
+        try:
+            return Int.convert_int(value)
+        except ValueError:
+            raise ConversionError(name=self.get_param_name(),
+                                  index=index,
+                                  error=ugettext(self.type_error))
+
+
 class Any(Param):
     """
     A parameter capable of holding values of any type. For internal use only.
@@ -1792,7 +1796,7 @@ class DNParam(Param):
         """
         Convert a single scalar value.
         """
-        if type(value) is self.type:
+        if type(value) in self._get_types():
             return value
 
         try:
diff --git a/ipatests/test_ipalib/test_parameters.py b/ipatests/test_ipalib/test_parameters.py
index 71acfce71b52c24a4bb80b846d9d2f3e3313574b..f58eb935a1aa21bebf8f0d1aed60536da05b5e80 100644
--- a/ipatests/test_ipalib/test_parameters.py
+++ b/ipatests/test_ipalib/test_parameters.py
@@ -1173,7 +1173,7 @@ class test_Int(ClassChecker):
         """
         # Test with no kwargs:
         o = self.cls('my_number')
-        assert o.type is int
+        assert o.type == (int, long)
         assert isinstance(o, parameters.Int)
         assert o.minvalue == int(MININT)
         assert o.maxvalue == int(MAXINT)
-- 
1.8.3.1

_______________________________________________
Freeipa-devel mailing list
Freeipa-devel@redhat.com
https://www.redhat.com/mailman/listinfo/freeipa-devel

Reply via email to