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

org.python.util.JLineConsole 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.util;

import java.io.EOFException;
import java.io.File;
import java.io.FileDescriptor;
import java.io.FileInputStream;
import java.io.FilterInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.PrintStream;
import java.util.Arrays;
import java.util.List;

import jline.console.ConsoleReader;
import jline.WindowsTerminal;
import jline.console.history.FileHistory;
import jnr.constants.platform.Errno;

import org.python.core.PlainConsole;
import org.python.core.PyObject;
import org.python.core.Py;

/**
 * This class uses JLine to provide readline like
 * functionality to its console without requiring native readline support.
 */
public class JLineConsole extends PlainConsole {

    /** Main interface to JLine. */
    public ConsoleReader reader;
    /** Callable object set by readline.set_startup_hook. */
    protected PyObject startup_hook;
    /** Not currently set by readline.set_pre_input_hook. Why not? */
    protected PyObject pre_input_hook;
    /** Whether reader is a WindowsTerminal. */
    private boolean windows;
    /** The ctrl-z character String. */
    protected static final String CTRL_Z = "\u001a";
    /** Stream wrapping System.out in order to capture the last prompt. */
    private ConsoleOutputStream outWrapper;

    /**
     * Errno strerrors possibly caused by a SIGSTP (ctrl-z). They may propagate up to IOException
     * messages.
     */
    private static final List SUSPENDED_STRERRORS = Arrays.asList(
            Errno.EINTR.description(), Errno.EIO.description());

    /**
     * Construct an instance of the console class specifying the character encoding. This encoding
     * must be one supported by the JVM.
     * 

* Most of the initialisation is deferred to the {@link #install()} method so that any prior * console can uninstall itself before we change system console settings and * System.in. * * @param encoding name of a supported encoding or null for * Charset.defaultCharset() */ public JLineConsole(String encoding) { /* * Super-class needs the encoding in order to re-encode the characters that * jline.ConsoleReader.readLine() has decoded. */ super(encoding); /* * Communicate the specified encoding to JLine. jline.ConsoleReader.readLine() edits a line * of characters, decoded from stdin. */ System.setProperty("jline.WindowsTerminal.input.encoding", this.encoding); System.setProperty("input.encoding", this.encoding); // ... not "jline.UnixTerminal.input.encoding" as you might think, not even in JLine2 } private static class HistoryCloser implements Runnable { FileHistory history; public HistoryCloser(FileHistory history) { this.history = history; } @Override public void run() { try { history.flush(); } catch (IOException e) { // could not save console history, but quietly ignore in this case } } } /** * {@inheritDoc} *

* This implementation overrides that by setting System.in to a * FilterInputStream object that wraps JLine, and System.out * with a layer that buffers incomplete lines for use as the console prompt. */ @Override public void install() { String userHomeSpec = System.getProperty("user.home", "."); // Configure a ConsoleReader (the object that does most of the line editing). try { // Create the reader as unbuffered as possible InputStream in = new FileInputStream(FileDescriptor.in); reader = new ConsoleReader("jython", in, System.out, null, encoding); reader.setKeyMap("jython"); reader.setHandleUserInterrupt(true); reader.setCopyPasteDetection(true); // We find the bell too noisy reader.setBellEnabled(false); // Do not attempt to expand ! in the input reader.setExpandEvents(false); /* * Everybody else, using sys.stdout or java.lang.System.out, gets to write on a special * PrintStream that keeps the last incomplete line in case it turns out to be a console * prompt. */ outWrapper = new ConsoleOutputStream(System.out, reader.getTerminal().getWidth()); System.setOut(new PrintStream(outWrapper, true, encoding)); } catch (IOException e) { throw new RuntimeException(e); } // Access and load (if possible) the line history. try { File historyFile = new File(userHomeSpec, ".jline-jython.history"); FileHistory history = new FileHistory(historyFile); Runtime.getRuntime().addShutdownHook(new Thread(new HistoryCloser(history))); reader.setHistory(history); } catch (IOException e) { // oh well, no history from file } // Check for OS type windows = reader.getTerminal() instanceof WindowsTerminal; // Replace System.in FilterInputStream wrapper = new Stream(); System.setIn(wrapper); } /** * Class to wrap the line-oriented interface to JLine with an InputStream that can replace * System.in. */ private class Stream extends ConsoleInputStream { /** Create a System.in replacement with JLine that adds system-specific line endings */ Stream() { super(System.in, encodingCharset, EOLPolicy.ADD, LINE_SEPARATOR); } @Override protected CharSequence getLine() throws IOException, EOFException { // Get a line and hope to be done. The prompt is the current partial output line. String prompt = outWrapper.getPrompt(encodingCharset).toString(); String line = readerReadLine(prompt); if (!isEOF(line)) { return line; } else { // null or ctrl-z on Windows indicates EOF throw new EOFException(); } } } /** * Wrapper on reader.readLine(prompt) that deals with retries (on Unix) when the user enters * ctrl-Z to background Jython, then brings it back to the foreground. The inherited * implementation says this is necessary and effective on BSD Unix. * * @param prompt to display * @return line of text read in * @throws IOException if an error occurs (other than an end of suspension) * @throws EOFException if an EOF is detected */ private String readerReadLine(String prompt) throws IOException, EOFException { // We must be prepared to try repeatedly since the read may be interrupted. while (true) { try { // If there's a hook, call it if (startup_hook != null) { startup_hook.__call__(); } try { // Resumption from control-Z suspension may occur without JLine telling us // Work around by putting the terminal into a well-known state before // each read line, if possible reader.getTerminal().init(); } catch (Exception exc) {} // Send the cursor to the start of the line (no prompt, empty buffer). reader.setPrompt(null); reader.redrawLine(); // The prompt is whatever was already on the line. return reader.readLine(prompt); } catch (IOException ioe) { // Something went wrong, or we were interrupted (seems only BSD throws this) if (!fromSuspend(ioe)) { // The interruption is not the result of (the end of) a ctrl-Z suspension throw ioe; } else { // The interruption seems to be (return from) a ctrl-Z suspension: try { // Must reset JLine and continue (not repeating the prompt) reader.resetPromptLine (prompt, null, 0); prompt = ""; } catch (Exception e) { // Do our best to say what went wrong throw new IOException("Failed to re-initialize JLine: " + e.getMessage()); } } } } } /** * Determine if the IOException was likely caused by a SIGSTP (ctrl-z). Seems only applicable to * BSD platforms. */ private boolean fromSuspend(IOException ioe) { return !windows && SUSPENDED_STRERRORS.contains(ioe.getMessage()); } /** * Determine if line denotes an EOF. */ private boolean isEOF(String line) { return line == null || (windows && CTRL_Z.equals(line)); } /** * @return the JLine console reader associated with this interpreter */ public ConsoleReader getReader() { return reader; } /** * @return the startup hook (called prior to each readline) */ public PyObject getStartupHook() { return startup_hook; } /** * Sets the startup hook (called prior to each readline) */ public void setStartupHook(PyObject hook) { // Convert None to null here, so that readerReadLine can use only a null check if (hook == Py.None) { hook = null; } startup_hook = hook; } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy