Author: Armin Rigo <[email protected]>
Branch:
Changeset: r998:246ee2ad7b58
Date: 2012-10-19 17:31 +0200
http://bitbucket.org/cffi/cffi/changeset/246ee2ad7b58/
Log: FILE limited support
diff --git a/c/_cffi_backend.c b/c/_cffi_backend.c
--- a/c/_cffi_backend.c
+++ b/c/_cffi_backend.c
@@ -1695,8 +1695,9 @@
return ct_int;
}
-static PyObject *
-_prepare_pointer_call_argument(CTypeDescrObject *ctptr, PyObject *init)
+static int
+_prepare_pointer_call_argument(CTypeDescrObject *ctptr, PyObject *init,
+ char **output_data)
{
/* 'ctptr' is here a pointer type 'ITEM *'. Accept as argument an
initializer for an array 'ITEM[]'. This includes the case of
@@ -1712,11 +1713,11 @@
We assume that the C code won't modify the 'char *' data. */
if ((ctitem->ct_flags & CT_PRIMITIVE_CHAR) &&
(ctitem->ct_size == sizeof(char))) {
- Py_INCREF(init);
- return init;
+ output_data[0] = PyBytes_AS_STRING(init);
+ return 1;
}
else
- return Py_None;
+ return 0;
}
else if (PyList_Check(init) || PyTuple_Check(init)) {
length = PySequence_Fast_GET_SIZE(init);
@@ -1725,32 +1726,44 @@
/* from a unicode, we add the null terminator */
length = _my_PyUnicode_SizeAsWideChar(init) + 1;
}
+ else if (PyFile_Check(init)) {
+ if (strcmp(ctptr->ct_itemdescr->ct_name, "struct _IO_FILE") != 0) {
+ PyErr_Format(PyExc_TypeError,
+ "FILE object passed to a '%s' argument",
+ ctptr->ct_name);
+ return -1;
+ }
+ output_data[0] = (char *)PyFile_AsFile(init);
+ return 1;
+ }
else {
/* refuse to receive just an integer (and interpret it
as the array size) */
- return Py_None;
+ return 0;
}
if (ctitem->ct_size <= 0)
- return Py_None;
+ return 0;
datasize = length * ctitem->ct_size;
if ((datasize / ctitem->ct_size) != length) {
PyErr_SetString(PyExc_OverflowError,
"array size would overflow a Py_ssize_t");
- return NULL;
+ return -1;
}
result = PyBytes_FromStringAndSize(NULL, datasize);
if (result == NULL)
- return NULL;
+ return -1;
data = PyBytes_AS_STRING(result);
memset(data, 0, datasize);
if (convert_array_from_object(data, ctptr, init) < 0) {
Py_DECREF(result);
- return NULL;
+ return -1;
}
- return result;
+ output_data[0] = data;
+ output_data[1] = (char *)result;
+ return 1;
}
static PyObject*
@@ -1870,20 +1883,19 @@
argtype = (CTypeDescrObject *)PyTuple_GET_ITEM(fvarargs, i);
if (argtype->ct_flags & CT_POINTER) {
- PyObject *string;
+ ((char **)data)[1] = NULL;
+
if (!CData_Check(obj)) {
- string = _prepare_pointer_call_argument(argtype, obj);
- if (string != Py_None) {
- if (string == NULL)
+ int res = _prepare_pointer_call_argument(argtype, obj,
+ (char **)data);
+ if (res != 0) {
+ if (res < 0)
goto error;
- ((char **)data)[0] = PyBytes_AS_STRING(string);
- ((char **)data)[1] = (char *)string;
assert(i < nargs_declared); /* otherwise, obj is a CData */
free_me_until = i + 1;
continue;
}
}
- ((char **)data)[1] = NULL;
}
if (convert_from_object(data, argtype, obj) < 0)
goto error;
@@ -1928,8 +1940,8 @@
argtype = (CTypeDescrObject *)PyTuple_GET_ITEM(signature, 2 + i);
if (argtype->ct_flags & CT_POINTER) {
char *data = buffer + cif_descr->exchange_offset_arg[1 + i];
- PyObject *string_or_null = (PyObject *)(((char **)data)[1]);
- Py_XDECREF(string_or_null);
+ PyObject *tmpobj_or_null = (PyObject *)(((char **)data)[1]);
+ Py_XDECREF(tmpobj_or_null);
}
}
if (buffer)
diff --git a/c/test_c.py b/c/test_c.py
--- a/c/test_c.py
+++ b/c/test_c.py
@@ -2215,3 +2215,55 @@
buffer(p)[:] = bytearray(b"foo\x00")
assert len(p) == 4
assert list(p) == [b"f", b"o", b"o", b"\x00"]
+
+def test_FILE():
+ if sys.platform == "win32":
+ py.test.skip("testing FILE not implemented")
+ #
+ BFILE = new_struct_type("_IO_FILE")
+ BFILEP = new_pointer_type(BFILE)
+ BChar = new_primitive_type("char")
+ BCharP = new_pointer_type(BChar)
+ BInt = new_primitive_type("int")
+ BFunc = new_function_type((BCharP, BFILEP), BInt, False)
+ BFunc2 = new_function_type((BFILEP, BCharP), BInt, True)
+ ll = find_and_load_library('c')
+ fputs = ll.load_function(BFunc, "fputs")
+ fscanf = ll.load_function(BFunc2, "fscanf")
+ #
+ import posix
+ fdr, fdw = posix.pipe()
+ fr1 = posix.fdopen(fdr, 'r')
+ fw1 = posix.fdopen(fdw, 'w')
+ #
+ res = fputs(b"hello world\n", fw1)
+ assert res >= 0
+ fw1.close()
+ #
+ p = newp(new_array_type(BCharP, 100), None)
+ res = fscanf(fr1, b"%s\n", p)
+ assert res == 1
+ assert string(p) == b"hello"
+ fr1.close()
+
+def test_FILE_only_for_FILE_arg():
+ if sys.platform == "win32":
+ py.test.skip("testing FILE not implemented")
+ #
+ B_NOT_FILE = new_struct_type("NOT_FILE")
+ B_NOT_FILEP = new_pointer_type(B_NOT_FILE)
+ BChar = new_primitive_type("char")
+ BCharP = new_pointer_type(BChar)
+ BInt = new_primitive_type("int")
+ BFunc = new_function_type((BCharP, B_NOT_FILEP), BInt, False)
+ ll = find_and_load_library('c')
+ fputs = ll.load_function(BFunc, "fputs")
+ #
+ import posix
+ fdr, fdw = posix.pipe()
+ fr1 = posix.fdopen(fdr, 'r')
+ fw1 = posix.fdopen(fdw, 'w')
+ #
+ e = py.test.raises(TypeError, fputs, b"hello world\n", fw1)
+ assert str(e.value) == ("FILE object passed to a 'struct NOT_FILE *' "
+ "argument")
diff --git a/cffi/api.py b/cffi/api.py
--- a/cffi/api.py
+++ b/cffi/api.py
@@ -59,7 +59,7 @@
if name.startswith('RTLD_'):
setattr(self, name, getattr(backend, name))
#
- lines = []
+ lines = ['typedef struct _IO_FILE FILE;']
by_size = {}
for cname in ['long long', 'long', 'int', 'short', 'char']:
by_size[self.sizeof(cname)] = cname
diff --git a/doc/source/index.rst b/doc/source/index.rst
--- a/doc/source/index.rst
+++ b/doc/source/index.rst
@@ -397,7 +397,12 @@
some headers declare a different type (e.g. an enum) and also call it
``bool``.
+* *New in version 0.4:* FILE. Limited support: just enough to declare C
+ functions taking a ``FILE *`` argument and calling them with a Python
+ file object.
+
.. "versionadded:: 0.4": bool
+.. "versionadded:: 0.4": FILE
As we will see on `the verification step`_ below, the declarations can
also contain "``...``" at various places; these are placeholders that will
diff --git a/testing/test_function.py b/testing/test_function.py
--- a/testing/test_function.py
+++ b/testing/test_function.py
@@ -2,6 +2,7 @@
from cffi import FFI
import math, os, sys
from cffi.backend_ctypes import CTypesBackend
+from testing.udir import udir
try:
from StringIO import StringIO
@@ -295,3 +296,20 @@
m = ffi.dlopen("m")
x = m.sin(1.23)
assert x == math.sin(1.23)
+
+ def test_fputs_custom_FILE(self):
+ if self.Backend is CTypesBackend:
+ py.test.skip("FILE not supported with the ctypes backend")
+ filename = str(udir.join('fputs_custom_FILE'))
+ ffi = FFI(backend=self.Backend())
+ ffi.cdef("int fputs(const char *, FILE *);")
+ C = ffi.dlopen(None)
+ with open(filename, 'wb') as f:
+ f.write(b'[')
+ C.fputs(b"hello from custom file", f)
+ f.write(b'][')
+ C.fputs(b"some more output", f)
+ f.write(b']')
+ with open(filename, 'rb') as f:
+ res = f.read()
+ assert res == b'[hello from custom file][some more output]'
_______________________________________________
pypy-commit mailing list
[email protected]
http://mail.python.org/mailman/listinfo/pypy-commit