[Jython-checkins] jython: Support "readall" on files reported as having size=0. Fixes #2358

jim.baker jython-checkins at python.org
Mon Dec 28 12:30:41 EST 2015


https://hg.python.org/jython/rev/c8245364e407
changeset:   7842:c8245364e407
user:        Jim Baker <jim.baker at rackspace.com>
date:        Mon Dec 28 10:30:36 2015 -0700
summary:
  Support "readall" on files reported as having size=0. Fixes #2358

On Linux for files in the /proc virtual filesystem, nearly all files
have a reported size of zero. Previously the implementation of
"readall" functionality (eg, file.read()) depended on this size as
being accurate. Now if the size=0, Jython attempts to read the file in
chunks.

files:
  Lib/test/test_file_jy.py           |  15 +++++-
  src/org/python/core/io/FileIO.java |  47 ++++++++++++++++-
  2 files changed, 57 insertions(+), 5 deletions(-)


diff --git a/Lib/test/test_file_jy.py b/Lib/test/test_file_jy.py
--- a/Lib/test/test_file_jy.py
+++ b/Lib/test/test_file_jy.py
@@ -2,10 +2,10 @@
 
 Made for Jython.
 """
-from __future__ import with_statement
 import os
 import unittest
 from test import test_support
+from java.lang import System
 
 class FileTestCase(unittest.TestCase):
 
@@ -51,6 +51,19 @@
         f = open(test_support.TESTFN, 'w')      # should succeed, raised IOError (permission denied) prior to fix
         f.close()
 
+    # http://bugs.jython.org/issue2358
+    def test_read_empty_file(self):
+        f = open(test_support.TESTFN, 'w')
+        f.close()
+        f = open(test_support.TESTFN)
+        self.assertEqual(f.read(), '')
+
+    # http://bugs.jython.org/issue2358
+    @unittest.skipUnless(System.getProperty('os.name') == u'Linux', 'Linux required')
+    def test_can_read_proc_filesystem(self):
+        with open('/proc/{}/cmdline'.format(os.getpid())) as f:
+            self.assertIn('org.python.util.jython', f.read())
+
 
 def test_main():
     test_support.run_unittest(FileTestCase)
diff --git a/src/org/python/core/io/FileIO.java b/src/org/python/core/io/FileIO.java
--- a/src/org/python/core/io/FileIO.java
+++ b/src/org/python/core/io/FileIO.java
@@ -13,10 +13,11 @@
 import java.nio.ByteBuffer;
 import java.nio.channels.Channels;
 import java.nio.channels.FileChannel;
+import java.util.ArrayList;
+import java.util.List;
 
 import jnr.constants.platform.Errno;
 import jnr.posix.util.FieldAccess;
-import jnr.posix.util.Platform;
 import org.python.core.Py;
 import org.python.core.PyObject;
 import org.python.core.PyString;
@@ -281,7 +282,9 @@
      * Read until EOF with one readinto() call.
      *
      * Takes advantage of the fact that the underlying file's size is
-     * available.
+     * available. However, we have to special case if file size is 0,
+     * as seen in the /proc virtual file system - in this case we cannot
+     * assume the file is truly empty.
      *
      * @return {@inheritDoc}
      */
@@ -301,10 +304,46 @@
         if (toRead > Integer.MAX_VALUE) {
             throw Py.OverflowError("requested number of bytes is more than a Python string can "
                                    + "hold");
+        } else if (toRead == 0) {
+            // Support files that the underlying OS has labeled with size=0, even though they have content,
+            // eg, /proc files on Linux
+            return readallInChunks();
+        } else {
+            ByteBuffer all = ByteBuffer.allocate((int) toRead);
+            readinto(all);
+            all.flip();
+            return all;
+        }
+    }
+
+    private ByteBuffer readallInChunks() {
+        // assumes checks have been performed
+        final List<ByteBuffer> chunks = new ArrayList<>();
+        final int MAX_CHUNK_SIZE = 8192;
+        int length = 0;
+        while (true) {
+            final ByteBuffer chunk = ByteBuffer.allocate(MAX_CHUNK_SIZE);
+            readinto(chunk);
+            int chunkSize = chunk.position();
+            length += chunkSize;
+            chunk.flip();
+            chunks.add(chunk);
+            if (chunkSize < MAX_CHUNK_SIZE) {
+                break;
+            }
         }
 
-        ByteBuffer all = ByteBuffer.allocate((int)toRead);
-        readinto(all);
+        // given the size of MAX_CHUNK_SIZE, perhaps most or even all files in /proc,
+        // or similar virtual filesystems, if any, might fit into one chunk
+        if (chunks.size() == 1) {
+            return chunks.get(0);
+        }
+
+        // otherwise append together into a single ByteBuffer
+        final ByteBuffer all = ByteBuffer.allocate(length);
+        for (ByteBuffer chunk : chunks) {
+            all.put(chunk);
+        }
         all.flip();
         return all;
     }

-- 
Repository URL: https://hg.python.org/jython


More information about the Jython-checkins mailing list