Skip to content

Commit e5ea4ff

Browse files
committed
Added JLineConsole to provide readline like functionality through JLine.
Moved the raw_input replacement out of ReadlineConsole into InteractiveConsole so the JLine and Readline consoles could share it.
1 parent df77de7 commit e5ea4ff

File tree

5 files changed

+142
-79
lines changed

5 files changed

+142
-79
lines changed

NEWS

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@ Jython 2.2 beta2
66
functions
77
- Classmethods added to newstyle classes
88
- array is a newstyle class
9+
- org.python.util.JLineConsole provides readline-like functionality with
10+
JLine without requiring native readline
911

1012
Bugs fixed
1113
- [ 1599012 ] current directory is prepended to entries in sys.path

build.xml

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,9 @@ servlet.jar=c:/workspace/HEAD/CH.obj.commons.core/make_lib/j2ee.jar
9191

9292
# - org.gnu.readline
9393
readline.jar=${basedir}/../externals/external-jars/readline.jar
94+
# - jline
95+
jline.jar=${basedir}/../externals/external-jars/jline-0.9.91.jar
96+
9497

9598
# - zxJDBC
9699
# (for mysql, see www.mysql.com/downloads/api-jdbc-stable.html)
@@ -172,6 +175,7 @@ svnant.jar.dir=${basedir}/../externals/svnant-jars
172175
<!-- classpaths -->
173176
<path id="main.classpath">
174177
<pathelement path="${readline.jar}" />
178+
<pathelement path="${jline.jar}" />
175179
<pathelement path="${servlet.jar}" />
176180
<pathelement path="${informix.jar}" />
177181
<pathelement path="${oracle.jar}" />
@@ -186,6 +190,7 @@ svnant.jar.dir=${basedir}/../externals/svnant-jars
186190
<available property="secureclassloader.present" classname="java.security.SecureClassLoader" />
187191
<available property="servlet.present" classname="javax.servlet.Servlet" classpath="${servlet.jar}" />
188192
<available property="readline.present" classname="org.gnu.readline.Readline" classpath="${readline.jar}" />
193+
<available property="jline.present" classname="jline.Terminal" classpath="${jline.jar}" />
189194
<available property="informix.present" classname="com.informix.jdbc.IfxDriver" classpath="${informix.jar}" />
190195
<available property="mysql.present" classname="org.gjt.mm.mysql.Driver" classpath="${mysql.jar}" />
191196
<available property="postgresql.present" classname="org.postgresql.Driver" classpath="${postgresql.jar}" />
@@ -247,6 +252,7 @@ svnant.jar.dir=${basedir}/../externals/svnant-jars
247252
<taskdef name="svn" classname="org.tigris.subversion.svnant.SvnTask" classpathref="svn.classpath" />
248253
<!-- Require all of the optional jars for a full build -->
249254
<fail unless="readline.present" message="readline jar not present" />
255+
<fail unless="jline.present" message="jline jar not present" />
250256
<fail unless="servlet.present" message="servlet jar not present" />
251257
<fail unless="informix.present" message="informix jar not present" />
252258
<fail unless="mysql.present" message="mysql jar not present" />
@@ -268,6 +274,7 @@ svnant.jar.dir=${basedir}/../externals/svnant-jars
268274
<echo>secureclassloader = '${secureclassloader.present}'</echo>
269275
<echo>servlet = '${servlet.present}'</echo>
270276
<echo>readline = '${readline.present}'</echo>
277+
<echo>jline = '${jline.present}'</echo>
271278
<echo>oracle = '${oracle.present}'</echo>
272279
<echo>informix = '${informix.present}'</echo>
273280
<echo>mysql = '${mysql.present}'</echo>
@@ -436,6 +443,7 @@ svnant.jar.dir=${basedir}/../externals/svnant-jars
436443
<exclude name="org/python/parser/python.java" />
437444
<exclude name="**/PyServlet.java" unless="servlet.present" />
438445
<exclude name="**/ReadlineConsole.java" unless="readline.present" />
446+
<exclude name="**/JLineConsole.java" unless="jline.present" />
439447
<exclude name="**/handler/InformixDataHandler.java" unless="informix.present" />
440448
<exclude name="**/handler/MySQLDataHandler.java" unless="mysql.present" />
441449
<exclude name="**/handler/OracleDataHandler.java" unless="oracle.present" />
@@ -503,6 +511,7 @@ svnant.jar.dir=${basedir}/../externals/svnant-jars
503511
<attribute name="secureclassloader" value="${secureclassloader.present}" />
504512
<attribute name="servlet" value="${servlet.present}" />
505513
<attribute name="readline" value="${readline.present}" />
514+
<attribute name="jline" value="${jline.present}" />
506515
<attribute name="oracle" value="${oracle.present}" />
507516
<attribute name="informix" value="${informix.present}" />
508517
<attribute name="mysql" value="${mysql.present}" />
@@ -674,6 +683,7 @@ svnant.jar.dir=${basedir}/../externals/svnant-jars
674683
<attribute name="secureclassloader" value="${secureclassloader.present}" />
675684
<attribute name="servlet" value="${servlet.present}" />
676685
<attribute name="readline" value="${readline.present}" />
686+
<attribute name="jline" value="${jline.present}" />
677687
<attribute name="oracle" value="${oracle.present}" />
678688
<attribute name="informix" value="${informix.present}" />
679689
<attribute name="mysql" value="${mysql.present}" />

