[Jython-checkins] jython: Javadoc and tests relevant to Unicode in PythonInterpreter.

jeff.allen jython-checkins at python.org
Wed Dec 31 02:41:07 CET 2014


https://hg.python.org/jython/rev/20e60a04d605
changeset:   7477:20e60a04d605
parent:      7432:7e2e9537565f
user:        Jeff Allen <ja.py at farowl.co.uk>
date:        Mon Dec 15 22:45:08 2014 +0000
summary:
  Javadoc and tests relevant to Unicode in PythonInterpreter.

Makes trivial (format only) changes to Java code and adds Javadoc clarifying the current behaviour of PythonInterpreter and its subclasses with respect to Unicode programs and Reader/Writer streams used as sys.stdin/out/err. Tests are added to exercise the behaviour, including 3 skipped tests where features may be lacking.

files:
  Lib/test/test_pythoninterpreter_jy.py           |  261 +++++++++-
  src/org/python/util/InteractiveConsole.java     |   59 +-
  src/org/python/util/InteractiveInterpreter.java |  137 +++-
  src/org/python/util/PythonInterpreter.java      |  195 ++++---
  4 files changed, 507 insertions(+), 145 deletions(-)


diff --git a/Lib/test/test_pythoninterpreter_jy.py b/Lib/test/test_pythoninterpreter_jy.py
--- a/Lib/test/test_pythoninterpreter_jy.py
+++ b/Lib/test/test_pythoninterpreter_jy.py
@@ -1,35 +1,56 @@
 # -*- coding: utf-8 -*-
-import java.io.StringWriter
+import java.io
 import sys
 import traceback
+import types
 import unittest
 import test.test_support
+from org.python.core.util import StringUtil
+from org.python.core import PyFile
+from _codecs import encode
+from sun.awt.image import BufImgVolatileSurfaceManager
 
-
-def exec_code_in_pi(function, out, err, locals=None):
+def exec_code_in_pi(source, inp=None, out=None, err=None, locals=None):
     """Runs code in a separate context: (thread, PySystemState, PythonInterpreter)"""
 
-    def function_context():
+    def execution_context():
         from org.python.core import Py
         from org.python.util import PythonInterpreter
         from org.python.core import PySystemState
 
         ps = PySystemState()
         pi = PythonInterpreter({}, ps)
-        if locals:
-            pi.setLocals(locals)
-        pi.setOut(out)
-        pi.setErr(err)
+        if locals is not None: pi.setLocals(locals)
+        if inp is not None: pi.setIn(inp)
+        if out is not None: pi.setOut(out)
+        if err is not None: pi.setErr(err)
         try:
-            pi.exec(function.func_code)
+            if isinstance(source, types.FunctionType):
+                # A function wrapping a compiled code block
+                pi.exec(source.func_code)
+
+            elif isinstance(source, java.io.InputStream):
+                # A byte-oriented file-like input stream
+                pi.execfile(source)
+
+            elif isinstance(source, java.io.Reader):
+                # A character-oriented file-like input stream
+                code = pi.compile(source)
+                pi.exec(code)
+
+            else:
+                # A str or unicode (see UnicodeSourceTest)
+                pi.exec(source)
+
         except:
+            print
             print '-'*60
             traceback.print_exc(file=sys.stdout)
             print '-'*60
 
 
     import threading
-    context = threading.Thread(target=function_context)
+    context = threading.Thread(target=execution_context)
     context.start()
     context.join()
 
@@ -54,17 +75,17 @@
                 print x
         out = java.io.StringWriter()
         err = java.io.StringWriter()
-        exec_code_in_pi(f, out, err, {'text': source_text})
+        exec_code_in_pi(f, None, out, err, {'text': source_text})
         output_text = out.toString().splitlines()
-        for source, output in zip(source_text, output_text):
-            self.assertEquals(source, output)
+        for output, source in zip(output_text, source_text):
+            self.assertEquals(output, source)
 
     def test_pi_out(self):
         def f():
             print 42
         out = java.io.StringWriter()
         err = java.io.StringWriter()
-        exec_code_in_pi(f, out, err)
+        exec_code_in_pi(f, None, out, err)
         self.assertEquals(u"42\n", out.toString())
 
     def test_more_output(self):
@@ -73,16 +94,224 @@
                 print "*" * i
         out = java.io.StringWriter()
         err = java.io.StringWriter()
-        exec_code_in_pi(f, out, err)
+        exec_code_in_pi(f, None, out, err)
         output = out.toString().splitlines()
         for i, line in enumerate(output):
             self.assertEquals(line, u'*' * i)
         self.assertEquals(42, len(output))
 
 
