[pypy-commit] cffi default: As discussed on the mailing list: str() -> ffi.string()

arigo noreply at buildbot.pypy.org
Fri Aug 3 16:47:45 CEST 2012


Author: Armin Rigo <arigo at tunes.org>
Branch: 
Changeset: r760:6b8dd58deb11
Date: 2012-08-03 16:24 +0200
http://bitbucket.org/cffi/cffi/changeset/6b8dd58deb11/

Log:	As discussed on the mailing list: str() -> ffi.string()

diff --git a/c/_cffi_backend.c b/c/_cffi_backend.c
--- a/c/_cffi_backend.c
+++ b/c/_cffi_backend.c
@@ -1201,89 +1201,6 @@
     return result;
 }
 
-static PyObject *cdata_str(CDataObject *cd)
-{
-    if (cd->c_type->ct_flags & CT_PRIMITIVE_CHAR &&
-        cd->c_type->ct_size == sizeof(char)) {
-        return PyString_FromStringAndSize(cd->c_data, 1);
-    }
-    else if (cd->c_type->ct_itemdescr != NULL &&
-             cd->c_type->ct_itemdescr->ct_flags & CT_PRIMITIVE_CHAR &&
-             cd->c_type->ct_itemdescr->ct_size == sizeof(char)) {
-        Py_ssize_t length;
-
-        if (cd->c_type->ct_flags & CT_ARRAY) {
-            const char *start = cd->c_data;
-            const char *end;
-            length = get_array_length(cd);
-            end = (const char *)memchr(start, 0, length);
-            if (end != NULL)
-                length = end - start;
-        }
-        else {
-            if (cd->c_data == NULL) {
-                PyObject *s = cdata_repr(cd);
-                if (s != NULL) {
-                    PyErr_Format(PyExc_RuntimeError,
-                                 "cannot use str() on %s",
-                                 PyString_AS_STRING(s));
-                    Py_DECREF(s);
-                }
-                return NULL;
-            }
-            length = strlen(cd->c_data);
-        }
-
-        return PyString_FromStringAndSize(cd->c_data, length);
-    }
-    else if (cd->c_type->ct_flags & CT_IS_ENUM)
-        return convert_to_object(cd->c_data, cd->c_type);
-    else
-        return Py_TYPE(cd)->tp_repr((PyObject *)cd);
-}
-
-#ifdef HAVE_WCHAR_H
-static PyObject *cdata_unicode(CDataObject *cd)
-{
-    if (cd->c_type->ct_flags & CT_PRIMITIVE_CHAR &&
-        cd->c_type->ct_size == sizeof(wchar_t)) {
-        return _my_PyUnicode_FromWideChar((wchar_t *)cd->c_data, 1);
-    }
-    else if (cd->c_type->ct_itemdescr != NULL &&
-             cd->c_type->ct_itemdescr->ct_flags & CT_PRIMITIVE_CHAR &&
-             cd->c_type->ct_itemdescr->ct_size == sizeof(wchar_t)) {
-        Py_ssize_t length;
-        const wchar_t *start = (wchar_t *)cd->c_data;
-
-        if (cd->c_type->ct_flags & CT_ARRAY) {
-            const Py_ssize_t lenmax = get_array_length(cd);
-            length = 0;
-            while (length < lenmax && start[length])
-                length++;
-        }
-        else {
-            if (cd->c_data == NULL) {
-                PyObject *s = cdata_repr(cd);
-                if (s != NULL) {
-                    PyErr_Format(PyExc_RuntimeError,
-                                 "cannot use unicode() on %s",
-                                 PyString_AS_STRING(s));
-                    Py_DECREF(s);
-                }
-                return NULL;
-            }
-            length = 0;
-            while (start[length])
-                length++;
-        }
-
-        return _my_PyUnicode_FromWideChar((wchar_t *)cd->c_data, length);
-    }
-    else
-        return Py_TYPE(cd)->tp_repr((PyObject *)cd);
-}
-#endif
-
 static PyObject *cdataowning_repr(CDataObject *cd)
 {
     Py_ssize_t size;
@@ -1951,11 +1868,6 @@
     (objobjargproc)cdata_ass_sub, /*mp_ass_subscript*/
 };
 