src/org/python/util/InteractiveConsole.java

Lines changed: 62 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -1,57 +1,85 @@
11
// Copyright (c) Corporation for National Research Initiatives
22
package org.python.util;
3-
import org.python.core.*;
43

5-
// Based on CPython-1.5.2's code module
4+
import org.python.core.Py;
5+
import org.python.core.PyBuiltinFunctionSet;
6+
import org.python.core.PyException;
7+
import org.python.core.PyObject;
8+
import org.python.core.PySystemState;
9+
import org.python.core.__builtin__;
610

11+
// Based on CPython-1.5.2's code module
712
public class InteractiveConsole extends InteractiveInterpreter {
13+
14+
public static final String CONSOLE_FILENAME = "<console>";
15+
816
public String filename;
917

1018
public InteractiveConsole() {
11-
this(null, "<console>");
19+
this(null, CONSOLE_FILENAME);
1220
}
21+
1322
public InteractiveConsole(PyObject locals) {
14-
this(locals, "<console>");
23+
this(locals, CONSOLE_FILENAME);
1524
}
25+
1626
public InteractiveConsole(PyObject locals, String filename) {
27+
this(locals, filename, false);
28+
}
29+
30+
/**
31+
* @param replaceRawInput -
32+
* if true, we hook this Class's raw_input into the builtins
33+
* table so that clients like cmd.Cmd use it.
34+
*/
35+
public InteractiveConsole(PyObject locals, String filename, boolean replaceRawInput) {
1736
super(locals);
1837
this.filename = filename;
38+
if(replaceRawInput) {
39+
PyObject newRawInput = new PyBuiltinFunctionSet("raw_input", 0, 0, 1) {
40+
41+
public PyObject __call__() {
42+
return __call__(Py.EmptyString);
43+
}
44+
45+
public PyObject __call__(PyObject prompt) {
46+
return Py.newString(raw_input(prompt));
47+
}
48+
};
49+
Py.getSystemState().builtins.__setitem__("raw_input", newRawInput);
50+
}
1951
}
2052

2153
/**
2254
* Closely emulate the interactive Python console.
23-
*
24-
* The optional banner argument specifies the banner to print before
25-
* the first interaction; by default it prints a banner similar to the
26-
* one printed by the real Python interpreter, followed by the current
27-
* class name in parentheses (so as not to confuse this with the real
28-
* interpreter -- since it's so close!).
29-
**/
55+
*
56+
* The optional banner argument specifies the banner to print before the
57+
* first interaction; by default it prints "Jython <version> on <platform>".
58+
*/
3059
public void interact() {
3160
interact(getDefaultBanner());
3261
}
3362

3463
public static String getDefaultBanner() {
35-
return "Jython " + PySystemState.version + " on "
36-
+ PySystemState.platform;
64+
return "Jython " + PySystemState.version + " on " + PySystemState.platform;
3765
}
3866

3967
public void interact(String banner) {
40-
if (banner != null) {
68+
if(banner != null) {
4169
write(banner);
4270
write("\n");
4371
}
4472
// Dummy exec in order to speed up response on first command
4573
exec("2");
46-
//System.err.println("interp2");
74+
// System.err.println("interp2");
4775
boolean more = false;
48-
while (true) {
76+
while(true) {
4977
PyObject prompt = more ? systemState.ps2 : systemState.ps1;
5078
String line;
5179
try {
5280
line = raw_input(prompt);
53-
} catch (PyException exc) {
54-
if (!Py.matchException(exc, Py.EOFError))
81+
} catch(PyException exc) {
82+
if(!Py.matchException(exc, Py.EOFError))
5583
throw exc;
5684
write("\n");
5785
break;
@@ -62,36 +90,35 @@ public void interact(String banner) {
6290

6391
/**
6492
* Push a line to the interpreter.
65-
*
93+
*
6694
* The line should not have a trailing newline; it may have internal
67-
* newlines. The line is appended to a buffer and the interpreter's
68-
* runsource() method is called with the concatenated contents of the
69-
* buffer as source. If this indicates that the command was executed
70-
* or invalid, the buffer is reset; otherwise, the command is
71-
* incomplete, and the buffer is left as it was after the line was
72-
* appended. The return value is 1 if more input is required, 0 if the
73-
* line was dealt with in some way (this is the same as runsource()).
74-
**/
75-
95+
* newlines. The line is appended to a buffer and the interpreter's
96+
* runsource() method is called with the concatenated contents of the buffer
97+
* as source. If this indicates that the command was executed or invalid,
98+
* the buffer is reset; otherwise, the command is incomplete, and the buffer
99+
* is left as it was after the line was appended. The return value is 1 if
100+
* more input is required, 0 if the line was dealt with in some way (this is
101+
* the same as runsource()).
102+
*/
76103
public boolean push(String line) {
77-
if (buffer.length() > 0)
104+
if(buffer.length() > 0)
78105
buffer.append("\n");
79106
buffer.append(line);
80107
boolean more = runsource(buffer.toString(), filename);
81-
if (!more)
108+
if(!more)
82109
resetbuffer();
83110
return more;
84111
}
85112

86113
/**
87114
* Write a prompt and read a line.
88-
*
89-
* The returned line does not include the trailing newline. When the
90-
* user enters the EOF key sequence, EOFError is raised.
91-
*
115+
*
116+
* The returned line does not include the trailing newline. When the user
117+
* enters the EOF key sequence, EOFError is raised.
118+
*
92119
* The base implementation uses the built-in function raw_input(); a
93120
* subclass may replace this with a different implementation.
94-
**/
121+
*/
95122
public String raw_input(PyObject prompt) {
96123
return __builtin__.raw_input(prompt);
97124
}
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
package org.python.util;
2+
3+
import java.io.IOException;
4+
import jline.ConsoleReader;
5+
import jline.Terminal;
6+
import org.python.core.Py;
7+
import org.python.core.PyObject;
8+
9+
/**
10+
* This class uses <a href="http://jline.sourceforge.net/">JLine</a> to provide
11+
* readline like functionality to its console without requiring native readline
12+
* support.
13+
*/
14+
public class JLineConsole extends InteractiveConsole {
15+
16+
public JLineConsole() {
17+
this(null);
18+
}
19+
20+
public JLineConsole(PyObject locals) {
21+
this(locals, CONSOLE_FILENAME);
22+
}
23+
24+
public JLineConsole(PyObject locals, String filename) {
25+
super(locals, filename, true);
26+
Terminal.setupTerminal();
27+
try {
28+
reader = new ConsoleReader();
29+
} catch(IOException e) {
30+
throw new RuntimeException(e);
31+
}
32+
}
33+
34+
public String raw_input(PyObject prompt) {
35+
String line = null;
36+
try {
37+
line = reader.readLine(prompt.toString());
38+
} catch(IOException io) {
39+
throw Py.IOError(io);
40+
}
41+
if(line == null) {
42+
throw Py.EOFError("Ctrl-D exit");
43+
}
44+
return line.endsWith("\n") ? line.substring(0, line.length() - 1) : line;
45+
}
46+
47+
private ConsoleReader reader;
48+
}

src/org/python/util/ReadlineConsole.java

Lines changed: 20 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -3,81 +3,57 @@
33

44
import org.gnu.readline.Readline;
55
import org.gnu.readline.ReadlineLibrary;
6-
import org.python.core.ArgParser;
76
import org.python.core.Py;
87
import org.python.core.PyException;
98
import org.python.core.PyObject;
10-
import org.python.core.PyString;
119
import org.python.core.PySystemState;
1210

1311
/**
14-
* Uses:
15-
* <a href="http://java-readline.sourceforge.net/">Java Readline</a>
16-
* <p/>
17-
*
12+
* Uses: <a href="http://java-readline.sourceforge.net/">Java Readline</a> <p/>
13+
*
1814
* Based on CPython-1.5.2's code module
19-
*
15+
*
2016
*/
2117
public class ReadlineConsole extends InteractiveConsole {
18+
2219
public String filename;
2320

2421
public ReadlineConsole() {
25-
this(null, "<console>");
22+
this(null, CONSOLE_FILENAME);
2623
}
24+
2725
public ReadlineConsole(PyObject locals) {
28-
this(locals, "<console>");
26+
this(locals, CONSOLE_FILENAME);
2927
}
28+
3029
public ReadlineConsole(PyObject locals, String filename) {
31-
super(locals,filename);
32-
String backingLib = PySystemState.registry.getProperty(
33-
"python.console.readlinelib", "Editline");
30+
super(locals, filename, true);
31+
String backingLib = PySystemState.registry.getProperty("python.console.readlinelib",
32+
"Editline");
3433
try {
3534
Readline.load(ReadlineLibrary.byName(backingLib));
36-
} catch (RuntimeException e) {
35+
} catch(RuntimeException e) {
3736
// Silently ignore errors during load of the native library.
3837
// Will use a pure java fallback.
3938
}
40-
41-
// hook into the builtins table so that clients like cmd.Cmd can
42-
// also use readline.
43-
Py.getSystemState().builtins.__setitem__("raw_input",
44-
Py.newJavaFunc(this.getClass(), "_raw_input"));
45-
4639
Readline.initReadline("jython");
4740
}
4841

49-
5042
/**
5143
* Write a prompt and read a line.
52-
*
53-
* The returned line does not include the trailing newline. When the
54-
* user enters the EOF key sequence, EOFError is raised.
55-
*
44+
*
45+
* The returned line does not include the trailing newline. When the user
46+
* enters the EOF key sequence, EOFError is raised.
47+
*
5648
* This subclass implements the functionality using JavaReadline.
57-
**/
49+
*/
5850
public String raw_input(PyObject prompt) {
59-
return _raw_input(new PyObject[] { prompt }, new String[0]);
60-
}
61-
62-
/**
63-
* Central point of dispatch to Readline library for all clients,
64-
* whether the console itself or others like cmd.Cmd interpreters.
65-
* Both of these uses come through here.
66-
*
67-
* @param args should contain a single prompt
68-
* @param kws keywords
69-
* @return the user input
70-
**/
71-
public static String _raw_input(PyObject args[], String kws[]) {
72-
ArgParser ap = new ArgParser("raw_input", args, kws, "prompt");
73-
PyObject prompt = ap.getPyObject(0, new PyString(""));
7451
try {
75-
String line = Readline.readline(
76-
prompt==null ? "" : prompt.toString());
52+
String line = Readline.readline(prompt == null ? "" : prompt.toString());
7753
return (line == null ? "" : line);
78-
} catch (java.io.EOFException eofe) {
54+
} catch(java.io.EOFException eofe) {
7955
throw new PyException(Py.EOFError);
80-
} catch (java.io.IOException e) {
56+
} catch(java.io.IOException e) {
8157
throw new PyException(Py.IOError);
8258
}
8359
}

0 commit comments

Comments
 (0)