Author: Armin Rigo <ar...@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(): _______________________________________________ pypy-commit mailing list pypy-commit@python.org http://mail.python.org/mailman/listinfo/pypy-commit