-static PyMethodDef CData_methods[] = {
-    {"__unicode__",     (PyCFunction)cdata_unicode,  METH_NOARGS},
-    {NULL,              NULL}           /* sentinel */
-};
-
 static PyTypeObject CData_Type = {
     PyVarObject_HEAD_INIT(NULL, 0)
     "_cffi_backend.CData",
@@ -1972,7 +1884,7 @@
     &CData_as_mapping,                          /* tp_as_mapping */
     (hashfunc)cdata_hash,                       /* tp_hash */
     (ternaryfunc)cdata_call,                    /* tp_call */
-    (reprfunc)cdata_str,                        /* tp_str */
+    0,                                          /* tp_str */
     (getattrofunc)cdata_getattro,               /* tp_getattro */
     (setattrofunc)cdata_setattro,               /* tp_setattro */
     0,                                          /* tp_as_buffer */
@@ -1984,7 +1896,6 @@
     0,                                          /* tp_weaklistoffset */
     (getiterfunc)cdata_iter,                    /* tp_iter */
     0,                                          /* tp_iternext */
-    CData_methods,                              /* tp_methods */
 };
 
 static PyTypeObject CDataOwning_Type = {
@@ -3888,6 +3799,85 @@
     return s;
 }
 
+static PyObject *b_string(PyObject *self, PyObject *args)
+{
+    CDataObject *cd;
+    Py_ssize_t maxlen = -1;
+    if (!PyArg_ParseTuple(args, "O!|n:string",
+                          &CData_Type, &cd, &maxlen))
+        return NULL;
+
+    if (cd->c_type->ct_itemdescr != NULL &&
+        cd->c_type->ct_itemdescr->ct_flags & (CT_PRIMITIVE_CHAR |
+                                              CT_PRIMITIVE_SIGNED |
+                                              CT_PRIMITIVE_UNSIGNED)) {
+        Py_ssize_t length = maxlen;
+        if (cd->c_data == NULL) {
+            PyObject *s = cdata_repr(cd);
+            if (s != NULL) {
+                PyErr_Format(PyExc_RuntimeError,
+                             "cannot use string() on %s",
+                             PyString_AS_STRING(s));
+                Py_DECREF(s);
+            }
+            return NULL;
+        }
+        if (cd->c_type->ct_itemdescr->ct_size == sizeof(char)) {
+            const char *start = cd->c_data;
+            if (length < 0 && cd->c_type->ct_flags & CT_ARRAY) {
+                length = get_array_length(cd);
+            }
+            if (length < 0)
+                length = strlen(start);
+            else {
+                const char *end;
+                end = (const char *)memchr(start, 0, length);
+                if (end != NULL)
+                    length = end - start;
+            }
+            return PyString_FromStringAndSize(start, length);
+        }
+#ifdef HAVE_WCHAR_H
+        else if (cd->c_type->ct_itemdescr->ct_size == sizeof(wchar_t)) {
+            const wchar_t *start = (wchar_t *)cd->c_data;
+            if (length < 0 && cd->c_type->ct_flags & CT_ARRAY) {
+                length = get_array_length(cd);
+            }
+            if (length < 0) {
+                length = 0;
+                while (start[length])
+                    length++;
+            }
+            else {
+                maxlen = length;
+                length = 0;
+                while (length < maxlen && start[length])
+                    length++;
+            }
+            return _my_PyUnicode_FromWideChar(start, length);
+        }
+#endif
+    }
+    else if (cd->c_type->ct_flags & CT_IS_ENUM) {
+        return convert_to_object(cd->c_data, cd->c_type);
+    }
+    else if (cd->c_type->ct_flags & (CT_PRIMITIVE_CHAR |
+                                     CT_PRIMITIVE_SIGNED |
+                                     CT_PRIMITIVE_UNSIGNED)) {
+        if (cd->c_type->ct_size == sizeof(char)) {
+            return PyString_FromStringAndSize(cd->c_data, 1);
+        }
+#ifdef HAVE_WCHAR_H
+        else if (cd->c_type->ct_size == sizeof(wchar_t)) {
+            return _my_PyUnicode_FromWideChar((wchar_t *)cd->c_data, 1);
+        }
+#endif
+    }
+    PyErr_Format(PyExc_TypeError, "string(): unexpected cdata '%s' argument",
+                 cd->c_type->ct_name);
+    return NULL;
+}
+
 static PyObject *b_buffer(PyObject *self, PyObject *args)
 {
     CDataObject *cd;
@@ -4131,6 +4121,7 @@
     {"typeof", b_typeof, METH_O},
     {"offsetof", b_offsetof, METH_VARARGS},
     {"getcname", b_getcname, METH_VARARGS},
+    {"string", b_string, METH_VARARGS},
     {"buffer", b_buffer, METH_VARARGS},
     {"get_errno", b_get_errno, METH_NOARGS},
     {"set_errno", b_set_errno, METH_VARARGS},
diff --git a/c/test_c.py b/c/test_c.py
--- a/c/test_c.py
+++ b/c/test_c.py
@@ -130,7 +130,7 @@
     assert long(cast(p, 'A')) == 65L
     assert type(int(cast(p, 'A'))) is int
     assert type(long(cast(p, 'A'))) is long
-    assert str(cast(p, 'A')) == 'A'
+    assert str(cast(p, 'A')) == repr(cast(p, 'A'))
     assert repr(cast(p, 'A')) == "<cdata 'char' 'A'>"
     assert repr(cast(p, 255)) == r"<cdata 'char' '\xff'>"
     assert repr(cast(p, 0)) == r"<cdata 'char' '\x00'>"
@@ -235,7 +235,9 @@
     assert p[0] == 'A'
     py.test.raises(TypeError, newp, BPtr, 65)
     py.test.raises(TypeError, newp, BPtr, "foo")
-    assert str(cast(BChar, 'A')) == 'A'
+    c = cast(BChar, 'A')
+    assert str(c) == repr(c)
+    assert int(c) == ord('A')
     py.test.raises(TypeError, cast, BChar, 'foo')
 
 def test_reading_pointer_to_pointer():
@@ -295,6 +297,9 @@
     py.test.raises(TypeError, "p[0]")
 
 def test_default_str():
+    BChar = new_primitive_type("char")
+    x = cast(BChar, 42)
+    assert str(x) == repr(x)
     BInt = new_primitive_type("int")
     x = cast(BInt, 42)
     assert str(x) == repr(x)
@@ -320,7 +325,7 @@
     y = cast(BInt, x)
     assert int(y) == 42
     y = cast(new_primitive_type("char"), x)
-    assert str(y) == chr(42)
+    assert int(y) == 42
     y = cast(new_primitive_type("float"), x)
     assert float(y) == 42.0
     #
@@ -461,7 +466,7 @@
     #
     p = new_primitive_type("char")
     n = cast(p, cast(p, "A"))
-    assert str(n) == "A"
+    assert int(n) == ord("A")
 
 def test_new_primitive_from_cdata():
     p = new_primitive_type("int")
@@ -959,14 +964,14 @@
     BEnum = new_enum_type("foo", ('def', 'c', 'ab'), (0, 1, -20))
     e = cast(BEnum, 0)
     assert repr(e) == "<cdata 'enum foo' 'def'>"
-    assert str(e) == 'def'
-    assert str(cast(BEnum, -20)) == 'ab'
-    assert str(cast(BEnum, 'c')) == 'c'
+    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, -242 + 2**128)) == -242
-    assert str(cast(BEnum, -242 + 2**128)) == '#-242'
-    assert str(cast(BEnum, '#-20')) == 'ab'
+    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'>"
 
