Index: src/org/python/jsr223/PyScriptEngine.java =================================================================== --- src/org/python/jsr223/PyScriptEngine.java (revision 6594) +++ src/org/python/jsr223/PyScriptEngine.java (working copy) @@ -35,6 +35,7 @@ private Object eval(PyCode code, ScriptContext context) throws ScriptException { try { + interp.setIn(context.getReader()); interp.setOut(context.getWriter()); interp.setErr(context.getErrorWriter()); return interp.eval(code).__tojava__(Object.class); Index: src/org/python/core/PyFileReader.java =================================================================== --- src/org/python/core/PyFileReader.java (revision 0) +++ src/org/python/core/PyFileReader.java (revision 0) @@ -0,0 +1,173 @@ +package org.python.core; + +import java.io.Reader; +import java.io.IOException; +import java.io.BufferedReader; + + +public class PyFileReader extends PyObject +{ + static final int DEFAULT_BUF_SIZE = 1024; + + private final BufferedReader reader; + private boolean closed; + + private char[] reuseableBuffer = null; + + public PyFileReader(Reader reader) + { + this.reader = (reader instanceof BufferedReader) ? (BufferedReader) reader : new BufferedReader(reader); + closed = false; + } + + public boolean closed() + { + return closed; + } + + public void checkClosed() + { + if (closed()) { + throw Py.ValueError("I/O operation on closed file"); + } + } + + public synchronized void flush() + { + checkClosed(); + } + + public void close() + { + try { + if (!closed()) { + reader.close(); + closed = true; + } + } catch (IOException e) { + throw Py.IOError(e); + } + } + + protected char[] needBuffer(int size) + { + if (reuseableBuffer == null) { + if (size > DEFAULT_BUF_SIZE) + return new char[size]; + + reuseableBuffer = new char[DEFAULT_BUF_SIZE]; + } + + if (size <= reuseableBuffer.length) + return reuseableBuffer; + + return new char[size]; + } + + public PyString read(int n) + { + if (n < 0) { + synchronized(reader) { + checkClosed(); + + final StringBuilder sb = new StringBuilder(); + + final char[] cbuf = needBuffer(DEFAULT_BUF_SIZE); + final int buflen = cbuf.length; + + while (true) { + try { + final int x = reader.read(cbuf, 0, buflen); + + if (x < 0) + break; + + sb.append(cbuf, 0, x); + + if (x < buflen) + break; + } catch (IOException e) { + throw Py.IOError(e); + } + } + + return new PyString(sb.toString()); + } + } + + synchronized(reader) { + checkClosed(); + + final char[] cbuf = needBuffer(n); + final int buflen = cbuf.length; + + try { + final int x = reader.read(cbuf, 0, n); + + if (x < 1) + return new PyString(""); + + return new PyString(new String(cbuf, 0, x)); + } catch (IOException e) { + throw Py.IOError(e); + } + } + } + + public PyString read() + { + return read(-1); + } + + public PyString readline(int max) + { + if (!(max < 0)) { + //TODO: Warn that we aren't really supporting this? + } + + synchronized (reader) { + try { + final String line = reader.readLine(); + + if (line == null) { + return new PyString(""); + } else { + return new PyString(line + "\n"); + } + } catch (IOException e) { + throw Py.IOError(e); + } + } + } + + public PyString readline() + { + return readline(-1); + } + + public PyObject readlines(final int sizehint) { + synchronized (reader) { + checkClosed(); + final PyList list = new PyList(); + int size = 0; + do { + final PyString line = readline(-1); + int len = line.string.length(); + if (len == 0) { + // EOF + break; + } + size += len; + list.append(line); + } while (sizehint <= 0 || size < sizehint); + + return list; + } + } + + public PyObject readlines() { + return readlines(0); + } + + +} Index: src/org/python/util/PythonInterpreter.java =================================================================== --- src/org/python/util/PythonInterpreter.java (revision 6594) +++ src/org/python/util/PythonInterpreter.java (working copy) @@ -19,6 +19,7 @@ import org.python.core.PyStringMap; import org.python.core.PySystemState; import org.python.core.__builtin__; +import org.python.core.PyFileReader; /** * The PythonInterpreter class is a standard wrapper for a Jython interpreter for use embedding in a @@ -88,6 +89,32 @@ } /** + * Set the Python object to use for the standard input stream + * + * @param inStream + * Python file-like object to use as input stream + */ + public void setIn(PyObject inStream) { + systemState.stdin = inStream; + } + + /** @deprecated */ + @Deprecated + public void setIn(java.io.Reader inStream) { + setIn(new PyFileReader(inStream)); + } + + /** + * Set a java.io.InputStream to use for the standard input stream + * + * @param inStream + * InputStream to use as input stream + */ + public void setIn(java.io.InputStream inStream) { + setIn(new PyFile(inStream)); + } + + /** * Set the Python object to use for the standard output stream * * @param outStream Index: tests/java/org/python/jsr223/ScriptEngineIOTest.java =================================================================== --- tests/java/org/python/jsr223/ScriptEngineIOTest.java (revision 0) +++ tests/java/org/python/jsr223/ScriptEngineIOTest.java (revision 0) @@ -0,0 +1,81 @@ +package org.python.jsr223; + +import javax.script.ScriptEngine; +import javax.script.ScriptException; +import javax.script.ScriptEngineFactory; + +import junit.framework.TestCase; + +import java.io.StringReader; +import java.io.StringWriter; + +public class ScriptEngineIOTest extends TestCase +{ + ScriptEngineFactory pythonEngineFactory; + ScriptEngine pythonEngine; + + public void setUp() throws ScriptException + { + pythonEngineFactory = new PyScriptEngineFactory(); + pythonEngine = new PyScriptEngine(pythonEngineFactory); + } + + public void testEvalString() throws ScriptException + { + assertNull(pythonEngine.eval("x = 5")); + assertEquals(Integer.valueOf(5), pythonEngine.eval("x")); + } + + public void testReadline() throws ScriptException + { + final String testString = "Shazaam Batman!\n"; + + pythonEngine.getContext().setReader(new StringReader(testString)); + + assertNull(pythonEngine.eval("import sys")); + assertEquals(testString, pythonEngine.eval("sys.stdin.readline()")); + } + + public void testReadlines() throws ScriptException + { + final String testString = "Holy Smokes Batman!\nBIF!\r\n\nKAPOW!!!\rTHE END."; + + pythonEngine.getContext().setReader(new StringReader(testString)); + + assertNull(pythonEngine.eval("import sys")); + final Object o = pythonEngine.eval("''.join(sys.stdin.readlines())"); + + assertEquals("Holy Smokes Batman!\nBIF!\n\nKAPOW!!!\nTHE END.\n", o); + } + + public void testWriter() throws ScriptException + { + final StringWriter sw = new StringWriter(); + + pythonEngine.getContext().setWriter(sw); + + final String testString = "It is a wonderful world."; + + assertNull(pythonEngine.eval("print '" + testString + "',")); + assertEquals(testString, sw.toString()); + } + + public void testErrorWriter() throws ScriptException + { + final StringWriter stdout = new StringWriter(); + final StringWriter stderr = new StringWriter(); + + pythonEngine.getContext().setWriter(stdout); + pythonEngine.getContext().setErrorWriter(stderr); + + final String testString1 = "It is a wonderful world."; + final String testString2 = "Stuff happens!"; + + assertNull(pythonEngine.eval("import sys")); + assertNull(pythonEngine.eval("sys.stdout.write ('" + testString1 + "')")); + assertNull(pythonEngine.eval("sys.stderr.write ('" + testString2 + "')")); + + assertEquals(testString1, stdout.toString()); + assertEquals(testString2, stderr.toString()); + } +}