Author: Armin Rigo <[email protected]>
Branch: 
Changeset: r73282:fd7c85c9370f
Date: 2014-09-02 16:04 +0200
http://bitbucket.org/pypy/pypy/changeset/fd7c85c9370f/

Log:    From RFile.close(), call the C function fclose() or pclose() by
        releasing the GIL. But from RFile.__del__(), we can't and don't
        release the GIL, so we use a different pair of llexternal functions.

diff --git a/rpython/rlib/rfile.py b/rpython/rlib/rfile.py
--- a/rpython/rlib/rfile.py
+++ b/rpython/rlib/rfile.py
@@ -60,11 +60,16 @@
 c_setvbuf = llexternal('setvbuf', [FILEP, rffi.CCHARP, rffi.INT, rffi.SIZE_T],
                        rffi.INT)
 
+c_fclose = llexternal('fclose', [FILEP], rffi.INT)
+c_pclose = llexternal('pclose', [FILEP], rffi.INT)
+
 # Note: the following two functions are called from __del__ methods,
 # so must be 'releasegil=False'.  Otherwise, a program using both
 # threads and the RFile class cannot translate.  See c684bf704d1f
-c_fclose = llexternal('fclose', [FILEP], rffi.INT, releasegil=False)
-c_pclose = llexternal('pclose', [FILEP], rffi.INT, releasegil=False)
+c_fclose_in_del = llexternal('fclose', [FILEP], rffi.INT, releasegil=False)
+c_pclose_in_del = llexternal('pclose', [FILEP], rffi.INT, releasegil=False)
+_fclose2 = (c_fclose, c_fclose_in_del)
+_pclose2 = (c_pclose, c_pclose_in_del)
 
 c_getc = llexternal('getc', [FILEP], rffi.INT, macro=True)
 c_fgets = llexternal('fgets', [rffi.CCHARP, rffi.INT, FILEP], rffi.CCHARP)
@@ -183,16 +188,23 @@
             lltype.free(ll_type, flavor='raw')
     finally:
         lltype.free(ll_command, flavor='raw')
-    return RFile(ll_file, c_pclose)
+    return RFile(ll_file, _pclose2)
 
 
 class RFile(object):
-    def __init__(self, ll_file, do_close=c_fclose):
+    def __init__(self, ll_file, close2=_fclose2):
         self._ll_file = ll_file
-        self._do_close = do_close
+        self._close2 = close2
 
     def __del__(self):
-        self.close()
+        """Closes the described file when the object's last reference
+        goes away.  Unlike an explicit call to close(), this is meant
+        as a last-resort solution and cannot release the GIL or return
+        an error code."""
+        ll_file = self._ll_file
+        if ll_file:
+            do_close = self._close2[1]
+            do_close(ll_file)       # return value ignored
 
     def close(self):
         """Closes the described file.
@@ -207,7 +219,8 @@
         if ll_file:
             # double close is allowed
             self._ll_file = lltype.nullptr(FILEP.TO)
-            res = self._do_close(ll_file)
+            do_close = self._close2[0]
+            res = do_close(ll_file)
             if res == -1:
                 errno = rposix.get_errno()
                 raise OSError(errno, os.strerror(errno))
diff --git a/rpython/rlib/test/test_rfile.py b/rpython/rlib/test/test_rfile.py
--- a/rpython/rlib/test/test_rfile.py
+++ b/rpython/rlib/test/test_rfile.py
@@ -1,4 +1,4 @@
-import os, sys, py, errno
+import os, sys, py, errno, gc
 from rpython.rtyper.test.tool import BaseRtypingTest
 from rpython.tool.udir import udir
 from rpython.rlib import rfile
@@ -267,6 +267,18 @@
         cls.tmpdir = udir.join('test_rfile_direct')
         cls.tmpdir.ensure(dir=True)
 
+    def test_auto_close(self):
+        fname = str(self.tmpdir.join('file_auto_close'))
+        f = rfile.create_file(fname, 'w')
+        f.write('a')    # remains in buffers
+        assert os.path.getsize(fname) == 0
+        del f
+        for i in range(5):
+            if os.path.getsize(fname) != 0:
+                break
+            gc.collect()
+        assert os.path.getsize(fname) == 1
+
     def test_read_a_lot(self):
         fname = str(self.tmpdir.join('file_read_a_lot'))
         with open(fname, 'w') as f:
_______________________________________________
pypy-commit mailing list
[email protected]
https://mail.python.org/mailman/listinfo/pypy-commit

Reply via email to