@@ -1116,11 +1121,12 @@
     BArray1 = new_array_type(new_pointer_type(BChar), 5)
     BArray2 = new_array_type(new_pointer_type(BArray1), 5)
     a = newp(BArray2, ["abc", "de", "ghij"])
-    assert str(a[2]) == "ghij"
+    assert string(a[1]) == "de"
+    assert string(a[2]) == "ghij"
     a[2] = "."
-    assert str(a[2]) == "."
+    assert string(a[2]) == "."
     a[2] = "12345"
-    assert str(a[2]) == "12345"
+    assert string(a[2]) == "12345"
     e = py.test.raises(IndexError, 'a[2] = "123456"')
     assert 'char[5]' in str(e.value)
     assert 'got 6 characters' in str(e.value)
@@ -1213,16 +1219,53 @@
     p2 = newp(new_pointer_type(BFunc), p1)
     assert p2[0] == p1
 
-def test_str():
+def test_string():
     BChar = new_primitive_type("char")
+    assert string(cast(BChar, 42)) == '*'
+    assert string(cast(BChar, 0)) == '\x00'
     BCharP = new_pointer_type(BChar)
     BArray = new_array_type(BCharP, 10)
     a = newp(BArray, "hello")
     assert len(a) == 10
-    assert str(a) == "hello"
+    assert string(a) == "hello"
     p = a + 2
