[pypy-commit] cffi default: hg merge enum-as-int
arigo
noreply at buildbot.pypy.org
Tue Feb 12 20:29:39 CET 2013
Author: Armin Rigo <arigo at tunes.org>
Branch:
Changeset: r1151:4aed2f4e2bc1
Date: 2013-02-12 18:26 +0100
http://bitbucket.org/cffi/cffi/changeset/4aed2f4e2bc1/
Log: hg merge enum-as-int
diff --git a/c/_cffi_backend.c b/c/_cffi_backend.c
--- a/c/_cffi_backend.c
+++ b/c/_cffi_backend.c
@@ -824,35 +824,6 @@
return (PyObject *)cd;
}
-static PyObject *convert_enum_string_to_int(CTypeDescrObject *ct, PyObject *ob)
-{
- PyObject *d_value;
- char *p = PyText_AsUTF8(ob);
- if (p == NULL)
- return NULL;
-
- if (p[0] == '#') {
- char *number = p + 1; /* strip initial '#' */
- PyObject *ob2 = PyText_FromString(number);
- if (ob2 == NULL)
- return NULL;
-
- d_value = PyNumber_Long(ob2);
- Py_DECREF(ob2);
- }
- else {
- d_value = PyDict_GetItem(PyTuple_GET_ITEM(ct->ct_stuff, 0), ob);
- if (d_value == NULL) {
- PyErr_Format(PyExc_ValueError,
- "'%s' is not an enumerator for %s",
- p, ct->ct_name);
- return NULL;
- }
- Py_INCREF(d_value);
- }
- return d_value;
-}
-
static CDataObject *_new_casted_primitive(CTypeDescrObject *ct); /*forward*/
static PyObject *
@@ -887,21 +858,7 @@
PY_LONG_LONG value;
/*READ(data, ct->ct_size)*/
value = read_raw_signed_data(data, ct->ct_size);
-
- if (ct->ct_flags & CT_IS_ENUM) {
- PyObject *d_value, *d_key = PyInt_FromLong((int)value);
- if (d_key == NULL)
- return NULL;
-
- d_value = PyDict_GetItem(PyTuple_GET_ITEM(ct->ct_stuff, 1), d_key);
- Py_DECREF(d_key);
- if (d_value != NULL)
- Py_INCREF(d_value);
- else
- d_value = PyText_FromFormat("#%d", (int)value);
- return d_value;
- }
- else if (ct->ct_flags & CT_PRIMITIVE_FITS_LONG)
+ if (ct->ct_flags & CT_PRIMITIVE_FITS_LONG)
return PyInt_FromLong((long)value);
else
return PyLong_FromLongLong(value);
@@ -1190,27 +1147,8 @@
}
if (ct->ct_flags & CT_PRIMITIVE_SIGNED) {
PY_LONG_LONG value = _my_PyLong_AsLongLong(init);
-
- if (value == -1 && PyErr_Occurred()) {
- if (!(ct->ct_flags & CT_IS_ENUM))
- return -1;
- else {
- PyObject *ob;
- PyErr_Clear();
- if (!PyTextAny_Check(init)) {
- expected = "str or int";
- goto cannot_convert;
- }
-
- ob = convert_enum_string_to_int(ct, init);
- if (ob == NULL)
- return -1;
- value = PyLong_AsLongLong(ob);
- Py_DECREF(ob);
- if (value == -1 && PyErr_Occurred())
- return -1;
- }
- }
+ if (value == -1 && PyErr_Occurred())
+ return -1;
write_raw_integer_data(buf, value, ct->ct_size);
if (value != read_raw_signed_data(buf, ct->ct_size))
goto overflow;
@@ -1469,20 +1407,48 @@
static PyObject *cdata_float(CDataObject *cd); /*forward*/
+static PyObject *convert_cdata_to_enum_string(CDataObject *cd, int both)
+{
+ PyObject *d_key, *d_value;
+ CTypeDescrObject *ct = cd->c_type;
+
+ assert(ct->ct_flags & CT_IS_ENUM);
+ d_key = convert_to_object(cd->c_data, ct);
+ if (d_key == NULL)
+ return NULL;
+
+ d_value = PyDict_GetItem(PyTuple_GET_ITEM(ct->ct_stuff, 1), d_key);
+ if (d_value != NULL) {
+ if (both) {
+ PyObject *o = PyObject_Str(d_key);
+ if (o == NULL)
+ d_value = NULL;
+ else {
+ d_value = PyText_FromFormat("%s: %s",
+ PyText_AS_UTF8(o),
+ PyText_AS_UTF8(d_value));
+ Py_DECREF(o);
+ }
+ }
+ else
+ Py_INCREF(d_value);
+ }
+ else
+ d_value = PyObject_Str(d_key);
+ Py_DECREF(d_key);
+ return d_value;
+}
+
static PyObject *cdata_repr(CDataObject *cd)
{
char *extra;
PyObject *result, *s;
if (cd->c_type->ct_flags & CT_PRIMITIVE_ANY) {
- if (!(cd->c_type->ct_flags & CT_IS_LONGDOUBLE)) {
- PyObject *o = convert_to_object(cd->c_data, cd->c_type);
- if (o == NULL)
- return NULL;
- s = PyObject_Repr(o);
- Py_DECREF(o);
+ if (cd->c_type->ct_flags & CT_IS_ENUM) {
+ s = convert_cdata_to_enum_string(cd, 1);
}
- else {
+ else if (cd->c_type->ct_flags & CT_IS_LONGDOUBLE) {
long double lvalue;
char buffer[128]; /* big enough */
/*READ(cd->c_data, sizeof(long double)*/
@@ -1490,6 +1456,13 @@
sprintf(buffer, "%LE", lvalue);
s = PyText_FromString(buffer);
}
+ else {
+ PyObject *o = convert_to_object(cd->c_data, cd->c_type);
+ if (o == NULL)
+ return NULL;
+ s = PyObject_Repr(o);
+ Py_DECREF(o);
+ }
}
else if ((cd->c_type->ct_flags & CT_ARRAY) && cd->c_type->ct_length < 0) {
s = PyText_FromFormat("sliced length %zd", get_array_length(cd));
@@ -2745,14 +2718,6 @@
(CT_POINTER|CT_FUNCTIONPTR|CT_ARRAY)) {
value = (Py_intptr_t)((CDataObject *)ob)->c_data;
}
- else if ((ct->ct_flags & CT_IS_ENUM) && PyTextAny_Check(ob)) {
- ob = convert_enum_string_to_int(ct, ob);
- if (ob == NULL)
- return NULL;
- cd = cast_to_integer_or_char(ct, ob);
- Py_DECREF(ob);
- return cd;
- }
#if PY_MAJOR_VERSION < 3
else if (PyString_Check(ob)) {
if (PyString_GET_SIZE(ob) != 1) {
@@ -4074,8 +4039,7 @@
return -1;
}
}
- if ((ctype->ct_flags & (CT_PRIMITIVE_SIGNED | CT_IS_ENUM))
- == CT_PRIMITIVE_SIGNED) {
+ if (ctype->ct_flags & CT_PRIMITIVE_SIGNED) {
PY_LONG_LONG value;
/* It's probably fine to always zero-extend, but you never
know: maybe some code somewhere expects a negative
@@ -4294,6 +4258,10 @@
CTypeDescrObject *td;
Py_ssize_t i, n;
struct aligncheck_int { char x; int y; };
+ struct aligncheck_long { char x; long y; };
+ long smallest_item = 0;
+ unsigned long largest_item = 0;
+ int size, flags;
if (!PyArg_ParseTuple(args, "sO!O!:new_enum_type",
&ename,
@@ -4311,8 +4279,13 @@
dict1 = PyDict_New();
if (dict1 == NULL)
goto error;
+ dict2 = PyDict_New();
+ if (dict2 == NULL)
+ goto error;
+
for (i=n; --i >= 0; ) {
long lvalue;
+ unsigned long ulvalue;
PyObject *value = PyTuple_GET_ITEM(enumvalues, i);
tmpkey = PyTuple_GET_ITEM(enumerators, i);
Py_INCREF(tmpkey);
@@ -4336,25 +4309,62 @@
}
}
lvalue = PyLong_AsLong(value);
- if ((lvalue == -1 && PyErr_Occurred()) || lvalue != (int)lvalue) {
- PyErr_Format(PyExc_OverflowError,
- "enum '%s' declaration for '%s' does not fit an int",
- ename, PyText_AS_UTF8(tmpkey));
- goto error;
+ if (PyErr_Occurred()) {
+ PyErr_Clear();
+ ulvalue = PyLong_AsUnsignedLong(value);
+ if (PyErr_Occurred()) {
+ PyErr_Format(PyExc_OverflowError,
+ "enum '%s' declaration for '%s' does not fit "
+ "a long or unsigned long",
+ ename, PyText_AS_UTF8(tmpkey));
+ goto error;
+ }
+ if (ulvalue > largest_item)
+ largest_item = ulvalue;
+ }
+ else {
+ if (lvalue < 0) {
+ if (lvalue < smallest_item)
+ smallest_item = lvalue;
+ }
+ else {
+ ulvalue = (unsigned long)lvalue;
+ if (ulvalue > largest_item)
+ largest_item = ulvalue;
+ }
}
if (PyDict_SetItem(dict1, tmpkey, value) < 0)
goto error;
+ if (PyDict_SetItem(dict2, value, tmpkey) < 0)
+ goto error;
Py_DECREF(tmpkey);
tmpkey = NULL;
}
- dict2 = PyDict_New();
- if (dict2 == NULL)
- goto error;
- for (i=n; --i >= 0; ) {
- if (PyDict_SetItem(dict2, PyTuple_GET_ITEM(enumvalues, i),
- PyTuple_GET_ITEM(enumerators, i)) < 0)
+ if (smallest_item < 0) {
+ flags = CT_PRIMITIVE_SIGNED | CT_PRIMITIVE_FITS_LONG | CT_IS_ENUM;
+ if (smallest_item == (int)smallest_item &&
+ largest_item <= (unsigned long)INT_MAX) {
+ size = sizeof(int);
+ }
+ else if (largest_item <= (unsigned long)LONG_MAX) {
+ size = sizeof(long);
+ }
+ else {
+ PyErr_Format(PyExc_OverflowError,
+ "enum '%s' values don't all fit into either 'long' "
+ "or 'unsigned long'", ename);
goto error;
+ }
+ }
+ else if (sizeof(unsigned int) < sizeof(unsigned long) &&
+ largest_item == (unsigned int)largest_item) {
+ flags = CT_PRIMITIVE_UNSIGNED | CT_PRIMITIVE_FITS_LONG | CT_IS_ENUM;
+ size = sizeof(unsigned int);
+ }
+ else {
+ flags = CT_PRIMITIVE_UNSIGNED | CT_IS_ENUM;
+ size = sizeof(unsigned long);
}
combined = PyTuple_Pack(2, dict1, dict2);
@@ -4364,10 +4374,10 @@
Py_CLEAR(dict2);
Py_CLEAR(dict1);
- switch (sizeof(int)) {
+ switch (size) {
case 4: ffitype = &ffi_type_sint32; break;
case 8: ffitype = &ffi_type_sint64; break;
- default: Py_FatalError("'int' is not 4 or 8 bytes");
+ default: Py_FatalError("'int' or 'long' is not 4 or 8 bytes"); return NULL;
}
name_size = strlen("enum ") + strlen(ename) + 1;
@@ -4378,10 +4388,11 @@
memcpy(td->ct_name, "enum ", strlen("enum "));
memcpy(td->ct_name + strlen("enum "), ename, name_size - strlen("enum "));
td->ct_stuff = combined;
- td->ct_size = sizeof(int);
- td->ct_length = offsetof(struct aligncheck_int, y);
+ td->ct_size = size;
+ td->ct_length = size == sizeof(int) ? offsetof(struct aligncheck_int, y)
+ : offsetof(struct aligncheck_long, y);
td->ct_extra = ffitype;
- td->ct_flags = CT_PRIMITIVE_SIGNED | CT_PRIMITIVE_FITS_LONG | CT_IS_ENUM;
+ td->ct_flags = flags;
td->ct_name_position = name_size - 1;
return (PyObject *)td;
@@ -4604,7 +4615,7 @@
#endif
}
else if (cd->c_type->ct_flags & CT_IS_ENUM) {
- return convert_to_object(cd->c_data, cd->c_type);
+ return convert_cdata_to_enum_string(cd, 0);
}
else if (cd->c_type->ct_flags & CT_IS_BOOL) {
/* fall through to TypeError */
diff --git a/c/test_c.py b/c/test_c.py
--- a/c/test_c.py
+++ b/c/test_c.py
@@ -1292,22 +1292,24 @@
def test_cast_to_enum():
BEnum = new_enum_type("foo", ('def', 'c', 'ab'), (0, 1, -20))
e = cast(BEnum, 0)
- assert repr(e) == "<cdata 'enum foo' 'def'>"
+ assert repr(e) == "<cdata 'enum foo' 0: def>"
+ assert repr(cast(BEnum, -42)) == "<cdata 'enum foo' -42>"
+ assert repr(cast(BEnum, -20)) == "<cdata 'enum foo' -20: ab>"
assert string(e) == 'def'
assert string(cast(BEnum, -20)) == 'ab'
- assert string(cast(BEnum, 'c')) == 'c'
- assert int(cast(BEnum, 'c')) == 1
- assert int(cast(BEnum, 'def')) == 0
+ assert int(cast(BEnum, 1)) == 1
+ assert int(cast(BEnum, 0)) == 0
assert int(cast(BEnum, -242 + 2**128)) == -242
- assert string(cast(BEnum, -242 + 2**128)) == '#-242'
- assert string(cast(BEnum, '#-20')) == 'ab'
- assert repr(cast(BEnum, '#-20')) == "<cdata 'enum foo' 'ab'>"
- assert repr(cast(BEnum, '#-21')) == "<cdata 'enum foo' '#-21'>"
+ assert string(cast(BEnum, -242 + 2**128)) == '-242'
+ #
+ BEnum = new_enum_type("bar", ('def', 'c', 'ab'), (0, 1, 20))
+ e = cast(BEnum, -1)
+ assert repr(e) == "<cdata 'enum bar' 4294967295>" # unsigned int
def test_enum_with_non_injective_mapping():
BEnum = new_enum_type("foo", ('ab', 'cd'), (7, 7))
e = cast(BEnum, 7)
- assert repr(e) == "<cdata 'enum foo' 'ab'>"
+ assert repr(e) == "<cdata 'enum foo' 7: ab>"
assert string(e) == 'ab'
def test_enum_in_struct():
@@ -1316,36 +1318,111 @@
BStructPtr = new_pointer_type(BStruct)
complete_struct_or_union(BStruct, [('a1', BEnum, -1)])
p = newp(BStructPtr, [-20])
- assert p.a1 == "ab"
- p = newp(BStructPtr, ["c"])
- assert p.a1 == "c"
+ assert p.a1 == -20
+ p = newp(BStructPtr, [12])
+ assert p.a1 == 12
e = py.test.raises(TypeError, newp, BStructPtr, [None])
- assert "must be a str or int, not NoneType" in str(e.value)
+ assert "an integer is required" in str(e.value)
+ py.test.raises(TypeError, 'p.a1 = "def"')
if sys.version_info < (3,):
- p.a1 = unicode("def")
- assert p.a1 == "def" and type(p.a1) is str
- py.test.raises(UnicodeEncodeError, "p.a1 = unichr(1234)")
BEnum2 = new_enum_type(unicode("foo"), (unicode('abc'),), (5,))
- assert string(cast(BEnum2, unicode('abc'))) == 'abc'
+ assert string(cast(BEnum2, 5)) == 'abc'
+ assert type(string(cast(BEnum2, 5))) is str
def test_enum_overflow():
- for ovf in (2**63, -2**63-1, 2**31, -2**31-1):
- e = py.test.raises(OverflowError, new_enum_type, "foo", ('a', 'b'),
- (5, ovf))
- assert str(e.value) == (
- "enum 'foo' declaration for 'b' does not fit an int")
+ max_uint = 2 ** (size_of_int()*8) - 1
+ max_int = max_uint // 2
+ max_ulong = 2 ** (size_of_long()*8) - 1
+ max_long = max_ulong // 2
+ # 'unsigned int' case
+ e = new_enum_type("foo", ('a', 'b'), (0, 3))
+ assert sizeof(e) == size_of_int()
+ assert int(cast(e, -1)) == max_uint # 'e' is unsigned
+ e = new_enum_type("foo", ('a', 'b'), (0, max_uint))
+ assert sizeof(e) == size_of_int()
+ assert int(cast(e, -1)) == max_uint
+ assert e.elements == {0: 'a', max_uint: 'b'}
+ assert e.relements == {'a': 0, 'b': max_uint}
+ # 'signed int' case
+ e = new_enum_type("foo", ('a', 'b'), (-1, max_int))
+ assert sizeof(e) == size_of_int()
+ assert int(cast(e, -1)) == -1
+ assert e.elements == {-1: 'a', max_int: 'b'}
+ assert e.relements == {'a': -1, 'b': max_int}
+ e = new_enum_type("foo", ('a', 'b'), (-max_int-1, max_int))
+ assert sizeof(e) == size_of_int()
+ assert int(cast(e, -1)) == -1
+ assert e.elements == {-max_int-1: 'a', max_int: 'b'}
+ assert e.relements == {'a': -max_int-1, 'b': max_int}
+ # 'unsigned long' case
+ e = new_enum_type("foo", ('a', 'b'), (0, max_long))
+ assert sizeof(e) == size_of_long()
+ assert int(cast(e, -1)) == max_ulong # 'e' is unsigned
+ e = new_enum_type("foo", ('a', 'b'), (0, max_ulong))
+ assert sizeof(e) == size_of_long()
+ assert int(cast(e, -1)) == max_ulong
+ assert e.elements == {0: 'a', max_ulong: 'b'}
+ assert e.relements == {'a': 0, 'b': max_ulong}
+ # 'signed long' case
+ e = new_enum_type("foo", ('a', 'b'), (-1, max_long))
+ assert sizeof(e) == size_of_long()
+ assert int(cast(e, -1)) == -1
+ assert e.elements == {-1: 'a', max_long: 'b'}
+ assert e.relements == {'a': -1, 'b': max_long}
+ e = new_enum_type("foo", ('a', 'b'), (-max_long-1, max_long))
+ assert sizeof(e) == size_of_long()
+ assert int(cast(e, -1)) == -1
+ assert e.elements == {-max_long-1: 'a', max_long: 'b'}
+ assert e.relements == {'a': -max_long-1, 'b': max_long}
+ # overflow: both negative items and items larger than max_long
+ e = py.test.raises(OverflowError, new_enum_type, "foo", ('a', 'b'),
+ (-1, max_long + 1))
+ assert str(e.value) == (
+ "enum 'foo' values don't all fit into either 'long' "
+ "or 'unsigned long'")
+ # overflow: items smaller than -max_long-1
+ e = py.test.raises(OverflowError, new_enum_type, "foo", ('a', 'b'),
+ (-max_long-2, 5))
+ assert str(e.value) == (
+ "enum 'foo' declaration for 'a' does not fit a long or unsigned long")
+ # overflow: items larger than max_ulong
+ e = py.test.raises(OverflowError, new_enum_type, "foo", ('a', 'b'),
+ (5, max_ulong+1))
+ assert str(e.value) == (
+ "enum 'foo' declaration for 'b' does not fit a long or unsigned long")
def test_callback_returning_enum():
BInt = new_primitive_type("int")
BEnum = new_enum_type("foo", ('def', 'c', 'ab'), (0, 1, -20))
def cb(n):
- return '#%d' % n
+ if n & 1:
+ return cast(BEnum, n)
+ else:
+ return n
BFunc = new_function_type((BInt,), BEnum)
f = callback(BFunc, cb)
- assert f(0) == 'def'
- assert f(1) == 'c'
- assert f(-20) == 'ab'
- assert f(20) == '#20'
+ assert f(0) == 0
+ assert f(1) == 1
+ assert f(-20) == -20
+ assert f(20) == 20
+ assert f(21) == 21
+
+def test_callback_returning_enum_unsigned():
+ BInt = new_primitive_type("int")
+ BEnum = new_enum_type("foo", ('def', 'c', 'ab'), (0, 1, 20))
+ def cb(n):
+ print n
+ if n & 1:
+ return cast(BEnum, n)
+ else:
+ return n
+ BFunc = new_function_type((BInt,), BEnum)
+ f = callback(BFunc, cb)
+ assert f(0) == 0
+ assert f(1) == 1
+ assert f(-21) == 2**32 - 21
+ assert f(20) == 20
+ assert f(21) == 21
def test_callback_returning_char():
BInt = new_primitive_type("int")
diff --git a/cffi/backend_ctypes.py b/cffi/backend_ctypes.py
--- a/cffi/backend_ctypes.py
+++ b/cffi/backend_ctypes.py
@@ -926,49 +926,43 @@
def new_enum_type(self, name, enumerators, enumvalues):
assert isinstance(name, str)
- mapping = dict(zip(enumerators, enumvalues))
reverse_mapping = dict(zip(reversed(enumvalues),
reversed(enumerators)))
- CTypesInt = self.ffi._get_cached_btype(model.PrimitiveType('int'))
- #
- def forward_map(source):
- if not isinstance(source, str):
- return source
- try:
- return mapping[source]
- except KeyError:
- if source.startswith('#'):
- try:
- return int(source[1:])
- except ValueError:
- pass
- raise ValueError("%r is not an enumerator for %r" % (
- source, CTypesEnum))
+ smallest = min(enumvalues or [0])
+ largest = max(enumvalues or [0])
+ if smallest < 0:
+ if largest == ctypes.c_int(largest).value:
+ tp = 'int'
+ elif largest == ctypes.c_long(largest).value:
+ tp = 'long'
+ else:
+ raise OverflowError
+ else:
+ if largest == ctypes.c_uint(largest).value:
+ tp = 'unsigned int'
+ elif largest == ctypes.c_ulong(largest).value:
+ tp = 'unsigned long'
+ else:
+ raise OverflowError
+ CTypesInt = self.ffi._get_cached_btype(model.PrimitiveType(tp))
#
class CTypesEnum(CTypesInt):
__slots__ = []
_reftypename = 'enum %s &' % name
+ def _get_own_repr(self):
+ value = self._value
+ try:
+ return '%d: %s' % (value, reverse_mapping[value])
+ except KeyError:
+ return str(value)
+
def _to_string(self, maxlen):
- return str(CTypesEnum._from_ctypes(self._value))
-
- @classmethod
- def _cast_from(cls, source):
- source = forward_map(source)
- return super(CTypesEnum, cls)._cast_from(source)
-
- @staticmethod
- def _to_ctypes(x):
- x = forward_map(x)
- return CTypesInt._to_ctypes(x)
-
- @staticmethod
- def _from_ctypes(value):
- value = CTypesInt._from_ctypes(value)
+ value = self._value
try:
return reverse_mapping[value]
except KeyError:
- return '#%s' % value
+ return str(value)
#
CTypesEnum._fix_class()
return CTypesEnum
diff --git a/doc/source/index.rst b/doc/source/index.rst
--- a/doc/source/index.rst
+++ b/doc/source/index.rst
@@ -1031,7 +1031,7 @@
-------------------
``ffi.include(other_ffi)``: includes the typedefs, structs, unions and
-enums defined in another FFI instance. Usage is similar to a
+enum types defined in another FFI instance. Usage is similar to a
``#include`` in C, where a part of the program might include types
defined in another part for its own usage. Note that the include()
method has no effect on functions, constants and global variables, which
@@ -1065,8 +1065,9 @@
byte string or unicode string. (Note that in some situation a single
wchar_t may require a Python unicode string of length 2.)
-- If 'cdata' is an enum, returns the value of the enumerator as a
- string, or ``#NUMBER`` if the value is out of range.
+- If 'cdata' is an enum, returns the value of the enumerator as a string.
+ If the value is out of range, it is simply returned as the stringified
+ integer.
``ffi.buffer(cdata, [size])``: return a buffer object that references
@@ -1193,15 +1194,16 @@
length 0, allocating a ``char[]`` of the correct size, and casting
it to a struct pointer)
-* Enum types are always ``int``. GCC supports enums containing
- larger constants (``unsigned int``, or ``long long``) as an extension
- to the C standard, but CFFI does not. Use
- ``typedef <exact type> my_enum;`` and then some ``#define foo <value>``.
-
.. versionadded:: 0.4
Now supported: the common GCC extension of anonymous nested
structs/unions inside structs/unions.
+.. versionadded:: 0.6
+ Enum types follow the GCC rules: they are defined as the first of
+ ``unsigned int``, ``int``, ``unsigned long`` or ``long`` that fits
+ all numeric values. Note that the first choice is unsigned. In CFFI
+ 0.5 and before, it was always ``int``.
+
Debugging dlopen'ed C libraries
-------------------------------
@@ -1254,8 +1256,8 @@
| C type | writing into | reading from |other operations|
+===============+========================+==================+================+
| integers | an integer or anything | a Python int or | int() |
-| | on which int() works | long, depending | |
-| | (but not a float!). | on the type | |
+| and enums | on which int() works | long, depending | |
+| `(*****)` | (but not a float!). | on the type | |
| | Must be within range. | | |
+---------------+------------------------+------------------+----------------+
| ``char`` | a string of length 1 | a string of | int() |
@@ -1313,11 +1315,6 @@
| union | same as struct, but | | read/write |
| | with at most one field | | fields |
+---------------+------------------------+------------------+----------------+
-| enum | an integer, or the enum| the enum value | int() |
-| | value as a string or | as a string, or | |
-| | as ``"#NUMBER"`` | ``"#NUMBER"`` | |
-| | | if out of range | |
-+---------------+------------------------+------------------+----------------+
.. versionchanged:: 0.3
`(*)` Note that when calling a function, as per C, a ``item *`` argument
@@ -1358,6 +1355,15 @@
C. As for slice assignment, it accepts any iterable, including a list
of items or another array-like cdata object, but the length must match.
+.. versionchanged:: 0.6
+ `(*****)` Enums are now handled like ints (unsigned or signed, int or
+ long, like GCC; note that the first choice is unsigned). In previous
+ versions, you would get the enum's value as a string. Now we follow the C
+ convention and treat them as really equivalent to integers. To compare
+ their value symbolically, use code like ``if x.field == lib.FOO``.
+ If you really want to get their value as a string, use
+ ``ffi.string(ffi.cast("the_enum_type", x.field))``.
+
Reference: verifier
-------------------
diff --git a/testing/backend_tests.py b/testing/backend_tests.py
--- a/testing/backend_tests.py
+++ b/testing/backend_tests.py
@@ -860,54 +860,55 @@
def test_enum(self):
ffi = FFI(backend=self.Backend())
ffi.cdef("enum foo { A, B, CC, D };")
- assert int(ffi.cast("enum foo", "A")) == 0
- assert int(ffi.cast("enum foo", "B")) == 1
- assert int(ffi.cast("enum foo", "CC")) == 2
- assert int(ffi.cast("enum foo", "D")) == 3
- ffi.cdef("enum bar { A, B=-2, CC, D };")
- assert int(ffi.cast("enum bar", "A")) == 0
- assert int(ffi.cast("enum bar", "B")) == -2
- assert int(ffi.cast("enum bar", "CC")) == -1
- assert int(ffi.cast("enum bar", "D")) == 0
- assert ffi.cast("enum bar", "B") != ffi.cast("enum bar", "B")
- assert ffi.cast("enum foo", "A") != ffi.cast("enum bar", "A")
- assert ffi.cast("enum bar", "A") != ffi.cast("int", 0)
- assert repr(ffi.cast("enum bar", "CC")) == "<cdata 'enum bar' 'CC'>"
- py.test.raises(ValueError, ffi.cast, "enum bar", "UNKNOWN")
+ assert ffi.string(ffi.cast("enum foo", 0)) == "A"
+ assert ffi.string(ffi.cast("enum foo", 2)) == "CC"
+ assert ffi.string(ffi.cast("enum foo", 3)) == "D"
+ assert ffi.string(ffi.cast("enum foo", 4)) == "4"
+ ffi.cdef("enum bar { A, B=-2, CC, D, E };")
+ assert ffi.string(ffi.cast("enum bar", 0)) == "A"
+ assert ffi.string(ffi.cast("enum bar", -2)) == "B"
+ assert ffi.string(ffi.cast("enum bar", -1)) == "CC"
+ assert ffi.string(ffi.cast("enum bar", 1)) == "E"
+ assert ffi.cast("enum bar", -2) != ffi.cast("enum bar", -2)
+ assert ffi.cast("enum foo", 0) != ffi.cast("enum bar", 0)
+ assert ffi.cast("enum bar", 0) != ffi.cast("int", 0)
+ assert repr(ffi.cast("enum bar", -1)) == "<cdata 'enum bar' -1: CC>"
+ assert repr(ffi.cast("enum foo", -1)) == ( # enums are unsigned, if
+ "<cdata 'enum foo' 4294967295>") # they contain no neg value
ffi.cdef("enum baz { A=0x1000, B=0x2000 };")
- assert int(ffi.cast("enum baz", "A")) == 0x1000
- assert int(ffi.cast("enum baz", "B")) == 0x2000
+ assert ffi.string(ffi.cast("enum baz", 0x1000)) == "A"
+ assert ffi.string(ffi.cast("enum baz", 0x2000)) == "B"
def test_enum_in_struct(self):
ffi = FFI(backend=self.Backend())
ffi.cdef("enum foo { A, B, C, D }; struct bar { enum foo e; };")
s = ffi.new("struct bar *")
s.e = 0
- assert s.e == "A"
- s.e = "D"
- assert s.e == "D"
- assert s[0].e == "D"
- s[0].e = "C"
- assert s.e == "C"
- assert s[0].e == "C"
+ assert s.e == 0
+ s.e = 3
+ assert s.e == 3
+ assert s[0].e == 3
+ s[0].e = 2
+ assert s.e == 2
+ assert s[0].e == 2
s.e = ffi.cast("enum foo", -1)
- assert s.e == '#-1'
- assert s[0].e == '#-1'
+ assert s.e == 4294967295
+ assert s[0].e == 4294967295
s.e = s.e
- py.test.raises(TypeError, "s.e = None")
+ py.test.raises(TypeError, "s.e = 'B'")
+ py.test.raises(TypeError, "s.e = '2'")
+ py.test.raises(TypeError, "s.e = '#2'")
+ py.test.raises(TypeError, "s.e = '#7'")
def test_enum_non_contiguous(self):
ffi = FFI(backend=self.Backend())
ffi.cdef("enum foo { A, B=42, C };")
- assert int(ffi.cast("enum foo", "A")) == 0
- assert int(ffi.cast("enum foo", "B")) == 42
- assert int(ffi.cast("enum foo", "C")) == 43
assert ffi.string(ffi.cast("enum foo", 0)) == "A"
assert ffi.string(ffi.cast("enum foo", 42)) == "B"
assert ffi.string(ffi.cast("enum foo", 43)) == "C"
invalid_value = ffi.cast("enum foo", 2)
assert int(invalid_value) == 2
- assert ffi.string(invalid_value) == "#2"
+ assert ffi.string(invalid_value) == "2"
def test_array_of_struct(self):
ffi = FFI(backend=self.Backend())
@@ -1301,7 +1302,7 @@
def test_enum_with_non_injective_mapping(self):
ffi = FFI(backend=self.Backend())
ffi.cdef("enum e { AA=0, BB=0, CC=0, DD=0 };")
- e = ffi.cast("enum e", 'CC')
+ e = ffi.cast("enum e", 0)
assert ffi.string(e) == "AA" # pick the first one arbitrarily
def test_nested_anonymous_struct(self):
diff --git a/testing/test_unicode_literals.py b/testing/test_unicode_literals.py
--- a/testing/test_unicode_literals.py
+++ b/testing/test_unicode_literals.py
@@ -49,7 +49,7 @@
def test_enum():
ffi = FFI()
ffi.cdef("enum foo_e { AA, BB, CC };") # unicode literal
- x = ffi.cast("enum foo_e", "BB")
+ x = ffi.cast("enum foo_e", 1)
assert int(ffi.cast("int", x)) == 1
def test_dlopen():
diff --git a/testing/test_verify.py b/testing/test_verify.py
--- a/testing/test_verify.py
+++ b/testing/test_verify.py
@@ -558,13 +558,12 @@
ffi.cdef("enum ee { EE1, EE2, EE3, ... \n \t };")
py.test.raises(VerificationMissing, ffi.cast, 'enum ee', 'EE2')
ffi.verify("enum ee { EE1=10, EE2, EE3=-10, EE4 };")
- assert int(ffi.cast('enum ee', 'EE2')) == 11
- assert int(ffi.cast('enum ee', 'EE3')) == -10
- py.test.raises(ValueError, ffi.cast, 'enum ee', '__dotdotdot0__')
+ assert ffi.string(ffi.cast('enum ee', 11)) == "EE2"
+ assert ffi.string(ffi.cast('enum ee', -10)) == "EE3"
#
# try again
ffi.verify("enum ee { EE1=10, EE2, EE3=-10, EE4 };")
- assert int(ffi.cast('enum ee', 'EE2')) == 11
+ assert ffi.string(ffi.cast('enum ee', 11)) == "EE2"
def test_full_enum():
ffi = FFI()
@@ -578,25 +577,35 @@
lib = ffi.verify("enum ee { EE1, EE2, EE3, EE4 };")
assert lib.EE3 == 2
+def test_enum_usage():
+ ffi = FFI()
+ ffi.cdef("enum ee { EE1,EE2 }; typedef struct { enum ee x; } *sp;")
+ lib = ffi.verify("enum ee { EE1,EE2 }; typedef struct { enum ee x; } *sp;")
+ assert lib.EE2 == 1
+ s = ffi.new("sp", [lib.EE2])
+ assert s.x == 1
+ s.x = 17
+ assert s.x == 17
+
def test_nonfull_enum_syntax2():
ffi = FFI()
ffi.cdef("enum ee { EE1, EE2=\t..., EE3 };")
py.test.raises(VerificationMissing, ffi.cast, 'enum ee', 'EE1')
ffi.verify("enum ee { EE1=10, EE2, EE3=-10, EE4 };")
- assert int(ffi.cast('enum ee', 'EE2')) == 11
- assert int(ffi.cast('enum ee', 'EE3')) == -10
+ assert ffi.string(ffi.cast('enum ee', 11)) == 'EE2'
+ assert ffi.string(ffi.cast('enum ee', -10)) == 'EE3'
#
ffi = FFI()
ffi.cdef("enum ee { EE1, EE2=\t... };")
py.test.raises(VerificationMissing, ffi.cast, 'enum ee', 'EE1')
ffi.verify("enum ee { EE1=10, EE2, EE3=-10, EE4 };")
- assert int(ffi.cast('enum ee', 'EE2')) == 11
+ assert ffi.string(ffi.cast('enum ee', 11)) == 'EE2'
#
ffi = FFI()
ffi.cdef("enum ee2 { EE4=..., EE5=..., ... };")
ffi.verify("enum ee2 { EE4=-1234-5, EE5 }; ")
- assert int(ffi.cast('enum ee2', 'EE4')) == -1239
- assert int(ffi.cast('enum ee2', 'EE5')) == -1238
+ assert ffi.string(ffi.cast('enum ee2', -1239)) == 'EE4'
+ assert ffi.string(ffi.cast('enum ee2', -1238)) == 'EE5'
def test_get_set_errno():
ffi = FFI()
@@ -961,7 +970,7 @@
int foo_func(enum foo_e e) { return e; }
""")
assert lib.foo_func(lib.BB) == 2
- assert lib.foo_func("BB") == 2
+ py.test.raises(TypeError, lib.foo_func, "BB")
def test_enum_as_function_result():
ffi = FFI()
@@ -973,7 +982,7 @@
enum foo_e { AA, CC, BB };
enum foo_e foo_func(int x) { return x; }
""")
- assert lib.foo_func(lib.BB) == "BB"
+ assert lib.foo_func(lib.BB) == lib.BB == 2
def test_enum_values():
ffi = FFI()
@@ -1001,7 +1010,7 @@
ffi = FFI()
ffi.cdef("typedef enum { AA, BB, ... } enum1_t;")
lib = ffi.verify("typedef enum { AA, CC, BB } enum1_t;")
- assert ffi.string(ffi.cast("enum1_t", 1)) == '#1'
+ assert ffi.string(ffi.cast("enum1_t", 1)) == '1'
assert ffi.string(ffi.cast("enum1_t", 2)) == 'BB'
assert lib.AA == 0
assert lib.BB == 2
@@ -1016,7 +1025,7 @@
typedef enum { AA, CC, BB } foo_t;
foo_t foo_func(int x) { return x; }
""")
- assert lib.foo_func(lib.BB) == "BB"
+ assert lib.foo_func(lib.BB) == lib.BB == 2
def test_callback_calling_convention():
py.test.skip("later")
@@ -1429,7 +1438,7 @@
ffi2.cdef("int myfunc(enum foo_e);")
lib2 = ffi2.verify("enum foo_e { CC, BB, AA };"
"int myfunc(enum foo_e x) { return (int)x; }")
- res = lib2.myfunc("AA")
+ res = lib2.myfunc(lib2.AA)
assert res == 2
def test_string_to_voidp_arg():
More information about the pypy-commit
mailing list