Commits

Anonymous committed 580ce86

Implement _io.PyFileIO.readinto() and inherit read methods in place of local implementation.
This is part of the progressive change to the hierarchical implementation with delegation. Score in test_io.py is a little lower at fail/error/skip = 19/18/99.

Comments (0)

Files changed (3)

src/org/python/modules/_io/PyFileIO.java

 import org.python.core.BuiltinDocs;
 import org.python.core.Py;
 import org.python.core.PyArray;
+import org.python.core.PyBuffer;
 import org.python.core.PyInteger;
 import org.python.core.PyJavaType;
+import org.python.core.PyLong;
 import org.python.core.PyObject;
 import org.python.core.PyString;
 import org.python.core.PyType;
                 + (updating ? "+" : "");
     }
 
+    /*
+     * ===========================================================================================
+     * Exposed methods in the order they appear in CPython's fileio.c method table
+     * ===========================================================================================
+     */
+
+    // _RawIOBase.read is correct for us
+    // _RawIOBase.readall is correct for us
+
+    @Override
+    public PyObject readinto(PyObject buf) {
+        return FileIO_readinto(buf);
+    }
+
+    @ExposedMethod(doc = readinto_doc)
+    final PyLong FileIO_readinto(PyObject buf) {
+        // Check we can do this
+        _checkClosed();
+        _checkReadable();
+        // Perform the operation through a buffer view on the object
+        PyBuffer pybuf = writablePyBuffer(buf);
+        try {
+            PyBuffer.Pointer bp = pybuf.getBuf();
+            ByteBuffer byteBuffer = ByteBuffer.wrap(bp.storage, bp.offset, pybuf.getLen());
+            int count;
+            synchronized (ioDelegate) {
+                count = ioDelegate.readinto(byteBuffer);
+            }
+            return new PyLong(count);
+        } finally {
+            // Must unlock the PyBuffer view from client's object
+            pybuf.release();
+        }
+    }
+
+    @Override
+    public PyObject write(PyObject buf) {
+        return FileIO_write(buf);
+    }
+
+    @ExposedMethod(doc = write_doc)
+    final PyLong FileIO_write(PyObject obj) {
+        _checkWritable();
+        // Get or synthesise a buffer API on the object to be written
+        PyBuffer pybuf = readablePyBuffer(obj);
+        try {
+            // Access the data as a java.nio.ByteBuffer [pos:limit] within possibly larger array
+            PyBuffer.Pointer bp = pybuf.getBuf();
+            ByteBuffer byteBuffer = ByteBuffer.wrap(bp.storage, bp.offset, pybuf.getLen());
+            int count;
+            synchronized (ioDelegate) {
+                count = ioDelegate.write(byteBuffer);
+            }
+            return new PyLong(count);
+        } finally {
+            // Even if that went badly, we should release the lock on the client buffer
+            pybuf.release();
+        }
+    }
+
+    @Override
+    public long truncate() {
+        return _truncate();
+    }
+
+    @Override
+    public long truncate(long size) {
+        return _truncate(size);
+    }
+
+    @ExposedMethod(defaults = "null", doc = truncate_doc)
+    final long FileIO_truncate(PyObject size) {
+        return (size != null) ? _truncate(size.asLong()) : _truncate();
+    }
+
+    /** Common to FileIO_truncate(null) and truncate(). */
+    private final long _truncate() {
+        synchronized (ioDelegate) {
+            return ioDelegate.truncate(ioDelegate.tell());
+        }
+    }
+
+    /** Common to FileIO_truncate(size) and truncate(size). */
+    private final long _truncate(long size) {
+        synchronized (ioDelegate) {
+            return ioDelegate.truncate(size);
+        }
+    }
+
     /**
      * Close the underlying ioDelegate only if <code>closefd</code> was specified as (or defaulted
      * to) <code>True</code>.
 
     @ExposedMethod(defaults = {"0"}, doc = BuiltinDocs.file_seek_doc)
     final synchronized PyObject FileIO_seek(long pos, int how) {
-        checkClosed();
+        _checkClosed();
         return Py.java2py(ioDelegate.seek(pos, how));
     }
 
 
     @ExposedMethod(doc = BuiltinDocs.file_tell_doc)
     final synchronized long FileIO_tell() {
-        checkClosed();
+        _checkClosed();
         return ioDelegate.tell();
     }
 
     }
 
     @Override
-    public long truncate() {
-        return _truncate();
-    }
-
-    @Override
-    public long truncate(long size) {
-        return _truncate(size);
-    }
-
-    @ExposedMethod(defaults = "null", doc = truncate_doc)
-    final long FileIO_truncate(PyObject size) {
-        return (size != null) ? _truncate(size.asLong()) : _truncate();
-    }
-
-    /** Common to FileIO_truncate(null) and truncate(). */
-    private final long _truncate() {
-        synchronized (ioDelegate) {
-            return ioDelegate.truncate(ioDelegate.tell());
-        }
-    }
-
-    /** Common to FileIO_truncate(size) and truncate(size). */
-    private final long _truncate(long size) {
-        synchronized (ioDelegate) {
-            return ioDelegate.truncate(size);
-        }
-    }
-
-    @Override
     public boolean isatty() {
         return FileIO_isatty();
     }
         return PyJavaType.wrapJavaObject(ioDelegate.fileno());
     }
 