-    assert str(p) == "llo"
-    py.test.raises(RuntimeError, str, cast(BCharP, 0))
+    assert string(p) == "llo"
+    assert string(newp(new_array_type(BCharP, 4), "abcd")) == "abcd"
+    py.test.raises(RuntimeError, string, cast(BCharP, 0))
+    assert string(a, 4) == "hell"
+    assert string(a, 5) == "hello"
+    assert string(a, 6) == "hello"
+
+def test_string_byte():
+    BByte = new_primitive_type("signed char")
+    assert string(cast(BByte, 42)) == '*'
+    assert string(cast(BByte, 0)) == '\x00'
+    BArray = new_array_type(new_pointer_type(BByte), None)
+    a = newp(BArray, [65, 66, 67])
+    assert type(string(a)) is str and string(a) == 'ABC'
+    #
+    BByte = new_primitive_type("unsigned char")
+    assert string(cast(BByte, 42)) == '*'
+    assert string(cast(BByte, 0)) == '\x00'
+    BArray = new_array_type(new_pointer_type(BByte), None)
+    a = newp(BArray, [65, 66, 67])
+    assert type(string(a)) is str and string(a) == 'ABC'
+
+def test_string_wchar():
+    BWChar = new_primitive_type("wchar_t")
+    assert string(cast(BWChar, 42)) == u'*'
+    assert string(cast(BWChar, 0x4253)) == u'\u4253'
+    assert string(cast(BWChar, 0)) == u'\x00'
+    BArray = new_array_type(new_pointer_type(BWChar), None)
+    a = newp(BArray, [u'A', u'B', u'C'])
+    assert type(string(a)) is unicode and string(a) == u'ABC'
+    assert string(a, 10) == u'ABC'
+
+def test_string_typeerror():
+    BShort = new_primitive_type("short")
+    BArray = new_array_type(new_pointer_type(BShort), None)
+    a = newp(BArray, [65, 66, 67])
+    py.test.raises(TypeError, string, a)
 
 def test_bug_convert_to_ptr():
     BChar = new_primitive_type("char")
@@ -1239,12 +1282,12 @@
     BStructPtr = new_pointer_type(BStruct)
     complete_struct_or_union(BStruct, [('a1', BCharArray10, -1)])
     p = newp(BStructPtr, None)
-    assert str(p.a1) == ''
+    assert string(p.a1) == ''
     p.a1 = 'foo'
-    assert str(p.a1) == 'foo'
+    assert string(p.a1) == 'foo'
     assert list(p.a1) == ['f', 'o', 'o'] + ['\x00'] * 7
     p.a1 = ['x', 'y']
-    assert str(p.a1) == 'xyo'
+    assert string(p.a1) == 'xyo'
 
 def test_invalid_function_result_types():
     BFunc = new_function_type((), new_void_type())
@@ -1374,7 +1417,7 @@
     if wchar4:
         x = cast(BWChar, 0x12345)
         assert str(x) == "<cdata 'wchar_t' u'\U00012345'>"
-        assert unicode(x) == u'\U00012345'
+        assert int(x) == 0x12345
     else:
         assert not pyuni4
     #
@@ -1405,20 +1448,20 @@
     BWCharArray = new_array_type(BWCharP, None)
     a = newp(BWCharArray, u'hello \u1234 world')
     assert len(a) == 14   # including the final null
-    assert unicode(a) == u'hello \u1234 world'
+    assert string(a) == u'hello \u1234 world'
     a[13] = u'!'
-    assert unicode(a) == u'hello \u1234 world!'
+    assert string(a) == u'hello \u1234 world!'
     assert str(a) == repr(a)
     assert a[6] == u'\u1234'
     a[6] = u'-'
-    assert unicode(a) == 'hello - world!'
+    assert string(a) == u'hello - world!'
     assert str(a) == repr(a)
     #
     if wchar4:
         u = u'\U00012345\U00012346\U00012347'
         a = newp(BWCharArray, u)
         assert len(a) == 4
-        assert unicode(a) == u
+        assert string(a) == u
         assert len(list(a)) == 4
         expected = [u'\U00012345', u'\U00012346', u'\U00012347', unichr(0)]
         assert list(a) == expected
@@ -1429,17 +1472,17 @@
     w = cast(BWChar, 'a')
     assert repr(w) == "<cdata 'wchar_t' u'a'>"
     assert str(w) == repr(w)
-    assert unicode(w) == u'a'
+    assert string(w) == u'a'
     assert int(w) == ord('a')
     w = cast(BWChar, 0x1234)
     assert repr(w) == "<cdata 'wchar_t' u'\u1234'>"
     assert str(w) == repr(w)
-    assert unicode(w) == u'\u1234'
+    assert string(w) == u'\u1234'
     assert int(w) == 0x1234
     w = cast(BWChar, u'\u8234')
     assert repr(w) == "<cdata 'wchar_t' u'\u8234'>"
     assert str(w) == repr(w)
-    assert unicode(w) == u'\u8234'
+    assert string(w) == u'\u8234'
     assert int(w) == 0x8234
     w = cast(BInt, u'\u1234')
     assert repr(w) == "<cdata 'int' 4660>"