+class UnicodeSourceTest(unittest.TestCase):
+
+    # When the core PythonInterpreter is embedded in a Java program
+    # it may be supplied as Unicode source as a string or via streams.
+
+    def do_test(self, source, ref_out=u'', ref_var={}, inp=None):
+        out = java.io.StringWriter()
+        err = java.io.StringWriter()
+        var = {}
+        if inp is not None:
+            if isinstance(inp, bytes):
+                inp = java.io.ByteArrayInputStream(StringUtil.toBytes(inp))
+            elif isinstance(inp, unicode):
+                inp = java.io.StringReader(inp)
+
+        exec_code_in_pi(source, inp, out, err, var)
+        self.assertEquals(ref_var, var)
+        self.assertEquals(ref_out, out.toString())
+
+    def test_ascii_str(self):
+        # Program written in bytes with ascii range only
+        self.do_test('a = 42\nprint a', u'42\n', {'a':42})
+
+    def test_latin_str(self):
+        # Program written in bytes with codes above 127
+        self.do_test('a = "caf\xe9"\nprint a', u'caf\xe9\n', {'a':'caf\xe9'})
+
+    def test_ascii_unicode(self):
+        # Program written in Unicode with ascii range only
+        self.do_test(u'a = "hello"\nprint a', u'hello\n', {'a':'hello'})
+
+    def test_latin_unicode(self):
+        # Program written in Unicode with codes above 127
+       self.do_test(u'a = "caf\xe9"\nprint a', u'caf\xe9\n', {'a':'caf\xe9'})
+
+    @unittest.skip("PythonInterpreter.exec(String) does not distinguish str/unicode")
+    def test_bmp_unicode(self):
+        # Program written in Unicode with codes above 255
+        a = u"畫蛇添足 Λόγος"
+        prog = u'a = u"{:s}"\nprint repr(a)'.format(a)
+        # Submit via exec(unicode)
+        self.do_test(prog,
+                     u'{}\n'.format(repr(a)),
+                     {'a': a})
+
+    def test_bmp_utf8stream(self):
+        # Program written in Unicode with codes above 255
+        a = u"畫蛇添足 Λόγος"
+        prog = u'a = u"{:s}"\nprint repr(a)'.format(a)
+        # Program as bytes with declared encoding for execfile(InputStream)
+        progbytes = '# coding: utf-8\n' + prog.encode('utf-8')
+        stream = java.io.ByteArrayInputStream(StringUtil.toBytes(progbytes))
+        self.do_test(stream,
+                     u'{}\n'.format(repr(a)),
+                     {'a': a})
+
+    def test_bmp_reader(self):
+        # Program written in Unicode with codes above 255
+        a = u"畫蛇添足 Λόγος"
+        prog = u'a = u"{:s}"\nprint repr(a)'.format(a)
+        # Program as character stream for exec(compile(Reader))
+        self.do_test(java.io.StringReader(prog),
+                     u'{}\n'.format(repr(a)),
+                     {'a': a})
+
+def unicode_lines():
+    input_lines = [
+        u'Some text',
+        u'un café crème',
+        u"Λόγος",
+        u"畫蛇添足",
+        ]
+    input_text = u'\n'.join(input_lines)
+    return input_lines, input_text
+
+
+class InterpreterSetInTest(unittest.TestCase):
+
+    # When the core PythonInterpreter is embedded in a Java program it
+    # may be connected through SetIn to a Unicode or byte stream.
+    # However, the Unicode Reader interface narrows the data to bytes
+    # in a way that mangles anything beyond Latin-1. These tests
+    # illustrate that preparatory to a possible solution, in which the
+    # encoding is specified to the PythonInterpreter and appears as
+    # sys.stdin.encoding etc. for use by the application (and libraries).
+
+    @staticmethod
+    def do_read():
+        import sys
+        buf = bytearray()
+        while True:
+            c = sys.stdin.read(1)
+            if not c: break
+            buf.append(c)
+        # A defined encoding ought to be advertised in sys.stdin.encoding
+        enc = getattr(sys.stdin, 'encoding', None)
+        # In the test, allow an override via local variables
+        enc = locals().get('encoding', enc)
+        if enc:
+            result = buf.decode(enc) # unicode
+        else:
+            result = bytes(buf)
+
+    def test_pi_bytes_read(self):
+        # Test read() with pi.setIn(PyFile(InputStream))
+        input_lines, input_text = unicode_lines()
+        input_bytes = input_text.encode('utf-8')
+        inp = java.io.ByteArrayInputStream(input_bytes)
+        var = dict()
+        exec_code_in_pi(InterpreterSetInTest.do_read, inp, locals=var)
+        result = var['result']
+        self.assertEquals(result, input_bytes)
+        self.assertEquals(type(result), type(input_bytes))
+
+    @unittest.skip("Jython treats characters from a Reader as bytes.")
+    # Has no unicode encoding and fails to build PyString for codes > 255
+    def test_pi_unicode_read(self):
+        # Test read() with pi.setIn(Reader)
+        input_lines, input_text = unicode_lines()
+        inp = java.io.StringReader(input_text)
+        var = dict()
+        exec_code_in_pi(InterpreterSetInTest.do_read, inp, locals=var)
+        result = var['result']
+        self.assertEquals(result, input_text)
+        self.assertEquals(type(result), type(input_text))
+
+    def test_pi_encoding_read(self):
+        # Test read() with pi.setIn(PyFile(InputStream)) and defined encoding
+        input_lines, input_text = unicode_lines()
+        input_bytes = input_text.encode('utf-8')
+        inp = java.io.ByteArrayInputStream(input_bytes)
+        var = {'encoding': 'utf-8'}
+        exec_code_in_pi(InterpreterSetInTest.do_read, inp, locals=var)
+        result = var['result']
+        self.assertEquals(result, input_text)
+        self.assertEquals(type(result), type(input_text))
+
+    @staticmethod
+    def do_readline():
+        import sys
+        # A defined encoding ought to be advertised in sys.stdin.encoding
+        enc = getattr(sys.stdin, 'encoding', None)
+        # In the test, allow an override via local variables
+        enc = locals().get('encoding', enc)
+        result = list()
+        while True:
+            line = sys.stdin.readline()
+            if not line: break
+            if enc: line = line.decode(enc) # unicode
+            result.append(line.rstrip('\n'))
+
+    def test_pi_bytes_readline(self):
+        # Test readline() with pi.setIn(PyFile(InputStream))
+        input_lines, input_text = unicode_lines()
+        input_bytes = input_text.encode('utf-8')
+        inp = java.io.ByteArrayInputStream(input_bytes)
+        var = dict()
+        exec_code_in_pi(InterpreterSetInTest.do_readline, inp, locals=var)
+        for output, source in zip(var['result'], input_lines):
+            source = source.encode('utf-8')
+            self.assertEquals(output, source)
+            self.assertEquals(type(output), type(source))
+
+    @unittest.skip("Jython treats characters from a Reader as bytes.")
+    # Has no unicode encoding and fails to build PyString for codes > 255
+    def test_pi_unicode_readline(self):
+        # Test readline() with pi.setIn(Reader)
+        input_lines, input_text = unicode_lines()
+        inp = java.io.StringReader(input_text)
+        var = dict()
+        exec_code_in_pi(InterpreterSetInTest.do_readline, inp, locals=var)
+        for output, source in zip(var['result'], input_lines):
+            self.assertEquals(output, source)
+            self.assertEquals(type(output), type(source))
+
+    def test_pi_encoding_readline(self):
+        # Test readline() pi.setIn(PyFile(InputStream)) and defined encoding
+        input_lines, input_text = unicode_lines()
+        input_bytes = input_text.encode('utf-8')
+        inp = java.io.ByteArrayInputStream(input_bytes)
+        var = {'encoding': 'utf-8'}
+        exec_code_in_pi(InterpreterSetInTest.do_readline, inp, locals=var)
+        for output, source in zip(var['result'], input_lines):
+            self.assertEquals(output, source)
+            self.assertEquals(type(output), type(source))
+
+    @staticmethod
+    def do_readinto():
+        import sys
+        buf = bytearray(1024)
+        n = sys.stdin.readinto(buf)
+        result = buf[:n]
+
+    def test_pi_bytes_readinto(self):
+        # Test readinto() with pi.setIn(PyFile(InputStream))
+        input_lines, input_text = unicode_lines()
+        input_bytes = input_text.encode('utf-8')
+        inp = java.io.ByteArrayInputStream(input_bytes)
+        var = dict()
+        exec_code_in_pi(InterpreterSetInTest.do_readinto, inp, locals=var)
+        self.assertEquals(var['result'], input_bytes)
+
+    # There is no readinto() with pi.setIn(Reader)
+
 
 def test_main():
