Commits

Armin Rigo committed 3d9c73d

Support cffi's "enum-as-int" branch

Comments (0)

Files changed (4)

pypy/module/_cffi_backend/ctypeenum.py

 from rpython.rtyper.lltypesystem import rffi
 from rpython.rlib.rarithmetic import intmask, r_ulonglong
 from rpython.rlib.objectmodel import keepalive_until_here
+from rpython.rlib.objectmodel import specialize
 
 from pypy.module._cffi_backend.ctypeprim import W_CTypePrimitiveSigned
+from pypy.module._cffi_backend.ctypeprim import W_CTypePrimitiveUnsigned
 from pypy.module._cffi_backend import misc
 
 
-class W_CTypeEnum(W_CTypePrimitiveSigned):
-    _attrs_            = ['enumerators2values', 'enumvalues2erators']
-    _immutable_fields_ = ['enumerators2values', 'enumvalues2erators']
-    kind = "enum"
+class _Mixin_Enum(object):
+    _mixin_ = True
 
-    def __init__(self, space, name, enumerators, enumvalues):
-        from pypy.module._cffi_backend.newtype import alignment
+    def __init__(self, space, name, size, align, enumerators, enumvalues):
         name = "enum " + name
-        size = rffi.sizeof(rffi.INT)
-        align = alignment(rffi.INT)
-        W_CTypePrimitiveSigned.__init__(self, space, size,
-                                        name, len(name), align)
+        self._super.__init__(self, space, size, name, len(name), align)
         self.enumerators2values = {}   # str -> int
         self.enumvalues2erators = {}   # int -> str
         for i in range(len(enumerators)-1, -1, -1):
                 space.setitem(w_dct, space.wrap(enumerator),
                                      space.wrap(enumvalue))
             return w_dct
-        return W_CTypePrimitiveSigned._fget(self, attrchar)
+        return self._super._fget(self, attrchar)
+
+    def extra_repr(self, cdata):
+        value = self._get_value(cdata)
+        try:
+            s = self.enumvalues2erators[value]
+        except KeyError:
+            return str(value)
+        else:
+            return '%s: %s' % (value, s)
 
     def string(self, cdataobj, maxlen):
-        w_result = self.convert_to_object(cdataobj._cdata)
+        value = self._get_value(cdataobj._cdata)
         keepalive_until_here(cdataobj)
-        return w_result
+        try:
+            s = self.enumvalues2erators[value]
+        except KeyError:
+            s = str(value)
+        return self.space.wrap(s)
 
-    def convert_to_object(self, cdata):
-        value = misc.read_raw_long_data(cdata, self.size)
-        try:
-            enumerator = self.enumvalues2erators[value]
-        except KeyError:
-            enumerator = '#%d' % (value,)
-        return self.space.wrap(enumerator)
 
-    def convert_from_object(self, cdata, w_ob):
-        space = self.space
-        try:
-            return W_CTypePrimitiveSigned.convert_from_object(self, cdata,
-                                                              w_ob)
-        except OperationError, e:
-            if not e.match(space, space.w_TypeError):
-                raise
-        if space.isinstance_w(w_ob, space.w_basestring):
-            value = self.convert_enum_string_to_int(space.str_w(w_ob))
-            value = r_ulonglong(value)
-            misc.write_raw_integer_data(cdata, value, self.size)
-        else:
-            raise self._convert_error("str or int", w_ob)
+class W_CTypeEnumSigned(_Mixin_Enum, W_CTypePrimitiveSigned):
+    _attrs_            = ['enumerators2values', 'enumvalues2erators']
+    _immutable_fields_ = ['enumerators2values', 'enumvalues2erators']
+    kind = "enum"
+    _super = W_CTypePrimitiveSigned
 
-    def cast_str(self, w_ob):
-        space = self.space
-        return self.convert_enum_string_to_int(space.str_w(w_ob))
+    def _get_value(self, cdata):
+        # returns a signed long
+        assert self.value_fits_long
+        return misc.read_raw_long_data(cdata, self.size)
 
-    def cast_unicode(self, w_ob):
-        return self.cast_str(w_ob)
 
-    def convert_enum_string_to_int(self, s):
-        space = self.space
-        if s.startswith('#'):
-            try:
-                return int(s[1:])
-            except ValueError:
-                raise OperationError(space.w_ValueError,
-                                     space.wrap("invalid literal after '#'"))
-        else:
-            try:
-                return self.enumerators2values[s]
-            except KeyError:
-                raise operationerrfmt(space.w_ValueError,
-                                      "'%s' is not an enumerator for %s",
-                                      s, self.name)
+class W_CTypeEnumUnsigned(_Mixin_Enum, W_CTypePrimitiveUnsigned):
+    _attrs_            = ['enumerators2values', 'enumvalues2erators']
+    _immutable_fields_ = ['enumerators2values', 'enumvalues2erators']
+    kind = "enum"
+    _super = W_CTypePrimitiveUnsigned
+
+    def _get_value(self, cdata):
+        # returns an unsigned long
+        assert self.value_fits_ulong
+        return misc.read_raw_ulong_data(cdata, self.size)