@@ -1447,7 +1490,7 @@
         w = cast(BWChar, u'\U00012345')
         assert repr(w) == "<cdata 'wchar_t' u'\U00012345'>"
         assert str(w) == repr(w)
-        assert unicode(w) == u'\U00012345'
+        assert string(w) == u'\U00012345'
         assert int(w) == 0x12345
         w = cast(BInt, u'\U00012345')
         assert repr(w) == "<cdata 'int' 74565>"
@@ -1457,23 +1500,23 @@
     #
     a = newp(BWCharArray, u'hello - world')
     p = cast(BWCharP, a)
-    assert unicode(p) == u'hello - world'
+    assert string(p) == u'hello - world'
     p[6] = u'\u2345'
-    assert unicode(p) == u'hello \u2345 world'
+    assert string(p) == u'hello \u2345 world'
     #
     s = newp(BStructPtr, [u'\u1234', p])
     assert s.a1 == u'\u1234'
     assert s.a2 == p
     assert str(s.a2) == repr(s.a2)
-    assert unicode(s.a2) == u'hello \u2345 world'
+    assert string(s.a2) == u'hello \u2345 world'
     #
     q = cast(BWCharP, 0)
     assert str(q) == repr(q)
-    py.test.raises(RuntimeError, unicode, q)
+    py.test.raises(RuntimeError, string, q)
     #
     def cb(p):
         assert repr(p).startswith("<cdata 'wchar_t *' 0x")
-        return len(unicode(p))
+        return len(string(p))
     BFunc = new_function_type((BWCharP,), BInt, False)
     f = callback(BFunc, cb, -42)
     assert f(u'a\u1234b') == 3
@@ -1481,9 +1524,9 @@
     if wchar4 and not pyuni4:
         # try out-of-range wchar_t values
         x = cast(BWChar, 1114112)
-        py.test.raises(ValueError, unicode, x)
+        py.test.raises(ValueError, string, x)
         x = cast(BWChar, -1)
-        py.test.raises(ValueError, unicode, x)
+        py.test.raises(ValueError, string, x)
 
 def test_keepalive_struct():
     # exception to the no-keepalive rule: p=newp(BStructPtr) returns a
@@ -1595,12 +1638,12 @@
     assert c[2] == '-'
     assert str(buf) == "hi-there\x00"
     buf[:2] = 'HI'
-    assert str(c) == 'HI-there'
+    assert string(c) == 'HI-there'
     assert buf[:4:2] == 'H-'
     if '__pypy__' not in sys.builtin_module_names:
         # XXX pypy doesn't support the following assignment so far
         buf[:4:2] = 'XY'
-        assert str(c) == 'XIYthere'
+        assert string(c) == 'XIYthere'
 
 def test_getcname():
     BUChar = new_primitive_type("unsigned char")
diff --git a/cffi/api.py b/cffi/api.py
--- a/cffi/api.py
+++ b/cffi/api.py
@@ -180,6 +180,24 @@
             cdecl = self._typeof(cdecl)
         return self._backend.cast(cdecl, source)
 