-    @ExposedMethod(defaults = {"-1"}, doc = BuiltinDocs.file_read_doc)
-    final synchronized PyString FileIO_read(int size) {
-        checkClosed();
-        ByteBuffer buf = ioDelegate.read(size);
-        return new PyString(StringUtil.fromBytes(buf));
+    // fileio.c has no flush(), but why not, when there is fdflush()?
+    // And it is a no-op for Jython io.FileIO, but why when there is FileChannel.force()?
+    @Override
+    public void flush() {
+        FileIO_flush();
     }
 
-    @Override
-    public PyString read(int size) {
-        return FileIO_read(size);
-    }
-
-    @ExposedMethod(doc = BuiltinDocs.file_read_doc)
-    final synchronized PyString FileIO_readall() {
-        return FileIO_read(-1);
-    }
-
-    /**
-     * Return a String for writing to the underlying file from obj.
-     */
-    private String asWritable(PyObject obj, String message) {
-        if (obj instanceof PyUnicode) {
-            return ((PyUnicode)obj).encode();
-        } else if (obj instanceof PyString) {
-            return ((PyString)obj).getString();
-        } else if (obj instanceof PyArray) {
-            return ((PyArray)obj).tostring();
-        } else if (obj instanceof BaseBytes) {
-            return StringUtil.fromBytes((BaseBytes)obj);
+    @ExposedMethod(doc = "Flush write buffers.")
+    final void FileIO_flush() {
+        if (writable()) {
+            // Check for *downstream* close. (Locally, closed means "closed to client actions".)
+            ioDelegate.checkClosed();
+            ioDelegate.flush();
         }
-        if (message == null) {
-            message =
-                    String.format("argument 1 must be string or buffer, not %.200s", obj.getType()
-                            .fastGetName());
-        }
-        throw Py.TypeError(message);
-    }
-
-    @ExposedMethod(doc = BuiltinDocs.file_write_doc)
-    final PyObject FileIO_write(PyObject obj) {
-        String writable = asWritable(obj, null);
-        byte[] bytes = StringUtil.toBytes(writable);
-        int written = write(ByteBuffer.wrap(bytes));
-        return new PyInteger(written);
-    }
-
-    final synchronized int write(ByteBuffer buf) {
-        checkClosed();
-        return ioDelegate.write(buf);
     }
 
     @ExposedMethod(names = {"__str__", "__repr__"}, doc = BuiltinDocs.file___str___doc)
         return FileIO_toString();
     }
 
-    private void checkClosed() {
-        ioDelegate.checkClosed();
-    }
-
-    @ExposedGet(name = "closed", doc = BuiltinDocs.file_closed_doc)
-    public boolean getClosed() {
-        return ioDelegate.closed();
-    }
-
 }

src/org/python/modules/_io/PyIOBase.java

     @ExposedMethod(doc = close_doc)
     final void _IOBase_close() {
         if (!__closed) {
-            // Cancel the wake-up call
-            closer.dismiss();
-            // Close should invoke (possibly overridden) flush here.
-            invoke("flush");
-            // Prevent further access from upstream
-            __closed = true;
+            /*
+             * The downstream file (file descriptor) will sometimes have been closed by another
+             * client. This should raise an error for us, (since data potentially in our buffers
+             * will be discarded) but we still must end up closed. the local close comes after the
+             * flush, in case operations within flush() test for "the wrong kind of closed".
+             */
+            try {
+                // Cancel the wake-up call
+                closer.dismiss();
+                // Close should invoke (possibly overridden) flush here.
+                invoke("flush");
+            } finally {
+                // Become closed to further client operations (other than certain enquiries)
+                __closed = true;
+            }
         }
     }
 
 
     @ExposedMethod(defaults = "null", doc = readline_doc)
     final PyObject _IOBase_readline(PyObject limit) {
-        if (limit == null || limit==Py.None) {
+        if (limit == null || limit == Py.None) {
             return _readline(-1);
         } else if (limit.isIndex()) {
             return _readline(limit.asInt());
     public PyObject __iter__() {
         _checkClosed();
         // Not like this, in spite of what base comment says, because file *is* an iterator
-        //        return new PySequenceIter(this);
+        // return new PySequenceIter(this);
         return this;
     }
 
 
         int h = 0;
 
-        if (hint==null || hint == Py.None) {
+        if (hint == null || hint == Py.None) {
             return new PyList(this);
 
         } else if (!hint.isIndex()) {

src/org/python/modules/_io/_io.java

                     + "Argument names are not part of the specification, and only the arguments\n"
                     + "of open() are intended to be used as keyword arguments.\n";
 
-// + "\n"
-// + "data:\n"
-// + "\n"
-// + "DEFAULT_BUFFER_SIZE\n"
-// + "\n"
-// + "   An int containing the default buffer size used by the module's buffered\n"
-// + "   I/O classes. open() uses the file's blksize (as obtained by os.stat) if\n"
-// + "   possible.\n";
-
     public static final String __doc__open =
             "Open file and return a stream.  Raise IOError upon failure.\n" + "\n"
                     + "file is either a text or byte string giving the name (and the path\n"