All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.python.modules._io.PyFileIO Maven / Gradle / Ivy

Go to download

Jython is an implementation of the high-level, dynamic, object-oriented language Python written in 100% Pure Java, and seamlessly integrated with the Java platform. It thus allows you to run Python on any Java platform.

There is a newer version: 2.7.4
Show newest version
/* Copyright (c)2013 Jython Developers */
package org.python.modules._io;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.nio.ByteBuffer;

import org.python.core.ArgParser;
import org.python.core.BuiltinDocs;
import org.python.core.Py;
import org.python.core.PyArray;
import org.python.core.PyBuffer;
import org.python.core.PyException;
import org.python.core.PyJavaType;
import org.python.core.PyLong;
import org.python.core.PyNewWrapper;
import org.python.core.PyObject;
import org.python.core.PyString;
import org.python.core.PyType;
import org.python.core.PyUnicode;
import org.python.core.Untraversable;
import org.python.core.io.FileIO;
import org.python.core.io.RawIOBase;
import org.python.core.io.StreamIO;
import org.python.expose.ExposedGet;
import org.python.expose.ExposedMethod;
import org.python.expose.ExposedNew;
import org.python.expose.ExposedSet;
import org.python.expose.ExposedType;

import jnr.constants.platform.Errno;

@Untraversable
@ExposedType(name = "_io.FileIO", base = PyRawIOBase.class)
public class PyFileIO extends PyRawIOBase {

    public static final PyType TYPE = PyType.fromClass(PyFileIO.class);

    /** The {@link FileIO} or {@link StreamIO} to which we delegate operations not complete locally. */
    private RawIOBase ioDelegate;

    /*
     * Implementation note: CPython fileio does not use the base-class, possibly overridden,
     * readable(), writable() and seekable(). Instead it sets local variables for readable and
     * writable using the open mode, and returns these as readable() and writable(), while using
     * them internally. The local variable seekable (and seekable()) is worked out from a one-time
     * trial seek.
     */
    /** Set true when stream must be readable = reading | updating */
    private boolean readable;

    /** Set true when stream must be writable = writing | updating | appending */
    private boolean writable;

    /** Set true when we have made the seekable test */
    private boolean seekableKnown;

    /** Set true when stream is seekable */
    private boolean seekable;

    /** Whether to close the underlying stream on closing this object. */
    @ExposedGet(doc = "True if the file descriptor will be closed")
    public final boolean closefd;

    /** The mode as a PyString based on readable and writable */
    @ExposedGet(doc = "String giving the file mode: 'rb', 'rb+', or 'wb'")
    public final PyString mode;

    @ExposedSet(name = "mode")
    public final void mode_readonly(PyString value) {
        readonlyAttributeError("mode");
    }

    private static final PyString defaultMode = new PyString("r");

    /**
     * Construct an open _io.FileIO starting with an object that may be a file name or
     * a file descriptor (actually a {@link RawIOBase}). Only the relevant flags within the parsed
     * mode object are consulted (so that flags meaningful to this sub-class need not be processed
     * out).
     *
     * @param file path or descriptor on which this should be constructed
     * @param mode type of access specified
     * @param closefd if false, do not close fd on call to
     *            close()
     */
    public PyFileIO(PyObject file, OpenMode mode, boolean closefd) {
        this(TYPE, file, mode, closefd);
    }

    /**
     * Construct an open _io.FileIO for a sub-class constructor starting with an object
     * that may be a file name or a file descriptor (actually a {@link RawIOBase}). Only the
     * relevant flags within the parsed mode object are consulted (so that flags meaningful to this
     * sub-class need not be processed out).
     *
     * @param subtype for which construction is occurring
     * @param file path or descriptor on which this should be constructed
     * @param mode type of access specified
     * @param closefd if false, do not close file on call to
     *            close()
     */
    public PyFileIO(PyType subtype, PyObject file, OpenMode mode, boolean closefd) {
        super(subtype);

        // Establish the direction(s) of flow
        readable = mode.reading | mode.updating;
        writable = mode.writing | mode.updating | mode.appending;

        // Assign a delegate according to the file argument
        this.closefd = closefd;
        setDelegate(file, mode);

        // The mode string of a raw file always asserts it is binary: "rb", "rb+", or "wb".
        if (readable) {
            this.mode = new PyString(writable ? "rb+" : "rb");
        } else {
            this.mode = new PyString("wb");
        }
    }

