[pypy-commit] cffi enum-as-int: Implementation
arigo
noreply at buildbot.pypy.org
Fri Feb 8 16:35:19 CET 2013
Author: Armin Rigo <arigo at tunes.org>
Branch: enum-as-int
Changeset: r1132:2b7869e88162
Date: 2013-02-08 16:35 +0100
http://bitbucket.org/cffi/cffi/changeset/2b7869e88162/
Log: Implementation
diff --git a/c/_cffi_backend.c b/c/_cffi_backend.c
--- a/c/_cffi_backend.c
+++ b/c/_cffi_backend.c
@@ -823,32 +823,29 @@
return (PyObject *)cd;
}
-static PyObject *convert_enum_string_to_int(CTypeDescrObject *ct, PyObject *ob)
+static PyObject *convert_cdata_to_enum_string(CDataObject *cd, int both)
{
- PyObject *d_value;
- char *p = PyText_AsUTF8(ob);
- if (p == NULL)
+ int value;
+ PyObject *d_key, *d_value;
+ CTypeDescrObject *ct = cd->c_type;
+
+ assert(ct->ct_flags & CT_IS_ENUM);
+ value = (int)read_raw_signed_data(cd->c_data, ct->ct_size);
+ d_key = PyInt_FromLong(value);
+ if (d_key == 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);
- }
+ d_value = PyDict_GetItem(PyTuple_GET_ITEM(ct->ct_stuff, 1), d_key);
+ if (d_value != NULL) {
+ if (both)
+ d_value = PyText_FromFormat("%d: %s", value,
+ PyText_AS_UTF8(d_value));
+ else
+ Py_INCREF(d_value);
+ }
+ else
+ d_value = PyObject_Str(d_key);
+ Py_DECREF(d_key);
return d_value;
}
@@ -886,21 +883,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);
@@ -1189,27 +1172,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;
@@ -1474,14 +1438,10 @@
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)*/
@@ -1489,6 +1449,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_data != NULL) {
@@ -2586,14 +2553,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) {
@@ -4144,6 +4103,10 @@
dict1 = PyDict_New();
if (dict1 == NULL)
goto error;
+ dict2 = PyDict_New();
+ if (dict2 == NULL)
+ goto error;
+
for (i=n; --i >= 0; ) {
long lvalue;
PyObject *value = PyTuple_GET_ITEM(enumvalues, i);
@@ -4177,19 +4140,12 @@
}
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)
- goto error;
- }
-
combined = PyTuple_Pack(2, dict1, dict2);
if (combined == NULL)
goto error;
@@ -4437,7 +4393,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,20 @@
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'
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,17 +1314,16 @@
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):
@@ -1339,13 +1336,17 @@
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_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,27 @@
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))
- #
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/testing/backend_tests.py b/testing/backend_tests.py
--- a/testing/backend_tests.py
+++ b/testing/backend_tests.py
@@ -860,54 +860,52 @@
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>"
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 == -1
+ assert s[0].e == -1
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 = '#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 +1299,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