-    test.test_support.run_unittest(InterpreterTest)
+    test.test_support.run_unittest(
+            InterpreterTest,
+            UnicodeSourceTest,
+            InterpreterSetInTest,
+    )
 
 if __name__ == "__main__":
     test_main()
diff --git a/src/org/python/util/InteractiveConsole.java b/src/org/python/util/InteractiveConsole.java
--- a/src/org/python/util/InteractiveConsole.java
+++ b/src/org/python/util/InteractiveConsole.java
@@ -8,6 +8,16 @@
 import org.python.core.PySystemState;
 import org.python.core.__builtin__;
 
+/**
+ * This class provides the read, execute, print loop needed by a Python console; it is not actually
+ * a console itself. The primary capability is the {@link #interact()} method, which repeatedly
+ * calls {@link #raw_input(PyObject)}, and hence {@link __builtin__#raw_input(PyObject)}, in order
+ * to get lines, and {@link #push(String)} them into the interpreter. The built-in
+ * <code>raw_input()</code> method prompts on <code>sys.stdout</code> and reads from
+ * <code>sys.stdin</code>, the standard console. These may be redirected using
+ * {@link #setOut(java.io.OutputStream)} and {@link #setIn(java.io.InputStream)}, as may also
+ * <code>sys.stderr</code>.
+ */
 // Based on CPython-1.5.2's code module
 public class InteractiveConsole extends InteractiveInterpreter {
 
@@ -15,21 +25,43 @@
 
     public String filename;
 
+    /**
+     * Construct an interactive console, which will "run" when {@link #interact()} is called. The
+     * name of the console (e.g. in error messages) will be {@value #CONSOLE_FILENAME}.
+     */
     public InteractiveConsole() {
         this(null, CONSOLE_FILENAME);
     }
 
+    /**
+     * Construct an interactive console, which will "run" when {@link #interact()} is called. The
+     * name of the console (e.g. in error messages) will be {@value #CONSOLE_FILENAME}.
+     *
+     * @param locals dictionary to use, or if <code>null</code>, a new empty one will be created
+     */
     public InteractiveConsole(PyObject locals) {
         this(locals, CONSOLE_FILENAME);
     }
 
+    /**
+     * Construct an interactive console, which will "run" when {@link #interact()} is called.
+     *
+     * @param locals dictionary to use, or if <code>null</code>, a new empty one will be created
+     * @param filename name with which to label this console input (e.g. in error messages).
+     */
     public InteractiveConsole(PyObject locals, String filename) {
         this(locals, filename, false);
     }
 
     /**
-     * @param replaceRawInput if true, we hook this Class's raw_input into the built-ins table so
-     *            that clients like cmd.Cmd use it.
+     * Full-feature constructor for an interactive console, which will "run" when
+     * {@link #interact()} is called. This version allows the caller to replace the built-in
+     * raw_input() methods with {@link #raw_input(PyObject)} and
+     * {@link #raw_input(PyObject, PyObject)}, which may be overridden in a sub-class.
+     *
+     * @param locals dictionary to use, or if <code>null</code>, a new empty one will be created
+     * @param filename name with which to label this console input
+     * @param replaceRawInput if true, hook this class's <code>raw_input</code> into the built-ins.
      */
     public InteractiveConsole(PyObject locals, String filename, boolean replaceRawInput) {
         super(locals);
@@ -52,19 +84,32 @@
     }
 
     /**
-     * Closely emulate the interactive Python console.
-     *
-     * The optional banner argument specifies the banner to print before the first interaction; by
-     * default it prints "Jython <version> on <platform>".
+     * Operate a Python console, as in {@link #interact(String, PyObject)}, on the standard input.
+     * The standard input may have been redirected by {@link #setIn(java.io.InputStream)} or its
+     * variants. The banner (printed before first input) is obtained by calling
+     * {@link #getDefaultBanner()}.
      */
     public void interact() {
         interact(getDefaultBanner(), null);
     }
 
+    /**
+     * Returns the banner to print before the first interaction: "Jython <version> on <platform>".
+     *
+     * @return the banner.
+     */
     public static String getDefaultBanner() {
-        return String.format("Jython %s on %s", PySystemState.version, Py.getSystemState().platform);
+        return String
+                .format("Jython %s on %s", PySystemState.version, Py.getSystemState().platform);
     }
 
+    /**
+     * Operate a Python console by repeatedly calling {@link #raw_input(PyObject, PyObject)} and
+     * interpreting the lines read. An end of file causes the method to return.
+     *
+     * @param banner to print before accepting input, or if <code>null</code>, no banner.
+     * @param file from which to read commands, or if <code>null</code>, read the console.
+     */
     public void interact(String banner, PyObject file) {
         if (banner != null) {
             write(banner);
diff --git a/src/org/python/util/InteractiveInterpreter.java b/src/org/python/util/InteractiveInterpreter.java
--- a/src/org/python/util/InteractiveInterpreter.java
+++ b/src/org/python/util/InteractiveInterpreter.java
@@ -1,51 +1,96 @@
 // Copyright (c) Corporation for National Research Initiatives
 package org.python.util;
+
 import org.python.core.*;
 
+/**
+ * This class provides the interface for compiling and running code that supports an interactive
+ * interpreter.
+ */
 // Based on CPython-1.5.2's code module
+public class InteractiveInterpreter extends PythonInterpreter {
 
-public class InteractiveInterpreter extends PythonInterpreter {
+    /**
+     * Construct an InteractiveInterpreter with all default characteristics: default state (from
+     * {@link Py#getSystemState()}), and a new empty dictionary of local variables.
+     * */
     public InteractiveInterpreter() {
         this(null);
     }
+
+    /**
+     * Construct an InteractiveInterpreter with state (from {@link Py#getSystemState()}), and the
+     * specified dictionary of local variables.
+     *
+     * @param locals dictionary to use, or if <code>null</code>, a new empty one will be created
+     */
     public InteractiveInterpreter(PyObject locals) {
         this(locals, null);
-
     }
-        public InteractiveInterpreter(PyObject locals, PySystemState systemState) {
-            super(locals, systemState);
-        }
 
     /**
-     * Compile and run some source in the interpreter.
+     * Construct an InteractiveInterpreter with, and system state the specified dictionary of local
+     * variables.
      *
-     * Arguments are as for compile_command().
+     * @param locals dictionary to use, or if <code>null</code>, a new empty one will be created
+     * @param systemState interpreter state, or if <code>null</code> use {@link Py#getSystemState()}
+     */
+    public InteractiveInterpreter(PyObject locals, PySystemState systemState) {
+        super(locals, systemState);
+    }
+
+    /**
+     * Compile and run some source in the interpreter, in the mode {@link CompileMode#single} which
+     * is used for incremental compilation at the interactive console, known as "<input>".
      *
-     * One several things can happen:
-     *
-     * 1) The input is incorrect; compile_command() raised an exception
-     * (SyntaxError or OverflowError).  A syntax traceback will be printed
-     * by calling the showsyntaxerror() method.
-     *
-     * 2) The input is incomplete, and more input is required;
-     * compile_command() returned None.  Nothing happens.
-     *
-     * 3) The input is complete; compile_command() returned a code object.
-     * The code is executed by calling self.runcode() (which also handles
-     * run-time exceptions, except for SystemExit).
-     *
-     * The return value is 1 in case 2, 0 in the other cases (unless an
-     * exception is raised).  The return value can be used to decide
-     * whether to use sys.ps1 or sys.ps2 to prompt the next line.
-     **/
+     * @param source Python code
+     * @return <code>true</code> to indicate a partial statement was entered
+     */
     public boolean runsource(String source) {
         return runsource(source, "<input>", CompileMode.single);
     }
 
+    /**
+     * Compile and run some source in the interpreter, in the mode {@link CompileMode#single} which
+     * is used for incremental compilation at the interactive console.
+     *
+     * @param source Python code
+     * @param filename name with which to label this console input (e.g. in error messages).
+     * @return <code>true</code> to indicate a partial statement was entered
+     */
     public boolean runsource(String source, String filename) {
         return runsource(source, filename, CompileMode.single);
     }
 
+    /**
+     * Compile and run some source in the interpreter, according to the {@link CompileMode} given.
+     * This method supports incremental compilation and interpretation through the return value,
+     * where <code>true</code> signifies that more input is expected in order to complete the Python
+     * statement. An interpreter can use this to decide whether to use <code>sys.ps1</code> ("
+     * <code>>>> </code> ") or <code>sys.ps2</code> ("<code>... </code> ") to prompt the next line.
+     * The arguments are the same as the mandatory ones in the Python <code>compile()</code>
+     * command.
+     * <p>
+     * One the following can happen:
+     * <ol>
+     * <li>The input is incorrect; compilation raised an exception (SyntaxError or OverflowError). A
+     * syntax traceback will be printed by calling {@link #showexception(PyException)}. Return is
+     * <code>false</code>.</li>
+     *
+     * <li>The input is incomplete, and more input is required; compilation returned no code.
+     * Nothing happens. Return is <code>true</code>.</li>
+     *
+     * <li>The input is complete; compilation returned a code object. The code is executed by
+     * calling {@link #runcode(PyObject)} (which also handles run-time exceptions, except for
+     * SystemExit). Return is <code>false</code>.</li>
+     * </ol>
+     *
+     * @param source Python code
+     * @param filename name with which to label this console input (e.g. in error messages).
+     * @param kind of compilation required: {@link CompileMode#eval}, {@link CompileMode#exec} or
+     *            {@link CompileMode#single}
+     * @return <code>true</code> to indicate a partial statement was provided
+     */
     public boolean runsource(String source, String filename, CompileMode kind) {
         PyObject code;
         try {
@@ -64,23 +109,20 @@
             }
         }
         // Case 2
-        if (code == Py.None)
+        if (code == Py.None) {
             return true;
+        }
         // Case 3
         runcode(code);
         return false;
     }
 
     /**
-     * execute a code object.
-     *
-     * When an exception occurs, self.showtraceback() is called to display
-     * a traceback.  All exceptions are caught except SystemExit, which is
-     * reraised.
-     *
-     * A note about KeyboardInterrupt: this exception may occur elsewhere
-     * in this code, and may not always be caught.  The caller should be
-     * prepared to deal with it.
+     * Execute a code object. When an exception occurs, {@link #showexception(PyException)} is
+     * called to display a stack trace, except in the case of SystemExit, which is re-raised.
+     * <p>
+     * A note about KeyboardInterrupt: this exception may occur elsewhere in this code, and may not
+     * always be caught. The caller should be prepared to deal with it.
      **/
 
     // Make this run in another thread somehow????
@@ -88,7 +130,9 @@
         try {
             exec(code);
         } catch (PyException exc) {
-            if (exc.match(Py.SystemExit)) throw exc;
+            if (exc.match(Py.SystemExit)) {
+                throw exc;
+            }
             showexception(exc);
         }
     }
@@ -96,7 +140,7 @@
     public void showexception(PyException exc) {
         // Should probably add code to handle skipping top stack frames
         // somehow...
-        Py.printException(exc); 
+        Py.printException(exc);
     }
 
     public void write(String data) {
@@ -104,48 +148,55 @@
     }
 
     public StringBuffer buffer = new StringBuffer();
-    public String filename="<stdin>";
+    public String filename = "<stdin>";
 
     public void resetbuffer() {
         buffer.setLength(0);
     }
 
-    /** Pause the current code, sneak an exception raiser into
-     * sys.trace_func, and then continue the code hoping that Jython will
-     * get control to do the break;
+    /**
+     * Pause the current code, sneak an exception raiser into sys.trace_func, and then continue the
+     * code hoping that Jython will get control to do the break;
      **/
     public void interrupt(ThreadState ts) {
         TraceFunction breaker = new BreakTraceFunction();
         TraceFunction oldTrace = ts.tracefunc;
         ts.tracefunc = breaker;
-        if (ts.frame != null)
+        if (ts.frame != null) {
             ts.frame.tracefunc = breaker;
+        }
         ts.tracefunc = oldTrace;
-        //ts.thread.join();
+        // ts.thread.join();
     }
 }
 
+
 class BreakTraceFunction extends TraceFunction {
+
     private void doBreak() {
         throw new Error("Python interrupt");
-        //Thread.currentThread().interrupt();
+        // Thread.currentThread().interrupt();
     }
 
+    @Override
     public TraceFunction traceCall(PyFrame frame) {
         doBreak();
         return null;
     }
 
+    @Override
     public TraceFunction traceReturn(PyFrame frame, PyObject ret) {
         doBreak();
         return null;
     }
 
+    @Override
     public TraceFunction traceLine(PyFrame frame, int line) {
         doBreak();
         return null;
     }
 
+    @Override
     public TraceFunction traceException(PyFrame frame, PyException exc) {
         doBreak();
         return null;
diff --git a/src/org/python/util/PythonInterpreter.java b/src/org/python/util/PythonInterpreter.java
--- a/src/org/python/util/PythonInterpreter.java
+++ b/src/org/python/util/PythonInterpreter.java
@@ -25,8 +25,8 @@
 import org.python.core.PyFileReader;
 
 /**
- * The PythonInterpreter class is a standard wrapper for a Jython interpreter
- * for embedding in a Java application.
+ * The PythonInterpreter class is a standard wrapper for a Jython interpreter for embedding in a
+ * Java application.
  */
 public class PythonInterpreter implements AutoCloseable, Closeable {
 
@@ -37,6 +37,7 @@
     protected final boolean useThreadLocalState;
 
     protected static ThreadLocal<Object[]> threadLocals = new ThreadLocal<Object[]>() {
+
         @Override
         protected Object[] initialValue() {
             return new Object[1];
@@ -48,24 +49,18 @@
     private volatile boolean closed = false;
 
     /**
-     * Initializes the Jython runtime. This should only be called
-     * once, before any other Python objects (including
-     * PythonInterpreter) are created.
+     * Initializes the Jython runtime. This should only be called once, before any other Python
+     * objects (including PythonInterpreter) are created.
      *
-     * @param preProperties
-     *            A set of properties. Typically
-     *            System.getProperties() is used.  preProperties
-     *            override properties from the registry file.
-     * @param postProperties
-     *            Another set of properties. Values like python.home,
-     *            python.path and all other values from the registry
-     *            files can be added to this property
-     *            set. postProperties override system properties and
-     *            registry properties.
-     * @param argv
-     *            Command line arguments, assigned to sys.argv.
+     * @param preProperties A set of properties. Typically System.getProperties() is used.
+     *            preProperties override properties from the registry file.
+     * @param postProperties Another set of properties. Values like python.home, python.path and all
+     *            other values from the registry files can be added to this property set.
+     *            postProperties override system properties and registry properties.
+     * @param argv Command line arguments, assigned to sys.argv.
      */
-    public static void initialize(Properties preProperties, Properties postProperties, String[] argv) {
+    public static void
+            initialize(Properties preProperties, Properties postProperties, String[] argv) {
         PySystemState.initialize(preProperties, postProperties, argv);
     }
 
@@ -77,13 +72,10 @@
     }
 
     /**
-     * Creates a new interpreter with the ability to maintain a
-     * separate local namespace for each thread (set by invoking
-     * setLocals()).
+     * Creates a new interpreter with the ability to maintain a separate local namespace for each
+     * thread (set by invoking setLocals()).
      *
-     * @param dict
-     *            a Python mapping object (e.g., a dictionary) for use
-     *            as the default namespace
+     * @param dict a Python mapping object (e.g., a dictionary) for use as the default namespace
      */
     public static PythonInterpreter threadLocalStateInterpreter(PyObject dict) {
         return new PythonInterpreter(dict, null, true);
@@ -92,9 +84,7 @@
     /**
      * Creates a new interpreter with a specified local namespace.
      *
-     * @param dict 
-     *            a Python mapping object (e.g., a dictionary) for use
-     *            as the namespace
+     * @param dict a Python mapping object (e.g., a dictionary) for use as the namespace
      */
     public PythonInterpreter(PyObject dict) {
         this(dict, null);
@@ -104,14 +94,16 @@
         this(dict, systemState, false);
     }
 
-    protected PythonInterpreter(PyObject dict, PySystemState systemState, boolean useThreadLocalState) {
+    protected PythonInterpreter(PyObject dict, PySystemState systemState,
+            boolean useThreadLocalState) {
         if (dict == null) {
             dict = Py.newStringMap();
         }
         globals = dict;
 
-        if (systemState == null)
+        if (systemState == null) {
             systemState = Py.getSystemState();
+        }
         this.systemState = systemState;
         setSystemState();
 
@@ -120,7 +112,7 @@
             PyModule module = new PyModule("__main__", dict);
             systemState.modules.__setitem__("__main__", module);
         }
-        
+
         if (Options.importSite) {
             // Ensure site-packages are available
             imp.load("site");
@@ -136,59 +128,116 @@
     }
 
     /**
-     * Sets a Python object to use for the standard input stream.
+     * Sets a Python object to use for the standard input stream, <code>sys.stdin</code>. This
+     * stream is used in a byte-oriented way, through calls to <code>read</code> and
+     * <code>readline</code> on the object.
      *
-     * @param inStream
-     *            a Python file-like object to use as input stream
+     * @param inStream a Python file-like object to use as the input stream
      */
     public void setIn(PyObject inStream) {
         getSystemState().stdin = inStream;
     }
 
+    /**
+     * Sets a {@link Reader} to use for the standard input stream, <code>sys.stdin</code>. This
+     * stream is wrapped such that characters will be narrowed to bytes. A character greater than
+     * <code>U+00FF</code> will raise a Java <code>IllegalArgumentException</code> from within
+     * {@link PyString}.
+     *
+     * @param inStream to use as the input stream
+     */
     public void setIn(java.io.Reader inStream) {
         setIn(new PyFileReader(inStream));
     }
 
     /**
-     * Sets a java.io.InputStream to use for the standard input
-     * stream.
+     * Sets a {@link java.io.InputStream} to use for the standard input stream.
      *
-     * @param inStream
-     *            InputStream to use as input stream
+     * @param inStream InputStream to use as input stream
      */
     public void setIn(java.io.InputStream inStream) {
         setIn(new PyFile(inStream));
     }
 
     /**
-     * Sets a Python object to use for the standard output stream.
+     * Sets a Python object to use for the standard output stream, <code>sys.stdout</code>. This
+     * stream is used in a byte-oriented way (mostly) that depends on the type of file-like object.
+     * The behaviour as implemented is:
+     * <table border=1>
+     * <tr align=center>
+     * <td></td>
+     * <td colspan=3>Python type of object <code>o</code> written</td>
+     * </tr>
+     * <tr align=left>
+     * <th></th>
+     * <th><code>str/bytes</code></th>
+     * <th><code>unicode</code></th>
+     * <th>Any other type</th>
+     * </tr>
+     * <tr align=left>
+     * <th>{@link PyFile}</th>
+     * <td>as bytes directly</td>
+     * <td>respect {@link PyFile#encoding}</td>
+     * <td>call <code>str(o)</code> first</td>
+     * </tr>
+     * <tr align=left>
+     * <th>{@link PyFileWriter}</th>
+     * <td>each byte value as a <code>char</code></td>
+     * <td>write as Java <code>char</code>s</td>
+     * <td>call <code>o.toString()</code> first</td>
+     * </tr>
+     * <tr align=left>
+     * <th>Other {@link PyObject} <code>f</code></th>
+     * <td>invoke <code>f.write(str(o))</code></td>
+     * <td>invoke <code>f.write(o)</code></td>
+     * <td>invoke <code>f.write(str(o))</code></td>
+     * </tr>
+     * </table>
      *
-     * @param outStream
-     *            Python file-like object to use as output stream
+     * @param outStream Python file-like object to use as the output stream
      */
     public void setOut(PyObject outStream) {
         getSystemState().stdout = outStream;
     }
 
+    /**
+     * Sets a {@link Writer} to use for the standard output stream, <code>sys.stdout</code>. The
+     * behaviour as implemented is to output each object <code>o</code> by calling
+     * <code>o.toString()</code> and writing this as UTF-16.
+     *
+     * @param outStream to use as the output stream
+     */
     public void setOut(java.io.Writer outStream) {
         setOut(new PyFileWriter(outStream));
     }
 
     /**
-     * Sets a java.io.OutputStream to use for the standard output
-     * stream.
+     * Sets a {@link java.io.OutputStream} to use for the standard output stream.
      *
-     * @param outStream
-     *            OutputStream to use as output stream
+     * @param outStream OutputStream to use as output stream
      */
     public void setOut(java.io.OutputStream outStream) {
         setOut(new PyFile(outStream));
     }
 
+    /**
+     * Sets a Python object to use for the standard output stream, <code>sys.stderr</code>. This
+     * stream is used in a byte-oriented way (mostly) that depends on the type of file-like object,
+     * in the same way as {@link #setOut(PyObject)}.
+     *
+     * @param outStream Python file-like object to use as the error output stream
+     */
     public void setErr(PyObject outStream) {
         getSystemState().stderr = outStream;
     }
 
+    /**
+     * Sets a {@link Writer} to use for the standard output stream, <code>sys.stdout</code>. The
+     * behaviour as implemented is to output each object <code>o</code> by calling
+     * <code>o.toString()</code> and writing this as UTF-16.
+     *
+     * @param outStream to use as the error output stream
+     */
     public void setErr(java.io.Writer outStream) {
         setErr(new PyFileWriter(outStream));
     }
@@ -198,8 +247,7 @@
     }
 
     /**
-     * Evaluates a string as a Python expression and returns the
-     * result.
+     * Evaluates a string as a Python expression and returns the result.
      */
     public PyObject eval(String s) {
         setSystemState();
@@ -253,36 +301,35 @@
     }
 
     /**
-     * Compiles a string of Python source as either an expression (if
-     * possible) or a module.
+     * Compiles a string of Python source as either an expression (if possible) or a module.
      *
-     * Designed for use by a JSR 223 implementation: "the Scripting
-     * API does not distinguish between scripts which return values
-     * and those which do not, nor do they make the corresponding
-     * distinction between evaluating or executing objects."
-     * (SCR.4.2.1)
+     * Designed for use by a JSR 223 implementation: "the Scripting API does not distinguish between
+     * scripts which return values and those which do not, nor do they make the corresponding
+     * distinction between evaluating or executing objects." (SCR.4.2.1)
      */
     public PyCode compile(String script) {
         return compile(script, "<script>");
     }
+
     public PyCode compile(Reader reader) {
         return compile(reader, "<script>");
     }
+
     public PyCode compile(String script, String filename) {
         return compile(new StringReader(script), filename);
     }
+
     public PyCode compile(Reader reader, String filename) {
         mod node = ParserFacade.parseExpressionOrModule(reader, filename, cflags);
         setSystemState();
         return Py.compile_flags(node, filename, CompileMode.eval, cflags);
     }
 
-
     public PyObject getLocals() {
         if (!useThreadLocalState) {
             return globals;
         } else {
-            PyObject locals = (PyObject) threadLocals.get()[0];
+            PyObject locals = (PyObject)threadLocals.get()[0];
             if (locals != null) {
                 return locals;
             }
@@ -293,8 +340,7 @@
     public void setLocals(PyObject d) {
         if (!useThreadLocalState) {
             globals = d;
-        }
-        else {
+        } else {
             threadLocals.get()[0] = d;
         }
     }
@@ -302,11 +348,8 @@
     /**
      * Sets a variable in the local namespace.
      *
-     * @param name
-     *            the name of the variable
-     * @param value
-     *            the object to set the variable to (as converted to
-     *            an appropriate Python object)
+     * @param name the name of the variable
+     * @param value the object to set the variable to (as converted to an appropriate Python object)
      */
     public void set(String name, Object value) {
         getLocals().__setitem__(name.intern(), Py.java2py(value));
@@ -315,10 +358,8 @@
     /**
      * Sets a variable in the local namespace.
      *
-     * @param name
-     *            the name of the variable
-     * @param value
-     *            the Python object to set the variable to
+     * @param name the name of the variable
+     * @param value the Python object to set the variable to
      */
     public void set(String name, PyObject value) {
         getLocals().__setitem__(name.intern(), value);
@@ -327,10 +368,8 @@
     /**
      * Returns the value of a variable in the local namespace.
      *
-     * @param name
-     *            the name of the variable
-     * @return the value of the variable, or null if that name isn't
-     * assigned
+     * @param name the name of the variable
+     * @return the value of the variable, or null if that name isn't assigned
      */
     public PyObject get(String name) {
         return getLocals().__finditem__(name.intern());
@@ -338,17 +377,14 @@
 
     /**
      * Returns the value of a variable in the local namespace.
-     * 
+     *
      * The value will be returned as an instance of the given Java class.
-     * <code>interp.get("foo", Object.class)</code> will return the most
-     * appropriate generic Java object.
+     * <code>interp.get("foo", Object.class)</code> will return the most appropriate generic Java
+     * object.
      *
-     * @param name
-     *            the name of the variable
-     * @param javaclass
-     *            the class of object to return
-     * @return the value of the variable as the given class, or null
-     * if that name isn't assigned
+     * @param name the name of the variable
+     * @param javaclass the class of object to return
+     * @return the value of the variable as the given class, or null if that name isn't assigned
      */
     public <T> T get(String name, Class<T> javaclass) {
         PyObject val = getLocals().__finditem__(name.intern());
@@ -376,6 +412,7 @@
         sys.cleanup();
     }
 
+    @Override
     public void close() {
         if (!closed) {
             closed = true;

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


More information about the Jython-checkins mailing list