    /**
     * Helper function that turns the arguments of the most general constructor, or
     * __new__, into a {@link FileIO}, assigned to {@link #ioDelegate}. It enforces
     * rules on {@link #closefd} and the type of object that may be a file descriptor, and assigns
     * the name attribute to the string name or the file descriptor (see Python docs
     * for io.FileIO.name). This places the logic of those several operations in one place.
     * 

* In many cases (such as construction from a file name, the FileIO is a newly-opened file. When * the file object passed in is a file descriptor, the FileIO may be created to wrap that * existing stream. * * @param file name or descriptor * @param mode parsed file open mode */ private void setDelegate(PyObject file, OpenMode mode) { if (file instanceof PyString) { // Open a file by name if (!closefd) { throw Py.ValueError("Cannot use closefd=False with file name"); } ioDelegate = new FileIO((PyString)file, mode.forFileIO()); } else { /* * Build an _io.FileIO from an existing "file descriptor", which we may or may not want * closed at the end. A CPython file descriptor is an int, but this is not the natural * choice in Jython, and file descriptors should be treated as opaque. */ Object fd = file.__tojava__(Object.class); if (fd instanceof FileIO || fd instanceof StreamIO) { /* * It is the "Jython file descriptor", of a type suitable to be the ioDelegate. The * allowed types are able to give us a non-null InputStream or OutputStream, * according to direction. */ ioDelegate = (RawIOBase)fd; } } // If we couldn't figure it out, ioDelegate will still be null if (ioDelegate == null) { // The file was a type we don't know how to use throw Py.TypeError(String.format("invalid file: %s", file.__repr__().asString())); } else { if (ioDelegate.closed()) { // A closed file descriptor is a "bad descriptor" throw Py.OSError(Errno.EBADF); } if ((readable && !ioDelegate.readable()) || (writable && !ioDelegate.writable())) { // Requested mode in conflict with underlying file or stream throw tailoredValueError(readable ? "read" : "writ"); } // The name is either the textual name or a file descriptor (see Python docs) fastGetDict().__setitem__("name", file); } } private static final String[] openArgs = {"file", "mode", "closefd"}; /** * Create a {@link PyFileIO} and its FileIO delegate from the arguments. */ @ExposedNew static PyObject FileIO___new__(PyNewWrapper new_, boolean init, PyType subtype, PyObject[] args, String[] keywords) { ArgParser ap = new ArgParser("FileIO", args, keywords, openArgs, 1); PyObject file = ap.getPyObject(0); PyObject m = ap.getPyObject(1, defaultMode); boolean closefd = Py.py2boolean(ap.getPyObject(2, Py.True)); // Decode the mode string and check it OpenMode mode = new OpenMode(m.asString()) { { invalid |= universal | text; // These other modes are invalid } }; mode.checkValid(); if (subtype == TYPE) { return new PyFileIO(subtype, file, mode, closefd); } else { return new PyFileIODerived(subtype, file, mode, closefd); } } /* * =========================================================================================== * 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) { int count; if (!readable) { // ... (or closed) throw tailoredValueError("read"); } if (buf instanceof PyArray) { // Special case: PyArray knows how to read into itself PyArray a = (PyArray)buf; try { // The ioDelegate, if readable, can always provide an InputStream (see setDelegate) InputStream is = ioDelegate.asInputStream(); count = a.fillFromStream(is); count *= a.getItemsize(); } catch (IOException ioe) { throw Py.IOError(ioe); } } else { // Perform the operation through a buffer view on the object try (PyBuffer pybuf = writablePyBuffer(buf)) { ByteBuffer byteBuffer = pybuf.getNIOByteBuffer(); synchronized (ioDelegate) { count = ioDelegate.readinto(byteBuffer); } } } return new PyLong(count); } @Override public PyObject write(PyObject buf) { return FileIO_write(buf); } @ExposedMethod(doc = write_doc) final PyLong FileIO_write(PyObject buf) { int count; if (!writable) { // ... (or closed) throw tailoredValueError("writ"); } if (buf instanceof PyArray) { // Special case: PyArray knows how to write itself try { // The ioDelegate, if writable, can always provide an OutputStream (see setDelegate) OutputStream os = ioDelegate.asOutputStream(); count = ((PyArray)buf).toStream(os); } catch (IOException ioe) { throw Py.IOError(ioe); } } else { // Get or synthesise a buffer API on the object to be written try (PyBuffer pybuf = readablePyBuffer(buf)) { // Access the data as a java.nio.ByteBuffer [pos:limit] within possibly larger array ByteBuffer byteBuffer = pybuf.getNIOByteBuffer(); synchronized (ioDelegate) { count = ioDelegate.write(byteBuffer); } } } return new PyLong(count); } @Override public long seek(long pos, int whence) { return FileIO_seek(pos, whence); } @ExposedMethod(defaults = "0", doc = seek_doc) final long FileIO_seek(long pos, int whence) { if (__closed) { throw closedValueError(); } synchronized (ioDelegate) { return ioDelegate.seek(pos, whence); } } // _IOBase.tell() is correct for us @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() { if (!writable) { // ... (or closed) throw tailoredValueError("writ"); } synchronized (ioDelegate) { return ioDelegate.truncate(ioDelegate.tell()); } } /** Common to FileIO_truncate(size) and truncate(size). */ private final long _truncate(long size) { if (!writable) { // ... (or closed) throw tailoredValueError("writ"); } synchronized (ioDelegate) { return ioDelegate.truncate(size); } } /** * Close the underlying ioDelegate only if closefd was specified as (or defaulted * to) True. */ @Override public void close() { FileIO_close(); } @ExposedMethod final synchronized void FileIO_close() { try { // Close this object to further input (also calls flush) super.close(); } finally { // Now close downstream (if required to) if (closefd) { ioDelegate.close(); } // This saves us doing two tests for each action (when the file is open) readable = false; writable = false; } } @Override public boolean seekable() { return FileIO_seekable(); } @ExposedMethod(doc = seekable_doc) final boolean FileIO_seekable() { if (__closed) { throw closedValueError(); } if (!seekableKnown) { try { ioDelegate.seek(0, 1); // Trial seek seekable = true; } catch (PyException exc) { if (!exc.match(Py.IOError)) { throw exc; } seekable = false; } seekableKnown = true; } return seekable; } @Override public boolean readable() throws PyException { return FileIO_readable(); } @ExposedMethod(doc = readable_doc) final boolean FileIO_readable() { if (__closed) { throw closedValueError(); } return readable; } @Override public boolean writable() throws PyException { return FileIO_writable(); } @ExposedMethod(doc = writable_doc) final boolean FileIO_writable() { if (__closed) { throw closedValueError(); } return writable; } @Override public PyObject fileno() { return FileIO_fileno(); } @ExposedMethod(doc = fileno_doc) final PyObject FileIO_fileno() { return PyJavaType.wrapJavaObject(ioDelegate.fileno()); } @Override public boolean isatty() { return FileIO_isatty(); } @ExposedMethod(doc = isatty_doc) final boolean FileIO_isatty() { if (__closed) { throw closedValueError(); } return ioDelegate.isatty(); } // 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(); } @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(); } } @ExposedMethod(names = {"__str__", "__repr__"}, doc = BuiltinDocs.object___str___doc) final String FileIO_toString() { if (closed()) { return "<_io.FileIO [closed]>"; } else { PyObject name = fastGetDict().__finditem__("name"); if (name != null && (name instanceof PyString)) { String xname = name.asString(); if (name instanceof PyUnicode) { xname = PyString.encode_UnicodeEscape(xname, false); } return String.format("<_io.FileIO name='%s' mode='%s'>", xname, mode); } else { return String.format("<_io.FileIO fd=%s mode='%s'>", fileno(), mode); } } } @Override public String toString() { return FileIO_toString().toString(); } /** * Convenience method providing the exception when an method requires the file to be open, and * it isn't. * * @return ValueError to throw */ private PyException closedValueError() { return Py.ValueError("I/O operation on closed file"); } /** * Convenience method providing the exception when an method requires the file to be open, * readable or writable, and it isn't. If the file is closed, return the message for that, * otherwise, one about reading or writing. * * @param action type of operation not valid ("read" or "writ" in practice). * @return ValueError to throw */ private PyException tailoredValueError(String action) { if (action == null || __closed) { return closedValueError(); } else { return Py.ValueError("File not open for " + action + "ing"); } } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy