diff -r d565310e7ae3 Doc/library/ctypes.rst --- a/Doc/library/ctypes.rst Sat Nov 30 23:15:38 2013 +0200 +++ b/Doc/library/ctypes.rst Sun Dec 01 12:44:04 2013 +0200 @@ -202,9 +202,9 @@ debugging crashes (e.g. from segmentation faults produced by erroneous C library calls). -``None``, integers, bytes objects and (unicode) strings are the only native +``None``, integers, bytes objects, bytearrays and (unicode) strings are the only native Python objects that can directly be used as parameters in these function calls. -``None`` is passed as a C ``NULL`` pointer, bytes objects and strings are passed +``None`` is passed as a C ``NULL`` pointer, bytes objects, bytearrays and strings are passed as pointer to the memory block that contains their data (:c:type:`char *` or :c:type:`wchar_t *`). Python integers are passed as the platforms default C :c:type:`int` type, their value is masked to fit into the C type. @@ -226,6 +226,7 @@ | :class:`c_bool` | :c:type:`_Bool` | bool (1) | +----------------------+------------------------------------------+----------------------------+ | :class:`c_char` | :c:type:`char` | 1-character bytes object | +| | | or bytearray | +----------------------+------------------------------------------+----------------------------+ | :class:`c_wchar` | :c:type:`wchar_t` | 1-character string | +----------------------+------------------------------------------+----------------------------+ @@ -261,7 +262,8 @@ +----------------------+------------------------------------------+----------------------------+ | :class:`c_longdouble`| :c:type:`long double` | float | +----------------------+------------------------------------------+----------------------------+ -| :class:`c_char_p` | :c:type:`char *` (NUL terminated) | bytes object or ``None`` | +| :class:`c_char_p` | :c:type:`char *` (NUL terminated) | bytes object, bytearray or | +| | | ``None`` | +----------------------+------------------------------------------+----------------------------+ | :class:`c_wchar_p` | :c:type:`wchar_t *` (NUL terminated) | string or ``None`` | +----------------------+------------------------------------------+----------------------------+ @@ -366,9 +368,9 @@ ArgumentError: argument 2: exceptions.TypeError: Don't know how to convert parameter 2 >>> -As has been mentioned before, all Python types except integers, strings, and -bytes objects have to be wrapped in their corresponding :mod:`ctypes` type, so -that they can be converted to the required C data type:: +As has been mentioned before, all Python types except integers, strings, +bytes objects, and bytearrays have to be wrapped in their corresponding +:mod:`ctypes` type, so that they can be converted to the required C data type:: >>> printf(b"An int %d, a double %f\n", 1234, c_double(3.14)) An int 1234, a double 3.140000 @@ -384,7 +386,7 @@ You can also customize :mod:`ctypes` argument conversion to allow instances of your own classes be used as function arguments. :mod:`ctypes` looks for an :attr:`_as_parameter_` attribute and uses this as the function argument. Of -course, it must be one of integer, string, or bytes:: +course, it must be one of integer, string, bytes, or bytearray:: >>> class Bottles: ... def __init__(self, number): @@ -439,7 +441,7 @@ whatever is needed to make sure this object is acceptable, and then return the object itself, its :attr:`_as_parameter_` attribute, or whatever you want to pass as the C function argument in this case. Again, the result should be an -integer, string, bytes, a :mod:`ctypes` instance, or an object with an +integer, string, bytes, bytarray, a :mod:`ctypes` instance, or an object with an :attr:`_as_parameter_` attribute. @@ -1769,12 +1771,13 @@ ctypes array of :class:`c_char`. *init_or_size* must be an integer which specifies the size of the array, or a - bytes object which will be used to initialize the array items. - - If a bytes object is specified as first argument, the buffer is made one item - larger than its length so that the last element in the array is a NUL - termination character. An integer can be passed as second argument which allows - to specify the size of the array if the length of the bytes should not be used. + bytes object or a bytearray which will be used to initialize the array items. + + If a bytes object or a bytearray is specified as first argument, the buffer + is made one item larger than its length so that the last element in the + array is a NUL termination character. An integer can be passed as second + argument which allows to specify the size of the array if the length of the + bytes should not be used. @@ -2038,8 +2041,8 @@ This attribute contains the actual value of the instance. For integer and pointer types, it is an integer, for character types, it is a single - character bytes object or string, for character pointer types it is a - Python bytes object or string. + character bytes object, bytearray or string, for character pointer types + it is a Python bytes object, bytearray or string. When the ``value`` attribute is retrieved from a ctypes instance, usually a new object is returned each time. :mod:`ctypes` does *not* implement @@ -2081,7 +2084,7 @@ Represents the C :c:type:`char *` datatype when it points to a zero-terminated string. For a general character pointer that may also point to binary data, ``POINTER(c_char)`` must be used. The constructor accepts an integer - address, or a bytes object. + address, a bytes object, or a bytearray. .. class:: c_double diff -r d565310e7ae3 Lib/ctypes/__init__.py --- a/Lib/ctypes/__init__.py Sat Nov 30 23:15:38 2013 +0200 +++ b/Lib/ctypes/__init__.py Sun Dec 01 12:44:04 2013 +0200 @@ -49,7 +49,7 @@ create_string_buffer(anInteger) -> character array create_string_buffer(aString, anInteger) -> character array """ - if isinstance(init, (str, bytes)): + if isinstance(init, (str, bytes, bytearray)): if size is None: size = len(init)+1 buftype = c_char * size @@ -284,7 +284,7 @@ create_unicode_buffer(anInteger) -> character array create_unicode_buffer(aString, anInteger) -> character array """ - if isinstance(init, (str, bytes)): + if isinstance(init, (str, bytes, bytearray)): if size is None: size = len(init)+1 buftype = c_wchar * size diff -r d565310e7ae3 Lib/ctypes/test/test_bytes.py --- a/Lib/ctypes/test/test_bytes.py Sat Nov 30 23:15:38 2013 +0200 +++ b/Lib/ctypes/test/test_bytes.py Sun Dec 01 12:44:04 2013 +0200 @@ -3,33 +3,16 @@ import sys from ctypes import * -class BytesTest(unittest.TestCase): - def test_c_char(self): - x = c_char(b"x") - x.value = b"y" - c_char.from_param(b"x") - (c_char * 3)(b"a", b"b", b"c") - +class WcharTest(unittest.TestCase): def test_c_wchar(self): x = c_wchar("x") x.value = "y" c_wchar.from_param("x") (c_wchar * 3)("a", "b", "c") - def test_c_char_p(self): - c_char_p(b"foo bar") - def test_c_wchar_p(self): c_wchar_p("foo bar") - def test_struct(self): - class X(Structure): - _fields_ = [("a", c_char * 3)] - - x = X(b"abc") - self.assertEqual(x.a, b"abc") - self.assertEqual(type(x.a), bytes) - def test_struct_W(self): class X(Structure): _fields_ = [("a", c_wchar * 3)] @@ -38,13 +21,41 @@ self.assertEqual(x.a, "abc") self.assertEqual(type(x.a), str) - if sys.platform == "win32": - def test_BSTR(self): - from _ctypes import _SimpleCData - class BSTR(_SimpleCData): - _type_ = "X" + @unittest.skipUnless(sys.platform == "win32", "Win32 specific test") + def test_BSTR(self): + from _ctypes import _SimpleCData + class BSTR(_SimpleCData): + _type_ = "X" - BSTR("abc") + BSTR("abc") + + +class AbstractBytesTest: + def test_c_char(self): + x = c_char(self.bytes_type(b"x")) + x.value = self.bytes_type(b"y") + c_char.from_param(self.bytes_type(b"x")) + (c_char * 3)(b"a", self.bytes_type(b"b"), self.bytes_type(b"c")) + + def test_c_char_p(self): + c_char_p(self.bytes_type(b"foo bar")) + + def test_struct(self): + class X(Structure): + _fields_ = [("a", c_char * 3)] + + x = X(self.bytes_type(b"abc")) + self.assertEqual(x.a, self.bytes_type(b"abc")) + self.assertEqual(type(x.a), bytes) + + +class BytesTest(AbstractBytesTest, unittest.TestCase): + bytes_type = bytes + + +class ByteArrayTest(AbstractBytesTest, unittest.TestCase): + bytes_type = bytearray + if __name__ == '__main__': unittest.main() diff -r d565310e7ae3 Modules/_ctypes/_ctypes.c --- a/Modules/_ctypes/_ctypes.c Sat Nov 30 23:15:38 2013 +0200 +++ b/Modules/_ctypes/_ctypes.c Sun Dec 01 12:44:04 2013 +0200 @@ -1075,14 +1075,22 @@ return -1; } - if (!PyBytes_Check(value)) { + if (PyBytes_Check(value)) { + Py_INCREF(value); + size = PyBytes_GET_SIZE(value); + ptr = PyBytes_AS_STRING(value); + } + else if (PyByteArray_Check(value)) { + Py_INCREF(value); + size = PyByteArray_GET_SIZE(value); + ptr = PyByteArray_AS_STRING(value); + } + else { PyErr_Format(PyExc_TypeError, "bytes expected instead of %s instance", Py_TYPE(value)->tp_name); return -1; - } else - Py_INCREF(value); - size = PyBytes_GET_SIZE(value); + } if (size > self->b_size) { PyErr_SetString(PyExc_ValueError, "string too long"); @@ -1090,7 +1098,6 @@ return -1; } - ptr = PyBytes_AS_STRING(value); memcpy(self->b_ptr, ptr, size); if (size < self->b_size) self->b_ptr[size] = '\0'; @@ -1431,7 +1438,7 @@ Py_INCREF(Py_None); return Py_None; } - if (PyUnicode_Check(value) || PyBytes_Check(value)) { + if (PyUnicode_Check(value) || PyBytes_Check(value) || PyByteArray_Check(value)) { PyCArgObject *parg; struct fielddesc *fd = _ctypes_get_fielddesc("Z"); @@ -1496,7 +1503,7 @@ Py_INCREF(Py_None); return Py_None; } - if (PyBytes_Check(value)) { + if (PyBytes_Check(value) || PyByteArray_Check(value)) { PyCArgObject *parg; struct fielddesc *fd = _ctypes_get_fielddesc("z"); @@ -1583,25 +1590,8 @@ return (PyObject *)parg; } /* XXX struni: remove later */ -/* string */ - if (PyBytes_Check(value)) { - PyCArgObject *parg; - struct fielddesc *fd = _ctypes_get_fielddesc("z"); - - parg = PyCArgObject_new(); - if (parg == NULL) - return NULL; - parg->pffi_type = &ffi_type_pointer; - parg->tag = 'z'; - parg->obj = fd->setfunc(&parg->value, value, 0); - if (parg->obj == NULL) { - Py_DECREF(parg); - return NULL; - } - return (PyObject *)parg; - } /* bytes */ - if (PyByteArray_Check(value)) { + if (PyBytes_Check(value) || PyByteArray_Check(value)) { PyCArgObject *parg; struct fielddesc *fd = _ctypes_get_fielddesc("z"); diff -r d565310e7ae3 Modules/_ctypes/callproc.c --- a/Modules/_ctypes/callproc.c Sat Nov 30 23:15:38 2013 +0200 +++ b/Modules/_ctypes/callproc.c Sun Dec 01 12:44:04 2013 +0200 @@ -667,6 +667,14 @@ return 0; } + if (PyByteArray_Check(obj)) { + pa->ffi_type = &ffi_type_pointer; + pa->value.p = PyByteArray_AsString(obj); + Py_INCREF(obj); + pa->keep = obj; + return 0; + } + #ifdef CTYPES_UNICODE if (PyUnicode_Check(obj)) { pa->ffi_type = &ffi_type_pointer; diff -r d565310e7ae3 Modules/_ctypes/cfield.c --- a/Modules/_ctypes/cfield.c Sat Nov 30 23:15:38 2013 +0200 +++ b/Modules/_ctypes/cfield.c Sun Dec 01 12:44:04 2013 +0200 @@ -1293,6 +1293,10 @@ if(PyBytes_Check(value)) { Py_INCREF(value); + data = PyBytes_AS_STRING(value); + } else if(PyByteArray_Check(value)) { + Py_INCREF(value); + data = PyByteArray_AS_STRING(value); } else { PyErr_Format(PyExc_TypeError, "expected string, %s found", @@ -1300,7 +1304,6 @@ return NULL; } - data = PyBytes_AS_STRING(value); if (!data) return NULL; size = strlen(data); /* XXX Why not Py_SIZE(value)? */ @@ -1335,7 +1338,13 @@ *(char **)ptr = PyBytes_AsString(value); Py_INCREF(value); return value; - } else if (PyLong_Check(value)) { + } + if (PyByteArray_Check(value)) { + *(char **)ptr = PyByteArray_AsString(value); + Py_INCREF(value); + return value; + } + if (PyLong_Check(value)) { #if SIZEOF_VOID_P == SIZEOF_LONG_LONG *(char **)ptr = (char *)PyLong_AsUnsignedLongLongMask(value); #else