[pypy-commit] pypy default: Attach the FILE to the Python file object from CFFI. This lets the FILE
arigo
noreply at buildbot.pypy.org
Fri Oct 26 16:32:40 CEST 2012
Author: Armin Rigo <arigo at tunes.org>
Branch:
Changeset: r58462:1f2e3adb4ec0
Date: 2012-10-26 15:35 +0200
http://bitbucket.org/pypy/pypy/changeset/1f2e3adb4ec0/
Log: Attach the FILE to the Python file object from CFFI. This lets the
FILE pointer stay valid for C code after the call to the function,
and implement support for 'new("FILE **", f)'.
diff --git a/pypy/module/_cffi_backend/ctypefunc.py b/pypy/module/_cffi_backend/ctypefunc.py
--- a/pypy/module/_cffi_backend/ctypefunc.py
+++ b/pypy/module/_cffi_backend/ctypefunc.py
@@ -4,7 +4,6 @@
import sys
from pypy.interpreter.error import OperationError, operationerrfmt
-from pypy.interpreter.error import wrap_oserror
from pypy.rpython.lltypesystem import lltype, llmemory, rffi
from pypy.rlib import jit, clibffi, jit_libffi, rposix
from pypy.rlib.jit_libffi import CIF_DESCRIPTION, CIF_DESCRIPTION_P
@@ -152,9 +151,6 @@
if flag == 1:
raw_string = rffi.cast(rffi.CCHARPP, data)[0]
lltype.free(raw_string, flavor='raw')
- elif flag == 2:
- file = rffi.cast(rffi.CCHARPP, data)[0]
- rffi_fclose(file)
lltype.free(buffer, flavor='raw')
return w_res
@@ -169,27 +165,6 @@
assert isinstance(abi, int)
return space.wrap(abi)
-rffi_fdopen = rffi.llexternal("fdopen", [rffi.INT, rffi.CCHARP], rffi.CCHARP)
-rffi_fclose = rffi.llexternal("fclose", [rffi.CCHARP], rffi.INT)
-
-def prepare_file_call_argument(fileobj):
- import os
- space = fileobj.space
- fileobj.direct_flush()
- fd = fileobj.direct_fileno()
- if fd < 0:
- raise OperationError(space.w_ValueError,
- space.wrap("file has no OS file descriptor"))
- try:
- fd2 = os.dup(fd)
- f = rffi_fdopen(fd2, fileobj.mode)
- if not f:
- os.close(fd2)
- raise OSError(rposix.get_errno(), "fdopen failed")
- except OSError, e:
- raise wrap_oserror(space, e)
- return f
-
# ____________________________________________________________
diff --git a/pypy/module/_cffi_backend/ctypeptr.py b/pypy/module/_cffi_backend/ctypeptr.py
--- a/pypy/module/_cffi_backend/ctypeptr.py
+++ b/pypy/module/_cffi_backend/ctypeptr.py
@@ -3,6 +3,7 @@
"""
from pypy.interpreter.error import OperationError, operationerrfmt
+from pypy.interpreter.error import wrap_oserror
from pypy.rpython.lltypesystem import lltype, rffi
from pypy.rlib.objectmodel import keepalive_until_here
from pypy.rlib.rarithmetic import ovfcheck
@@ -147,7 +148,15 @@
class W_CTypePtrBase(W_CTypePtrOrArray):
# base class for both pointers and pointers-to-functions
- _attrs_ = []
+ _attrs_ = ['is_file']
+ _immutable_fields_ = ['is_file']
+
+ def __init__(self, space, size, extra, extra_position, ctitem,
+ could_cast_anything=True, is_file=False):
+ W_CTypePtrOrArray.__init__(self, space, size,
+ extra, extra_position, ctitem,
+ could_cast_anything=could_cast_anything)
+ self.is_file = is_file
def convert_to_object(self, cdata):
ptrdata = rffi.cast(rffi.CCHARPP, cdata)[0]
@@ -157,6 +166,11 @@
space = self.space
ob = space.interpclass_w(w_ob)
if not isinstance(ob, cdataobj.W_CData):
+ if self.is_file:
+ result = self.prepare_file(w_ob)
+ if result:
+ rffi.cast(rffi.CCHARPP, cdata)[0] = result
+ return
raise self._convert_error("cdata pointer", w_ob)
other = ob.ctype
if not isinstance(other, W_CTypePtrBase):
@@ -171,14 +185,23 @@
rffi.cast(rffi.CCHARPP, cdata)[0] = ob._cdata
+ def prepare_file(self, w_ob):
+ from pypy.module._file.interp_file import W_File
+ from pypy.module._cffi_backend import ctypefunc
+ ob = self.space.interpclass_w(w_ob)
+ if isinstance(ob, W_File):
+ return prepare_file_argument(self.space, ob)
+ else:
+ return lltype.nullptr(rffi.CCHARP.TO)
+
def _alignof(self):
from pypy.module._cffi_backend import newtype
return newtype.alignment_of_pointer
class W_CTypePointer(W_CTypePtrBase):
- _attrs_ = ['is_file']
- _immutable_fields_ = ['is_file']
+ _attrs_ = []
+ _immutable_fields_ = []
def __init__(self, space, ctitem):
from pypy.module._cffi_backend import ctypearray
@@ -187,8 +210,9 @@
extra = "(*)" # obscure case: see test_array_add
else:
extra = " *"
- self.is_file = (ctitem.name == "struct _IO_FILE")
- W_CTypePtrBase.__init__(self, space, size, extra, 2, ctitem)
+ is_file = (ctitem.name == "struct _IO_FILE")
+ W_CTypePtrBase.__init__(self, space, size, extra, 2, ctitem,
+ is_file=is_file)
def newp(self, w_init):
space = self.space
@@ -245,11 +269,8 @@
# from a string, we add the null terminator
length = space.int_w(space.len(w_init)) + 1
elif self.is_file:
- from pypy.module._file.interp_file import W_File
- from pypy.module._cffi_backend import ctypefunc
- ob = space.interpclass_w(w_init)
- if isinstance(ob, W_File):
- result = ctypefunc.prepare_file_call_argument(ob)
+ result = self.prepare_file(w_init)
+ if result:
rffi.cast(rffi.CCHARPP, cdata)[0] = result
return 2
return 0
@@ -303,3 +324,31 @@
else:
raise OperationError(space.w_TypeError,
space.wrap("expected a 'cdata struct-or-union' object"))
+
+# ____________________________________________________________
+
+
+rffi_fdopen = rffi.llexternal("fdopen", [rffi.INT, rffi.CCHARP], rffi.CCHARP)
+rffi_fclose = rffi.llexternal("fclose", [rffi.CCHARP], rffi.INT)
+
+class CffiFileObj(object):
+ _immutable_ = True
+ def __init__(self, fd, mode):
+ self.llf = rffi_fdopen(fd, mode)
+ if not self.llf:
+ raise OSError(rposix.get_errno(), "fdopen failed")
+ def close(self):
+ rffi_fclose(self.llf)
+
+def prepare_file_argument(space, fileobj):
+ fileobj.direct_flush()
+ if fileobj.cffi_fileobj is None:
+ fd = fileobj.direct_fileno()
+ if fd < 0:
+ raise OperationError(space.w_ValueError,
+ space.wrap("file has no OS file descriptor"))
+ try:
+ fileobj.cffi_fileobj = CffiFileObj(fd, fileobj.mode)
+ except OSError, e:
+ raise wrap_oserror(space, e)
+ return fileobj.cffi_fileobj.llf
diff --git a/pypy/module/_cffi_backend/test/_backend_test_c.py b/pypy/module/_cffi_backend/test/_backend_test_c.py
--- a/pypy/module/_cffi_backend/test/_backend_test_c.py
+++ b/pypy/module/_cffi_backend/test/_backend_test_c.py
@@ -2264,3 +2264,36 @@
e = py.test.raises(TypeError, fputs, b"hello world\n", fw1)
assert str(e.value) == ("initializer for ctype 'struct NOT_FILE *' must "
"be a cdata pointer, not file")
+
+def test_FILE_object():
+ if sys.platform == "win32":
+ py.test.skip("testing FILE not implemented")
+ #
+ BFILE = new_struct_type("_IO_FILE")
+ BFILEP = new_pointer_type(BFILE)
+ BFILEPP = new_pointer_type(BFILEP)
+ 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,), BInt, False)
+ ll = find_and_load_library('c')
+ fputs = ll.load_function(BFunc, "fputs")
+ fileno = ll.load_function(BFunc2, "fileno")
+ #
+ import posix
+ fdr, fdw = posix.pipe()
+ fw1 = posix.fdopen(fdw, 'wb', 256)
+ #
+ fw1p = newp(BFILEPP, fw1)
+ fw1.write(b"X")
+ fw1.flush()
+ res = fputs(b"hello\n", fw1p[0])
+ assert res >= 0
+ res = fileno(fw1p[0])
+ assert res == fdw
+ fw1.close()
+ #
+ data = posix.read(fdr, 256)
+ assert data == b"Xhello\n"
+ posix.close(fdr)
diff --git a/pypy/module/_cffi_backend/test/test_ztranslation.py b/pypy/module/_cffi_backend/test/test_ztranslation.py
--- a/pypy/module/_cffi_backend/test/test_ztranslation.py
+++ b/pypy/module/_cffi_backend/test/test_ztranslation.py
@@ -1,8 +1,21 @@
from pypy.objspace.fake.checkmodule import checkmodule
+from pypy.module._cffi_backend import ctypeptr
+from pypy.rpython.lltypesystem import lltype, rffi
# side-effect: FORMAT_LONGDOUBLE must be built before test_checkmodule()
from pypy.module._cffi_backend import misc
def test_checkmodule():
- checkmodule('_cffi_backend')
+ # prepare_file_argument() is not working without translating the _file
+ # module too
+ def dummy_prepare_file_argument(space, fileobj):
+ return lltype.nullptr(rffi.CCHARP.TO)
+ old = ctypeptr.prepare_file_argument
+ try:
+ ctypeptr.prepare_file_argument = dummy_prepare_file_argument
+ #
+ checkmodule('_cffi_backend')
+ #
+ finally:
+ ctypeptr.prepare_file_argument = old
diff --git a/pypy/module/_file/interp_file.py b/pypy/module/_file/interp_file.py
--- a/pypy/module/_file/interp_file.py
+++ b/pypy/module/_file/interp_file.py
@@ -32,6 +32,7 @@
encoding = None
errors = None
fd = -1
+ cffi_fileobj = None # pypy/module/_cffi_backend
newlines = 0 # Updated when the stream is closed
@@ -148,7 +149,14 @@
del openstreams[stream]
except KeyError:
pass
- stream.close()
+ # close the stream. If cffi_fileobj is None, we close the
+ # underlying fileno too. Otherwise, we leave that to
+ # cffi_fileobj.close().
+ cffifo = self.cffi_fileobj
+ self.cffi_fileobj = None
+ stream.close1(cffifo is None)
+ if cffifo is not None:
+ cffifo.close()
def direct_fileno(self):
self.getstream() # check if the file is still open
diff --git a/pypy/rlib/streamio.py b/pypy/rlib/streamio.py
--- a/pypy/rlib/streamio.py
+++ b/pypy/rlib/streamio.py
@@ -268,6 +268,9 @@
return False
def close(self):
+ self.close1(True)
+
+ def close1(self, closefileno):
pass
def peek(self):
@@ -333,8 +336,9 @@
else:
data = data[n:]
- def close(self):
- os.close(self.fd)
+ def close1(self, closefileno):
+ if closefileno:
+ os.close(self.fd)
if sys.platform == "win32":
def truncate(self, size):
@@ -375,9 +379,10 @@
size = os.fstat(self.fd).st_size
self.mm = mmap.mmap(self.fd, size, access=self.access)
- def close(self):
+ def close1(self, closefileno):
self.mm.close()
- os.close(self.fd)
+ if closefileno:
+ os.close(self.fd)
def tell(self):
return self.pos
@@ -470,7 +475,7 @@
("truncate", [r_longlong]),
("flush", []),
("flushable", []),
- ("close", []),
+ ("close1", [int]),
("peek", []),
("try_to_find_file_descriptor", []),
("getnewlines", []),
@@ -715,7 +720,7 @@
truncate = PassThrough("truncate", flush_buffers=True)
flush = PassThrough("flush", flush_buffers=True)
flushable = PassThrough("flushable", flush_buffers=False)
- close = PassThrough("close", flush_buffers=False)
+ close1 = PassThrough("close1", flush_buffers=False)
try_to_find_file_descriptor = PassThrough("try_to_find_file_descriptor",
flush_buffers=False)
@@ -770,7 +775,7 @@
seek = PassThrough("seek", flush_buffers=True)
truncate = PassThrough("truncate", flush_buffers=True)
flush = PassThrough("flush", flush_buffers=True)
- close = PassThrough("close", flush_buffers=True)
+ close1 = PassThrough("close1", flush_buffers=True)
try_to_find_file_descriptor = PassThrough("try_to_find_file_descriptor",
flush_buffers=False)
@@ -838,7 +843,7 @@
flush = PassThrough("flush", flush_buffers=False)
flushable= PassThrough("flushable", flush_buffers=False)
- close = PassThrough("close", flush_buffers=False)
+ close1 = PassThrough("close1", flush_buffers=False)
try_to_find_file_descriptor = PassThrough("try_to_find_file_descriptor",
flush_buffers=False)
@@ -907,7 +912,7 @@
truncate = PassThrough("truncate", flush_buffers=True)
flush = PassThrough("flush", flush_buffers=False)
flushable= PassThrough("flushable", flush_buffers=False)
- close = PassThrough("close", flush_buffers=False)
+ close1 = PassThrough("close1", flush_buffers=False)
try_to_find_file_descriptor = PassThrough("try_to_find_file_descriptor",
flush_buffers=False)
@@ -1041,7 +1046,7 @@
truncate = PassThrough("truncate", flush_buffers=True)
flush = PassThrough("flush", flush_buffers=True)
flushable = PassThrough("flushable", flush_buffers=False)
- close = PassThrough("close", flush_buffers=False)
+ close1 = PassThrough("close1", flush_buffers=False)
try_to_find_file_descriptor = PassThrough("try_to_find_file_descriptor",
flush_buffers=False)
@@ -1067,7 +1072,7 @@
truncate = PassThrough("truncate", flush_buffers=False)
flush = PassThrough("flush", flush_buffers=False)
flushable = PassThrough("flushable", flush_buffers=False)
- close = PassThrough("close", flush_buffers=False)
+ close1 = PassThrough("close1", flush_buffers=False)
try_to_find_file_descriptor = PassThrough("try_to_find_file_descriptor",
flush_buffers=False)
@@ -1091,7 +1096,7 @@
peek = PassThrough("peek", flush_buffers=False)
flush = PassThrough("flush", flush_buffers=False)
flushable = PassThrough("flushable", flush_buffers=False)
- close = PassThrough("close", flush_buffers=False)
+ close1 = PassThrough("close1", flush_buffers=False)
write = PassThrough("write", flush_buffers=False)
truncate = PassThrough("truncate", flush_buffers=False)
getnewlines= PassThrough("getnewlines",flush_buffers=False)
@@ -1144,7 +1149,7 @@
truncate = PassThrough("truncate", flush_buffers=False)
flush = PassThrough("flush", flush_buffers=False)
flushable = PassThrough("flushable", flush_buffers=False)
- close = PassThrough("close", flush_buffers=False)
+ close1 = PassThrough("close1", flush_buffers=False)
try_to_find_file_descriptor = PassThrough("try_to_find_file_descriptor",
flush_buffers=False)
@@ -1172,6 +1177,6 @@
truncate = PassThrough("truncate", flush_buffers=False)
flush = PassThrough("flush", flush_buffers=False)
flushable = PassThrough("flushable", flush_buffers=False)
- close = PassThrough("close", flush_buffers=False)
+ close1 = PassThrough("close1", flush_buffers=False)
try_to_find_file_descriptor = PassThrough("try_to_find_file_descriptor",
flush_buffers=False)
More information about the pypy-commit
mailing list