+    def string(self, cdata, maxlen=-1):
+        """Return a Python string (or unicode string) from the 'cdata'.
+        If 'cdata' is a pointer or array of characters or bytes, returns
+        the null-terminated string.  The returned string extends until
+        the first null character, or at most 'maxlen' characters.  If
+        'cdata' is an array then 'maxlen' defaults to its length.
+
+        If 'cdata' is a pointer or array of wchar_t, returns a unicode
+        string following the same rules.
+
+        If 'cdata' is a single character or byte or a wchar_t, returns
+        it as a string or unicode string.
+
+        If 'cdata' is an enum, returns the value of the enumerator as a
+        string, or "#value" if the value is out of range.
+        """
+        return self._backend.string(cdata, maxlen)
+
     def buffer(self, cdata, size=-1):
         """Return a read-write buffer object that references the raw C data
         pointed to by the given 'cdata'.  The 'cdata' must be a pointer or
diff --git a/cffi/backend_ctypes.py b/cffi/backend_ctypes.py
--- a/cffi/backend_ctypes.py
+++ b/cffi/backend_ctypes.py
@@ -1,4 +1,4 @@
-import ctypes, ctypes.util, operator
+import ctypes, ctypes.util, operator, sys
 from . import model
 
 class CTypesData(object):
@@ -116,6 +116,9 @@
     def __hash__(self):
         return hash(type(self)) ^ hash(self._convert_to_address(None))
 
+    def _to_string(self, maxlen):
+        raise TypeError("string(): %r" % (self,))
+
 
 class CTypesGenericPrimitive(CTypesData):
     __slots__ = []
@@ -314,7 +317,10 @@
         elif name in ('float', 'double'):
             kind = 'float'
         else:
-            kind = 'int'
+            if name in ('signed char', 'unsigned char'):
+                kind = 'byte'
+            else:
+                kind = 'int'
             is_signed = (ctype(-1).value == -1)
         #
         def _cast_source_to_int(source):
@@ -345,7 +351,7 @@
                     return ctype()
                 return ctype(CTypesPrimitive._to_ctypes(init))
 
-            if kind == 'int':
+            if kind == 'int' or kind == 'byte':
                 @classmethod
                 def _cast_from(cls, source):
                     source = _cast_source_to_int(source)
@@ -362,8 +368,6 @@
                     return cls(source)
                 def __int__(self):
                     return ord(self._value)
-                def __str__(self):
-                    return self._value
 
             if kind == 'float':
                 @classmethod
@@ -386,7 +390,7 @@
 
             _cast_to_integer = __int__
 
-            if kind == 'int':
+            if kind == 'int' or kind == 'byte':
                 @staticmethod
                 def _to_ctypes(x):
                     if not isinstance(x, (int, long)):
@@ -428,13 +432,24 @@
             @staticmethod
             def _initialize(blob, init):
                 blob.value = CTypesPrimitive._to_ctypes(init)
+
+            if kind == 'char':
+                def _to_string(self, maxlen):
+                    return self._value
+            if kind == 'byte':
+                def _to_string(self, maxlen):
+                    return chr(self._value & 0xff)
         #
         CTypesPrimitive._fix_class()
         return CTypesPrimitive
 
     def new_pointer_type(self, BItem):
-        if BItem is self.ffi._get_cached_btype(model.PrimitiveType('char')):
+        getbtype = self.ffi._get_cached_btype
+        if BItem is getbtype(model.PrimitiveType('char')):
             kind = 'charp'
+        elif BItem in (getbtype(model.PrimitiveType('signed char')),
+                       getbtype(model.PrimitiveType('unsigned char'))):
+            kind = 'bytep'
         else:
             kind = 'generic'
         #
@@ -483,11 +498,6 @@
                 self._as_ctype_ptr[index] = BItem._to_ctypes(value)
 
             if kind == 'charp':
-                def __str__(self):
-                    n = 0
-                    while self._as_ctype_ptr[n] != '\x00':
-                        n += 1
-                    return ''.join([self._as_ctype_ptr[i] for i in range(n)])
                 @classmethod
                 def _arg_to_ctypes(cls, value):
                     if isinstance(value, str):
@@ -495,6 +505,17 @@
                     else:
                         return super(CTypesPtr, cls)._arg_to_ctypes(value)
 
+            if kind == 'charp' or kind == 'bytep':
+                def _to_string(self, maxlen):
+                    if maxlen < 0:
+                        maxlen = sys.maxint
+                    p = ctypes.cast(self._as_ctype_ptr,
+                                    ctypes.POINTER(ctypes.c_char))
+                    n = 0
+                    while n < maxlen and p[n] != '\x00':
+                        n += 1
+                    return ''.join([p[i] for i in range(n)])
+
             def _get_own_repr(self):
                 if getattr(self, '_own', False):
                     return 'owning %d bytes' % (
@@ -514,8 +535,12 @@
         else:
             brackets = ' &[%d]' % length
         BItem = CTypesPtr._BItem
-        if BItem is self.ffi._get_cached_btype(model.PrimitiveType('char')):
+        getbtype = self.ffi._get_cached_btype
+        if BItem is getbtype(model.PrimitiveType('char')):
             kind = 'char'
+        elif BItem in (getbtype(model.PrimitiveType('signed char')),
+                       getbtype(model.PrimitiveType('unsigned char'))):
+            kind = 'byte'
         else:
             kind = 'generic'
         #
@@ -567,14 +592,16 @@
                     raise IndexError
                 self._blob[index] = BItem._to_ctypes(value)
 
-            if kind == 'char':
-                def __str__(self):
-                    s = ''.join(self._blob)
-                    try:
-                        s = s[:s.index('\x00')]
-                    except ValueError:
-                        pass
-                    return s
+            if kind == 'char' or kind == 'byte':
+                def _to_string(self, maxlen):
+                    if maxlen < 0:
+                        maxlen = len(self._blob)
+                    p = ctypes.cast(self._blob,
+                                    ctypes.POINTER(ctypes.c_char))
+                    n = 0
+                    while n < maxlen and p[n] != '\x00':
+                        n += 1
+                    return ''.join([p[i] for i in range(n)])
 
             def _get_own_repr(self):
                 if getattr(self, '_own', False):
@@ -840,7 +867,7 @@
             __slots__ = []
             _reftypename = 'enum %s &' % name
 
-            def __str__(self):
+            def _to_string(self, maxlen):
                 return str(CTypesEnum._from_ctypes(self._value))
 
             @classmethod
@@ -870,6 +897,9 @@
     def set_errno(self, value):
         ctypes.set_errno(value)
 
+    def string(self, b, maxlen=-1):
+        return b._to_string(maxlen)
+
     def buffer(self, bptr, size=-1):
         # haaaaaaaaaaaack
         call = ctypes.pythonapi.PyBuffer_FromReadWriteMemory
diff --git a/testing/backend_tests.py b/testing/backend_tests.py
--- a/testing/backend_tests.py
+++ b/testing/backend_tests.py
@@ -550,57 +550,64 @@
         assert len(a) == 5
         assert ffi.sizeof(a) == 5 * SIZE_OF_INT
 
-    def test_str_from_char_pointer(self):
+    def test_string_from_char_pointer(self):
         ffi = FFI(backend=self.Backend())
-        assert str(ffi.new("char*", "x")) == "x"
-        assert str(ffi.new("char*", "\x00")) == ""
+        x = ffi.new("char*", "x")
+        assert str(x) == repr(x)
+        assert ffi.string(x) == "x"
+        assert ffi.string(ffi.new("char*", "\x00")) == ""
 
     def test_unicode_from_wchar_pointer(self):
         ffi = FFI(backend=self.Backend())
         self.check_wchar_t(ffi)
-        assert unicode(ffi.new("wchar_t*", u"x")) == u"x"
-        assert unicode(ffi.new("wchar_t*", u"\x00")) == u""
-        x = ffi.new("wchar_t*", u"\x00")
-        assert str(x) == repr(x)
+        x = ffi.new("wchar_t*", u"x")
+        assert unicode(x) == unicode(repr(x))
+        assert ffi.string(x) == u"x"
+        assert ffi.string(ffi.new("wchar_t*", u"\x00")) == u""
 
     def test_string_from_char_array(self):
         ffi = FFI(backend=self.Backend())
-        assert str(ffi.cast("char", "x")) == "x"
         p = ffi.new("char[]", "hello.")
         p[5] = '!'
-        assert str(p) == "hello!"
+        assert ffi.string(p) == "hello!"
         p[6] = '?'
-        assert str(p) == "hello!?"
+        assert ffi.string(p) == "hello!?"
         p[3] = '\x00'
-        assert str(p) == "hel"
+        assert ffi.string(p) == "hel"
+        assert ffi.string(p, 2) == "he"
         py.test.raises(IndexError, "p[7] = 'X'")
         #
         a = ffi.new("char[]", "hello\x00world")
         assert len(a) == 12
         p = ffi.cast("char *", a)
-        assert str(p) == 'hello'
+        assert ffi.string(p) == 'hello'
 
     def test_string_from_wchar_array(self):
         ffi = FFI(backend=self.Backend())
         self.check_wchar_t(ffi)
-        assert unicode(ffi.cast("wchar_t", "x")) == u"x"
-        assert unicode(ffi.cast("wchar_t", u"x")) == u"x"
+        assert ffi.string(ffi.cast("wchar_t", "x")) == u"x"
+        assert ffi.string(ffi.cast("wchar_t", u"x")) == u"x"
         x = ffi.cast("wchar_t", "x")
         assert str(x) == repr(x)
+        assert ffi.string(x) == u"x"
         #
         p = ffi.new("wchar_t[]", u"hello.")
         p[5] = u'!'
-        assert unicode(p) == u"hello!"
+        assert ffi.string(p) == u"hello!"
         p[6] = unichr(1234)
-        assert unicode(p) == u"hello!\u04d2"
+        assert ffi.string(p) == u"hello!\u04d2"
         p[3] = u'\x00'
-        assert unicode(p) == u"hel"
+        assert ffi.string(p) == u"hel"
+        assert ffi.string(p, 123) == u"hel"
         py.test.raises(IndexError, "p[7] = u'X'")
         #
         a = ffi.new("wchar_t[]", u"hello\x00world")
         assert len(a) == 12
         p = ffi.cast("wchar_t *", a)
-        assert unicode(p) == u'hello'
+        assert ffi.string(p) == u'hello'
+        assert ffi.string(p, 123) == u'hello'
+        assert ffi.string(p, 5) == u'hello'
+        assert ffi.string(p, 2) == u'he'
 
     def test_fetch_const_char_p_field(self):
         # 'const' is ignored so far
@@ -609,7 +616,7 @@
         t = ffi.new("const char[]", "testing")
         s = ffi.new("struct foo*", [t])
         assert type(s.name) is not str
-        assert str(s.name) == "testing"
+        assert ffi.string(s.name) == "testing"
         py.test.raises(TypeError, "s.name = None")
         s.name = ffi.NULL
         assert s.name == ffi.NULL
@@ -622,7 +629,7 @@
         t = ffi.new("const wchar_t[]", u"testing")
         s = ffi.new("struct foo*", [t])
         assert type(s.name) not in (str, unicode)
-        assert unicode(s.name) == u"testing"
+        assert ffi.string(s.name) == u"testing"
         s.name = ffi.NULL
         assert s.name == ffi.NULL
 
@@ -818,8 +825,8 @@
         ffi.cdef("struct foo_s { int a, b; };")
         seen = []
         def cb(argv):
-            seen.append(str(argv[0]))
-            seen.append(str(argv[1]))
+            seen.append(ffi.string(argv[0]))
+            seen.append(ffi.string(argv[1]))
         a = ffi.callback("void(*)(char *[])", cb)
         a([ffi.new("char[]", "foobar"), ffi.new("char[]", "baz")])
         assert seen == ["foobar", "baz"]
@@ -835,7 +842,7 @@
         a = ffi.cast("int", 12.9)
         assert int(a) == 12
         a = ffi.cast("char", 66.9 + 256)
-        assert str(a) == "B"
+        assert ffi.string(a) == "B"
         #
         a = ffi.cast("float", ffi.cast("int", 12))
         assert float(a) == 12.0
@@ -846,7 +853,7 @@
         a = ffi.cast("int", ffi.cast("double", 12.9))
         assert int(a) == 12
         a = ffi.cast("char", ffi.cast("double", 66.9 + 256))
-        assert str(a) == "B"
+        assert ffi.string(a) == "B"
 
     def test_enum(self):
         ffi = FFI(backend=self.Backend())
@@ -889,12 +896,12 @@
         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 str(ffi.cast("enum foo", 0)) == "A"
-        assert str(ffi.cast("enum foo", 42)) == "B"
-        assert str(ffi.cast("enum foo", 43)) == "C"
+        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 str(invalid_value) == "#2"
+        assert ffi.string(invalid_value) == "#2"
 
     def test_array_of_struct(self):
         ffi = FFI(backend=self.Backend())
@@ -1221,4 +1228,4 @@
         ffi = FFI(backend=self.Backend())
         ffi.cdef("enum e { AA=0, BB=0, CC=0, DD=0 };")
         e = ffi.cast("enum e", 'CC')
-        assert str(e) == "AA"     # pick the first one arbitrarily
+        assert ffi.string(e) == "AA"     # pick the first one arbitrarily
diff --git a/testing/test_function.py b/testing/test_function.py
--- a/testing/test_function.py
+++ b/testing/test_function.py
@@ -254,7 +254,7 @@
         ffi.C = ffi.dlopen(None)
         p = ffi.new("char[]", "hello world!")
         q = ffi.C.strchr(p, ord('w'))
-        assert str(q) == "world!"
+        assert ffi.string(q) == "world!"
 
     def test_function_with_struct_argument(self):
         if sys.platform == 'win32':
@@ -267,4 +267,4 @@
         ffi.C = ffi.dlopen(None)
         ina = ffi.new("struct in_addr *", [0x04040404])
         a = ffi.C.inet_ntoa(ina[0])
-        assert str(a) == '4.4.4.4'
+        assert ffi.string(a) == '4.4.4.4'
diff --git a/testing/test_verify.py b/testing/test_verify.py
--- a/testing/test_verify.py
+++ b/testing/test_verify.py
@@ -337,7 +337,7 @@
     ffi.cdef("static char *const PP;")
     lib = ffi.verify('static char *const PP = "testing!";\n')
     assert ffi.typeof(lib.PP) == ffi.typeof("char *")
-    assert str(lib.PP) == "testing!"
+    assert ffi.string(lib.PP) == "testing!"
 
 def test_nonfull_enum():
     ffi = FFI()
@@ -633,7 +633,7 @@
     """)
     foochar = ffi.cast("char *(*)(void)", lib.fooptr)
     s = foochar()
-    assert str(s) == "foobar"
+    assert ffi.string(s) == "foobar"
 
 def test_funcptr_as_argument():
     ffi = FFI()


More information about the pypy-commit mailing list