[Python-checkins] r62498 - in python/trunk: Doc/library/ctypes.rst Lib/ctypes/test/test_frombuffer.py Misc/NEWS Modules/_ctypes/_ctypes.c
thomas.heller
python-checkins at python.org
Fri Apr 25 17:44:17 CEST 2008
Author: thomas.heller
Date: Fri Apr 25 17:44:16 2008
New Revision: 62498
Log:
Add from_buffer and from_buffer_copy class methods to ctypes types.
Added:
python/trunk/Lib/ctypes/test/test_frombuffer.py (contents, props changed)
Modified:
python/trunk/Doc/library/ctypes.rst
python/trunk/Misc/NEWS
python/trunk/Modules/_ctypes/_ctypes.c
Modified: python/trunk/Doc/library/ctypes.rst
==============================================================================
--- python/trunk/Doc/library/ctypes.rst (original)
+++ python/trunk/Doc/library/ctypes.rst Fri Apr 25 17:44:16 2008
@@ -1950,6 +1950,28 @@
exact, they are methods of the :term:`metaclass`):
+ .. method:: _CData.from_buffer(source[, offset])
+
+ This method returns a ctypes instance that shares the buffer of
+ the ``source`` object. The ``source`` object must support the
+ writeable buffer interface. The optional ``offset`` parameter
+ specifies an offset into the source buffer in bytes; the default
+ is zero. If the source buffer is not large enough a ValueError
+ is raised.
+
+ .. versionadded:: 2.6
+
+ .. method:: _CData.from_buffer_copy(source[, offset])
+
+ This method creates a ctypes instance, the buffer is copied from
+ the source object buffer which must be readable. The optional
+ ``offset`` parameter specifies an offset into the source buffer
+ in bytes; the default is zero. If the source buffer is not
+ large enough a ValueError is raised.
+
+ .. versionadded:: 2.6
+
+
.. method:: from_address(address)
This method returns a ctypes type instance using the memory specified by
Added: python/trunk/Lib/ctypes/test/test_frombuffer.py
==============================================================================
--- (empty file)
+++ python/trunk/Lib/ctypes/test/test_frombuffer.py Fri Apr 25 17:44:16 2008
@@ -0,0 +1,81 @@
+from ctypes import *
+import array
+import gc
+import unittest
+
+class X(Structure):
+ _fields_ = [("c_int", c_int)]
+ init_called = False
+ def __init__(self):
+ self._init_called = True
+
+class Test(unittest.TestCase):
+ def test_fom_buffer(self):
+ a = array.array("i", range(16))
+ x = (c_int * 16).from_buffer(a)
+
+ y = X.from_buffer(a)
+ self.assertEqual(y.c_int, a[0])
+ self.failIf(y.init_called)
+
+ self.assertEqual(x[:], a.tolist())
+
+ a[0], a[-1] = 200, -200
+ self.assertEqual(x[:], a.tolist())
+
+ self.assert_(a in x._objects.values())
+
+ self.assertRaises(ValueError,
+ c_int.from_buffer, a, -1)
+
+ expected = x[:]
+ del a; gc.collect(); gc.collect(); gc.collect()
+ self.assertEqual(x[:], expected)
+
+ self.assertRaises(TypeError,
+ (c_char * 16).from_buffer, "a" * 16)
+
+ def test_fom_buffer_with_offset(self):
+ a = array.array("i", range(16))
+ x = (c_int * 15).from_buffer(a, sizeof(c_int))
+
+ self.assertEqual(x[:], a.tolist()[1:])
+ self.assertRaises(ValueError, lambda: (c_int * 16).from_buffer(a, sizeof(c_int)))
+ self.assertRaises(ValueError, lambda: (c_int * 1).from_buffer(a, 16 * sizeof(c_int)))
+
+ def test_from_buffer_copy(self):
+ a = array.array("i", range(16))
+ x = (c_int * 16).from_buffer_copy(a)
+
+ y = X.from_buffer_copy(a)
+ self.assertEqual(y.c_int, a[0])
+ self.failIf(y.init_called)
+
+ self.assertEqual(x[:], range(16))
+
+ a[0], a[-1] = 200, -200
+ self.assertEqual(x[:], range(16))
+
+ self.assertEqual(x._objects, None)
+
+ self.assertRaises(ValueError,
+ c_int.from_buffer, a, -1)
+
+ del a; gc.collect(); gc.collect(); gc.collect()
+ self.assertEqual(x[:], range(16))
+
+ x = (c_char * 16).from_buffer_copy("a" * 16)
+ self.assertEqual(x[:], "a" * 16)
+
+ def test_fom_buffer_copy_with_offset(self):
+ a = array.array("i", range(16))
+ x = (c_int * 15).from_buffer_copy(a, sizeof(c_int))
+
+ self.assertEqual(x[:], a.tolist()[1:])
+ self.assertRaises(ValueError,
+ (c_int * 16).from_buffer_copy, a, sizeof(c_int))
+ self.assertRaises(ValueError,
+ (c_int * 1).from_buffer_copy, a, 16 * sizeof(c_int))
+
+if __name__ == '__main__':
+ unittest.main()
Modified: python/trunk/Misc/NEWS
==============================================================================
--- python/trunk/Misc/NEWS (original)
+++ python/trunk/Misc/NEWS Fri Apr 25 17:44:16 2008
@@ -43,6 +43,9 @@
Library
-------
+- Add from_buffer() and from_buffer_copy() class methods to ctypes
+ data types
+
- Issue #2682: ctypes callback functions no longer contain a cyclic
reference to themselves.
Modified: python/trunk/Modules/_ctypes/_ctypes.c
==============================================================================
--- python/trunk/Modules/_ctypes/_ctypes.c (original)
+++ python/trunk/Modules/_ctypes/_ctypes.c Fri Apr 25 17:44:16 2008
@@ -412,6 +412,115 @@
return CData_AtAddress(type, buf);
}
+static char from_buffer_doc[] =
+"C.from_buffer(object, offset=0) -> C instance\ncreate a C instance from a writeable buffer";
+
+static int
+KeepRef(CDataObject *target, Py_ssize_t index, PyObject *keep);
+
+static PyObject *
+CDataType_from_buffer(PyObject *type, PyObject *args)
+{
+ void *buffer;
+ Py_ssize_t buffer_len;
+ Py_ssize_t offset = 0;
+ PyObject *obj, *result;
+ StgDictObject *dict = PyType_stgdict(type);
+ assert (dict);
+
+ if (!PyArg_ParseTuple(args,
+#if (PY_VERSION_HEX < 0x02050000)
+ "O|i:from_buffer",
+#else
+ "O|n:from_buffer",
+#endif
+ &obj, &offset))
+ return NULL;
+
+ if (-1 == PyObject_AsWriteBuffer(obj, &buffer, &buffer_len))
+ return NULL;
+
+ if (offset < 0) {
+ PyErr_SetString(PyExc_ValueError,
+ "offset cannit be negative");
+ return NULL;
+ }
+ if (dict->size > buffer_len - offset) {
+ PyErr_Format(PyExc_ValueError,
+#if (PY_VERSION_HEX < 0x02050000)
+ "Buffer size too small (%d instead of at least %d bytes)",
+#else
+ "Buffer size too small (%zd instead of at least %zd bytes)",
+#endif
+ buffer_len, dict->size + offset);
+ return NULL;
+ }
+
+ result = CData_AtAddress(type, (char *)buffer + offset);
+ if (result == NULL)
+ return NULL;
+
+ Py_INCREF(obj);
+ if (-1 == KeepRef((CDataObject *)result, -1, obj)) {
+ Py_DECREF(result);
+ return NULL;
+ }
+ return result;
+}
+
+static char from_buffer_copy_doc[] =
+"C.from_buffer_copy(object, offset=0) -> C instance\ncreate a C instance from a readable buffer";
+
+static PyObject *
+GenericCData_new(PyTypeObject *type, PyObject *args, PyObject *kwds);
+
+static PyObject *
+CDataType_from_buffer_copy(PyObject *type, PyObject *args)
+{
+ void *buffer;
+ Py_ssize_t buffer_len;
+ Py_ssize_t offset = 0;
+ PyObject *obj, *result;
+ StgDictObject *dict = PyType_stgdict(type);
+ assert (dict);
+
+ if (!PyArg_ParseTuple(args,
+#if (PY_VERSION_HEX < 0x02050000)
+ "O|i:from_buffer",
+#else
+ "O|n:from_buffer",
+#endif
+ &obj, &offset))
+ return NULL;
+
+ if (-1 == PyObject_AsReadBuffer(obj, &buffer, &buffer_len))
+ return NULL;
+
+ if (offset < 0) {
+ PyErr_SetString(PyExc_ValueError,
+ "offset cannit be negative");
+ return NULL;
+ }
+
+ if (dict->size > buffer_len - offset) {
+ PyErr_Format(PyExc_ValueError,
+#if (PY_VERSION_HEX < 0x02050000)
+ "Buffer size too small (%d instead of at least %d bytes)",
+#else
+ "Buffer size too small (%zd instead of at least %zd bytes)",
+#endif
+ buffer_len, dict->size + offset);
+ return NULL;
+ }
+
+ result = GenericCData_new((PyTypeObject *)type, NULL, NULL);
+ if (result == NULL)
+ return NULL;
+ memcpy(((CDataObject *)result)->b_ptr,
+ (char *)buffer+offset, dict->size);
+ return result;
+}
+
static char in_dll_doc[] =
"C.in_dll(dll, name) -> C instance\naccess a C instance in a dll";
@@ -516,6 +625,8 @@
static PyMethodDef CDataType_methods[] = {
{ "from_param", CDataType_from_param, METH_O, from_param_doc },
{ "from_address", CDataType_from_address, METH_O, from_address_doc },
+ { "from_buffer", CDataType_from_buffer, METH_VARARGS, from_buffer_doc, },
+ { "from_buffer_copy", CDataType_from_buffer_copy, METH_VARARGS, from_buffer_copy_doc, },
{ "in_dll", CDataType_in_dll, METH_VARARGS, in_dll_doc },
{ NULL, NULL },
};
@@ -845,6 +956,8 @@
static PyMethodDef PointerType_methods[] = {
{ "from_address", CDataType_from_address, METH_O, from_address_doc },
+ { "from_buffer", CDataType_from_buffer, METH_VARARGS, from_buffer_doc, },
+ { "from_buffer_copy", CDataType_from_buffer_copy, METH_VARARGS, from_buffer_copy_doc, },
{ "in_dll", CDataType_in_dll, METH_VARARGS, in_dll_doc},
{ "from_param", (PyCFunction)PointerType_from_param, METH_O, from_param_doc},
{ "set_type", (PyCFunction)PointerType_set_type, METH_O },
@@ -1859,6 +1972,8 @@
static PyMethodDef SimpleType_methods[] = {
{ "from_param", SimpleType_from_param, METH_O, from_param_doc },
{ "from_address", CDataType_from_address, METH_O, from_address_doc },
+ { "from_buffer", CDataType_from_buffer, METH_VARARGS, from_buffer_doc, },
+ { "from_buffer_copy", CDataType_from_buffer_copy, METH_VARARGS, from_buffer_copy_doc, },
{ "in_dll", CDataType_in_dll, METH_VARARGS, in_dll_doc},
{ NULL, NULL },
};
More information about the Python-checkins
mailing list