pypy/module/_cffi_backend/ctypeprim.py

             self.vrangemax = (r_uint(1) << sh) - 1
 
     def int(self, cdata):
-        # enums: really call convert_to_object() just below,
-        # and not the one overridden in W_CTypeEnum.
-        return W_CTypePrimitiveSigned.convert_to_object(self, cdata)
+        return self.convert_to_object(cdata)
 
     def convert_to_object(self, cdata):
         if self.value_fits_long:

pypy/module/_cffi_backend/newtype.py

 from pypy.interpreter.error import OperationError, operationerrfmt
 from pypy.interpreter.gateway import unwrap_spec
 from rpython.rtyper.lltypesystem import lltype, rffi
-from rpython.rlib.rarithmetic import ovfcheck
+from rpython.rlib.rarithmetic import ovfcheck, r_uint
+from rpython.rlib.rarithmetic import most_neg_value_of, most_pos_value_of
 from rpython.rlib.objectmodel import specialize
 
 from pypy.module._cffi_backend import ctypeobj, ctypeprim, ctypeptr, ctypearray
         raise OperationError(space.w_ValueError,
                              space.wrap("tuple args must have the same size"))
     enumerators = [space.str_w(w) for w in enumerators_w]
-    enumvalues = []
+    #
+    smallest_value = 0
+    largest_value = r_uint(0)
+    i = 0
     try:
         for w in enumvalues_w:
-            enumvalues.append(space.c_int_w(w))
+            try:
+                ulvalue = space.uint_w(w)
+            except OperationError, e:
+                if not e.match(space, space.w_ValueError):
+                    raise
+                lvalue = space.int_w(w)
+                if lvalue < smallest_value:
+                    smallest_value = lvalue
+            else:
+                if ulvalue > largest_value:
+                    largest_value = ulvalue
+            i += 1    # 'i' is here for the exception case, see below
     except OperationError, e:
         if not e.match(space, space.w_OverflowError):
             raise
-        i = len(enumvalues)
         raise operationerrfmt(space.w_OverflowError,
-            "enum '%s' declaration for '%s' does not fit an int",
+                              "enum '%s' declaration for '%s' does not fit "
+                              "a long or unsigned long",
                               name, enumerators[i])
-    ctype = ctypeenum.W_CTypeEnum(space, name, enumerators, enumvalues)
+    #
+    if smallest_value < 0:
+        if (smallest_value >= most_neg_value_of(rffi.INT) and
+             largest_value <= r_uint(most_pos_value_of(rffi.INT))):
+            size = rffi.sizeof(rffi.INT)
+            align = alignment(rffi.INT)
+        elif largest_value <= r_uint(most_pos_value_of(rffi.LONG)):
+            size = rffi.sizeof(rffi.LONG)
+            align = alignment(rffi.LONG)
+        else:
+            raise operationerrfmt(space.w_OverflowError,
+                         "enum '%s' values don't all fit into either 'long' "
+                         "or 'unsigned long'", name)
+        enumvalues = [space.int_w(w) for w in enumvalues_w]
+        ctype = ctypeenum.W_CTypeEnumSigned(space, name, size, align,
+                                            enumerators, enumvalues)
+    else:
+        if largest_value <= r_uint(most_pos_value_of(rffi.UINT)):
+            size = rffi.sizeof(rffi.UINT)
+            align = alignment(rffi.UINT)
+        else:
+            size = rffi.sizeof(rffi.ULONG)
+            align = alignment(rffi.ULONG)
+        enumvalues = [space.uint_w(w) for w in enumvalues_w]
+        ctype = ctypeenum.W_CTypeEnumUnsigned(space, name, size, align,
+                                              enumerators, enumvalues)
     return ctype
 
 # ____________________________________________________________

pypy/module/_cffi_backend/test/_backend_test_c.py

     p = newp(BStructPtr, [12])
     assert p.a1 == 12
     e = py.test.raises(TypeError, newp, BStructPtr, [None])
-    assert "an integer is required" in str(e.value)
+    assert ("an integer is required" in str(e.value) or
+        "unsupported operand type for int(): 'NoneType'" in str(e.value)) #PyPy
     py.test.raises(TypeError, 'p.a1 = "def"')
     if sys.version_info < (3,):
         BEnum2 = new_enum_type(unicode("foo"), (unicode('abc'),), (5,))