org.python.core.PyFile Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of jython-slim Show documentation
Show all versions of jython-slim Show documentation
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.
/*
* Copyright (c) Corporation for National Research Initiatives
* Copyright (c) Jython Developers
*/
package org.python.core;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.concurrent.Callable;
import org.python.core.finalization.FinalizableBuiltin;
import org.python.core.finalization.FinalizeTrigger;
import org.python.core.io.BinaryIOWrapper;
import org.python.core.io.BufferedIOBase;
import org.python.core.io.BufferedRandom;
import org.python.core.io.BufferedReader;
import org.python.core.io.BufferedWriter;
import org.python.core.io.FileIO;
import org.python.core.io.IOBase;
import org.python.core.io.LineBufferedRandom;
import org.python.core.io.LineBufferedWriter;
import org.python.core.io.RawIOBase;
import org.python.core.io.StreamIO;
import org.python.core.io.TextIOBase;
import org.python.core.io.TextIOWrapper;
import org.python.core.io.UniversalIOWrapper;
import org.python.expose.ExposedDelete;
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;
/**
* The Python file type. Wraps an {@link TextIOBase} object.
*/
@ExposedType(name = "file", doc = BuiltinDocs.file_doc)
public class PyFile extends PyObject implements FinalizableBuiltin, Traverseproc {
public static final PyType TYPE = PyType.fromClass(PyFile.class);
/** The filename */
@ExposedGet(doc = BuiltinDocs.file_name_doc)
public PyObject name;
/** The mode string */
@ExposedGet(doc = BuiltinDocs.file_mode_doc)
public String mode;
@ExposedGet(doc = BuiltinDocs.file_encoding_doc)
public String encoding;
@ExposedGet(doc = BuiltinDocs.file_errors_doc)
public String errors;
/** Indicator dictating whether a space should be written to this
* file on the next print statement (not currently implemented in
* print ) */
public boolean softspace = false;
/** Whether this file is opened for reading */
private boolean reading = false;
/** Whether this file is opened for writing */
private boolean writing = false;
/** Whether this file is opened in appending mode */
private boolean appending = false;
/** Whether this file is opened for updating */
private boolean updating = false;
/** Whether this file is opened in binary mode */
private boolean binary = false;
/** Whether this file is opened in universal newlines mode */
private boolean universal = false;
/** The underlying IO object */
private TextIOBase file;
/** The file's closer object; ensures the file is closed at
* shutdown */
private Closer closer;
public PyFile() {FinalizeTrigger.ensureFinalizer(this);}
public PyFile(PyType subType) {
super(subType);
FinalizeTrigger.ensureFinalizer(this);
}
public PyFile(RawIOBase raw, String name, String mode, int bufsize) {
parseMode(mode);
file___init__(raw, name, mode, bufsize);
FinalizeTrigger.ensureFinalizer(this);
}
public PyFile(InputStream istream, String name, String mode, int bufsize, boolean closefd) {
parseMode(mode);
file___init__(new StreamIO(istream, closefd), name, mode, bufsize);
FinalizeTrigger.ensureFinalizer(this);
}
/**
* Creates a file object wrapping the given InputStream
. The builtin
* method file
doesn't expose this functionality (open
does
* albeit deprecated) as it isn't available to regular Python code. To wrap an
* InputStream in a file from Python, use
* {@link org.python.core.util.FileUtil#wrap(InputStream, String, int)}
* {@link org.python.core.util.FileUtil#wrap(InputStream, String)}
* {@link org.python.core.util.FileUtil#wrap(InputStream, int)}
* {@link org.python.core.util.FileUtil#wrap(InputStream)}
*/
public PyFile(InputStream istream, String mode, int bufsize) {
this(istream, "", mode, bufsize, true);
}
public PyFile(InputStream istream, String mode) {
this(istream, mode, -1);
}
public PyFile(InputStream istream, int bufsize) {
this(istream, "r", bufsize);
}
public PyFile(InputStream istream) {
this(istream, -1);
}
public PyFile(OutputStream ostream, String name, String mode, int bufsize, boolean closefd) {
parseMode(mode);
file___init__(new StreamIO(ostream, closefd), name, mode, bufsize);
FinalizeTrigger.ensureFinalizer(this);
}
/**
* Creates a file object wrapping the given OutputStream
. The builtin
* method file
doesn't expose this functionality (open
does
* albeit deprecated) as it isn't available to regular Python code. To wrap an
* OutputStream in a file from Python, use
* {@link org.python.core.util.FileUtil#wrap(OutputStream, String, int)}
* {@link org.python.core.util.FileUtil#wrap(OutputStream, String)}
* {@link org.python.core.util.FileUtil#wrap(OutputStream, int)}
* {@link org.python.core.util.FileUtil#wrap(OutputStream)}
*/
public PyFile(OutputStream ostream, String mode, int bufsize) {
this(ostream, "", mode, bufsize, true);
}
public PyFile(OutputStream ostream, int bufsize) {
this(ostream, "w", bufsize);
}
public PyFile(OutputStream ostream) {
this(ostream, -1);
}
public PyFile(String name, String mode, int bufsize) {
file___init__(new FileIO(name, parseMode(mode)), name, mode, bufsize);
FinalizeTrigger.ensureFinalizer(this);
}
@ExposedNew
@ExposedMethod(doc = BuiltinDocs.file___init___doc)
final void file___init__(PyObject[] args, String[] kwds) {
ArgParser ap = new ArgParser("file", args, kwds, new String[] {"name", "mode", "buffering"},
1);
PyObject name = ap.getPyObject(0);
String mode = ap.getString(1, "r");
int bufsize = ap.getInt(2, -1);
file___init__(new FileIO((PyString) name, parseMode(mode)), name, mode, bufsize);
closer = new Closer(file, Py.getSystemState());
}
private void file___init__(RawIOBase raw, String name, String mode, int bufsize) {
file___init__(raw, Py.newStringOrUnicode(name), mode, bufsize);
}
private void file___init__(RawIOBase raw, PyObject name, String mode, int bufsize) {
this.name = name;
this.mode = mode;
BufferedIOBase buffer = createBuffer(raw, bufsize);
if (universal) {
this.file = new UniversalIOWrapper(buffer);
} else if (!binary) {
this.file = new TextIOWrapper(buffer);
} else {
this.file = new BinaryIOWrapper(buffer);
}
}
/**
* Set the strings defining the encoding and error handling policy. Setting these strings
* affects behaviour of the {@link #writelines(PyObject)} when passed a {@link PyUnicode} value.
*
* @param encoding the encoding
property of file
.
* @param errors the errors
property of file
(or null
).
*/
void setEncoding(String encoding, String errors) {
this.encoding = encoding;
this.errors = errors;
}
/**
* Wrap the given RawIOBase with a BufferedIOBase according to the
* mode and given bufsize.
*
* @param raw a RawIOBase value
* @param bufsize an int size of the buffer
* @return a BufferedIOBase wrapper
*/
private BufferedIOBase createBuffer(RawIOBase raw, int bufsize) {
if (bufsize < 0) {
bufsize = IOBase.DEFAULT_BUFFER_SIZE;
}
boolean lineBuffered = bufsize == 1;
BufferedIOBase buffer;
if (updating) {
buffer = lineBuffered ? new LineBufferedRandom(raw) : new BufferedRandom(raw, bufsize);
} else if (writing || appending) {
buffer = lineBuffered ? new LineBufferedWriter(raw) : new BufferedWriter(raw, bufsize);
} else if (reading) {
// Line buffering is for output only
buffer = new BufferedReader(raw, lineBuffered ? IOBase.DEFAULT_BUFFER_SIZE : bufsize);
} else {
// Should never happen
throw Py.ValueError("unknown mode: '" + mode + "'");
}
return buffer;
}
/**
* Parse and validate the python file mode, returning a cleaned file mode suitable for FileIO.
*
* @param mode a python file mode String
* @return a RandomAccessFile mode String
*/
private String parseMode(String mode) {
String message = null;
boolean duplicate = false, invalid = false, text_intent = false;
int n = mode.length();
// Convert the letters to booleans, noticing duplicates
for (int i = 0; i < n; i++) {
char c = mode.charAt(i);
switch (c) {
case 'r':
duplicate = reading;
reading = true;
break;
case 'w':
duplicate = writing;
writing = true;
break;
case 'a':
duplicate = appending;
appending = true;
break;
case '+':
duplicate = updating;
updating = true;
break;
case 'b':
duplicate = binary;
binary = true;
break;
case 't':
duplicate = text_intent;
text_intent = true;
binary = false;
break;
case 'U':
duplicate = universal;
universal = true;
break;
default:
invalid = true;
}
// duplicate is set iff c was encountered previously */
if (duplicate) {
invalid = true;
break;
}
}
// Implications
reading |= universal;
binary |= universal;
// Standard tests and the mode for FileIO
StringBuilder fileioMode = new StringBuilder();
if (!invalid) {
if (universal && (writing || appending)) {
// Not quite true, consider 'Ub', but it's what CPython says:
message = "universal newline mode can only be used with modes starting with 'r'";
} else {
// Build the FileIO mode string
if (reading) {
fileioMode.append('r');
}
if (writing) {
fileioMode.append('w');
}
if (appending) {
fileioMode.append('a');
}
if (fileioMode.length() != 1) {
// We should only have added one of the above
message = "mode string must begin with one of 'r', 'w', 'a' or 'U', not '" //
+ mode + "'";
}
if (updating) {
fileioMode.append('+');
}
}
invalid |= (message != null);
}
// Finally, if invalid, report this as an error
if (invalid) {
if (message == null) {
// Duplicates discovered or invalid symbols
message = String.format("invalid mode: '%.20s'", mode);
}
throw Py.ValueError(message);
}
return fileioMode.toString();
}
@ExposedMethod(defaults = {"-1"}, doc = BuiltinDocs.file_read_doc)
final synchronized PyString file_read(int size) {
checkClosed();
return new PyString(file.read(size));
}
public PyString read(int size) {
return file_read(size);
}
public PyString read() {
return file_read(-1);
}
@ExposedMethod(doc = BuiltinDocs.file_readinto_doc)
final synchronized int file_readinto(PyObject buf) {
checkClosed();
return file.readinto(buf);
}
public int readinto(PyObject buf) {
return file_readinto(buf);
}
@ExposedMethod(defaults = {"-1"}, doc = BuiltinDocs.file_readline_doc)
final synchronized PyString file_readline(int max) {
checkClosed();
return new PyString(file.readline(max));
}
public PyString readline(int max) {
return file_readline(max);
}
public PyString readline() {
return file_readline(-1);
}
@ExposedMethod(defaults = {"0"}, doc = BuiltinDocs.file_readlines_doc)
final synchronized PyObject file_readlines(int sizehint) {
checkClosed();
PyList list = new PyList();
int count = 0;
do {
String line = file.readline(-1);
int len = line.length();
if (len == 0) {
// EOF
break;
}
count += len;
list.append(new PyString(line));
} while (sizehint <= 0 || count < sizehint);
return list;
}
public PyObject readlines(int sizehint) {
return file_readlines(sizehint);
}
public PyObject readlines() {
return file_readlines(0);
}
@Override
public PyObject __iternext__() {
return file___iternext__();
}
final synchronized PyObject file___iternext__() {
checkClosed();
String next = file.readline(-1);
if (next.length() == 0) {
return null;
}
return new PyString(next);
}
@ExposedMethod(doc = BuiltinDocs.file_next_doc)
final PyObject file_next() {
PyObject ret = file___iternext__();
if (ret == null) {
throw Py.StopIteration("");
}
return ret;
}
public PyObject next() {
return file_next();
}
@ExposedMethod(names = {"__enter__", "__iter__", "xreadlines"},
doc = BuiltinDocs.file___iter___doc)
final PyObject file_self() {
checkClosed();
return this;
}
public PyObject __enter__() {
return file_self();
}
@Override
public PyObject __iter__() {
return file_self();
}
public PyObject xreadlines() {
return file_self();
}
@ExposedMethod(doc = BuiltinDocs.file_write_doc)
final void file_write(PyObject obj) {
file_write(asWritable(obj, null));
}
final synchronized void file_write(String string) {
checkClosed();
softspace = false;
file.write(string);
}
public void write(String string) {
file_write(string);
}
@ExposedMethod(doc = BuiltinDocs.file_writelines_doc)
final synchronized void file_writelines(PyObject lines) {
checkClosed();
PyObject iter = Py.iter(lines, "writelines() requires an iterable argument");
for (PyObject item = null; (item = iter.__iternext__()) != null;) {
checkClosed(); // ... in case a nasty iterable closed this file
softspace = false;
file.write(asWritable(item, "writelines() argument must be a sequence of strings"));
}
}
public void writelines(PyObject lines) {
file_writelines(lines);
}
/**
* Return a String for writing to the underlying file from obj. This is a helper for {@link file_write}
* and {@link file_writelines}.
*
* @param obj to write
* @param message for TypeError if raised (or null for default message)
* @return bytes representing the value (as a String in the Jython convention)
*/
private String asWritable(PyObject obj, String message) {
if (obj instanceof PyUnicode) {
// Unicode must be encoded into bytes (null arguments here invoke the default values)
return ((PyUnicode)obj).encode(encoding, errors);
} else if (obj instanceof PyString) {
// Take a short cut
return ((PyString)obj).getString();
} else if (obj instanceof PyArray && !binary) {
// Fall through to TypeError. (If binary, BufferProtocol takes care of PyArray.)
} else {
// Try to get a simple byte-oriented buffer
try (PyBuffer buf = ((BufferProtocol)obj).getBuffer(PyBUF.SIMPLE)) {
// ... and treat those bytes as a String
return buf.toString();
} catch (ClassCastException e) {
// Does not implement BufferProtocol (in reality). Fall through to message.
}
}
if (message == null) {
// Messages differ for text or binary streams (CPython) but we always add the type
String fmt = "expected a string or%s buffer, not %.200s";
message = String.format(fmt, (binary ? "" : " character"), obj.getType().fastGetName());
}
throw Py.TypeError(message);
}
@ExposedMethod(doc = BuiltinDocs.file_tell_doc)
final synchronized long file_tell() {
checkClosed();
return file.tell();
}
public long tell() {
return file_tell();
}
@ExposedMethod(defaults = {"0"}, doc = BuiltinDocs.file_seek_doc)
final synchronized void file_seek(long pos, int how) {
checkClosed();
file.seek(pos, how);
}
public void seek(long pos, int how) {
file_seek(pos, how);
}
public void seek(long pos) {
file_seek(pos, 0);
}
@ExposedMethod(doc = BuiltinDocs.file_flush_doc)
final synchronized void file_flush() {
checkClosed();
file.flush();
}
public void flush() {
file_flush();
}
@ExposedMethod(doc = BuiltinDocs.file_close_doc)
final synchronized void file_close() {
if (closer != null) {
closer.close();
closer = null;
} else {
file.close();
}
}
public void close() {
file_close();
}
@ExposedMethod(doc = BuiltinDocs.file___exit___doc)
final void file___exit__(PyObject type, PyObject value, PyObject traceback) {
close();
}
public void __exit__(PyObject type, PyObject value, PyObject traceback) {
file___exit__(type, value, traceback);
}
@ExposedMethod(defaults = {"null"}, doc = BuiltinDocs.file_truncate_doc)
final void file_truncate(PyObject position) {
if (position == null) {
file_truncate();
return;
}
file_truncate(position.asLong());
}
final synchronized void file_truncate(long position) {
file.truncate(position);
}
public void truncate(long position) {
file_truncate(position);
}
final synchronized void file_truncate() {
file.truncate(file.tell());
}
public void truncate() {
file_truncate();
}
public boolean isatty() {
return file_isatty();
}
@ExposedMethod(doc = BuiltinDocs.file_isatty_doc)
final boolean file_isatty() {
return file.isatty();
}
public PyObject fileno() {
return file_fileno();
}
@ExposedMethod(doc = BuiltinDocs.file_fileno_doc)
final PyObject file_fileno() {
return PyJavaType.wrapJavaObject(file.fileno());
}
@ExposedMethod(names = {"__str__", "__repr__"}, doc = BuiltinDocs.file___str___doc)
final String file_toString() {
String state = file.closed() ? "closed" : "open";
String id = Py.idstr(this);
String escapedName;
if (name instanceof PyUnicode) {
// unicode: always uses the format u'%s', and the escaped value thus:
escapedName = "u'"+PyString.encode_UnicodeEscape(name.toString(), false)+"'";
} else {
// anything else: uses repr(), which for str (common case) is smartly quoted
escapedName = name.__repr__().getString();
}
return String.format("<%s file %s, mode '%s' at %s>", state, escapedName, mode, id);
}
@Override
public String toString() {
return file_toString();
}
private void checkClosed() {
file.checkClosed();
}
@ExposedGet(name = "closed", doc = BuiltinDocs.file_closed_doc)
public boolean getClosed() {
return file.closed();
}
@ExposedGet(name = "newlines", doc = BuiltinDocs.file_newlines_doc)
public PyObject getNewlines() {
return file.getNewlines();
}
@ExposedGet(name = "softspace", doc = BuiltinDocs.file_softspace_doc)
public PyObject getSoftspace() {
// NOTE: not actual bools because CPython is this way
return softspace ? Py.One : Py.Zero;
}
@ExposedSet(name = "softspace")
public void setSoftspace(PyObject obj) {
softspace = obj.__nonzero__();
}
@ExposedDelete(name = "softspace")
public void delSoftspace() {
throw Py.TypeError("can't delete numeric/char attribute");
}
@Override
public Object __tojava__(Class> cls) {
Object obj = null;
if (InputStream.class.isAssignableFrom(cls)) {
obj = file.asInputStream();
} else if (OutputStream.class.isAssignableFrom(cls)) {
obj = file.asOutputStream();
}
if (obj == null) {
obj = super.__tojava__(cls);
}
return obj;
}
@Override
public void __del_builtin__() {
if (closer != null) {
closer.close();
}
}
/**
* XXX update docs - A mechanism to make sure PyFiles are closed on exit. On creation Closer adds itself
* to a list of Closers that will be run by PyFileCloser on JVM shutdown. When a
* PyFile's close or finalize methods are called, PyFile calls its Closer.close which
* clears Closer out of the shutdown queue.
*
* We use a regular object here rather than WeakReferences and their ilk as they may
* be collected before the shutdown hook runs. There's no guarantee that finalize will
* be called during shutdown, so we can't use it. It's vital that this Closer has no
* reference to the PyFile it's closing so the PyFile remains garbage collectable.
*/
private static class Closer implements Callable {
/**
* The underlying file
*/
private final TextIOBase file;
private PySystemState sys;
public Closer(TextIOBase file, PySystemState sys) {
this.file = file;
this.sys = sys;
sys.registerCloser(this);
}
/** For closing directly */
public void close() {
sys.unregisterCloser(this);
file.close();
sys = null;
}
/** For closing as part of a shutdown process */
@Override
public Void call() {
file.close();
sys = null;
return null;
}
}
/* Traverseproc implementation */
@Override
public int traverse(Visitproc visit, Object arg) {
return name == null ? 0 : visit.visit(name, arg);
}
@Override
public boolean refersDirectlyTo(PyObject ob) {
return ob != null && ob == name;
}
}