visualizer: Restore ipython support

Changes are based on [1].

Dropped compatibility code for ipython versions < 5.0.0.

[1] 68e4f4bbec/plugins/ipython_view.py
This commit is contained in:
André Apitzsch
2022-09-20 16:53:16 +02:00
committed by Gabriel Ferreira
parent 11a6696ead
commit b8d8c64e0e
2 changed files with 143 additions and 79 deletions

View File

@@ -69,7 +69,7 @@ else:
_import_error = None
try:
import ipython_viewxxxxxxxxxx
from . import ipython_view
except ImportError:
ipython_view = None

View File

@@ -13,15 +13,21 @@ is available at U{http://www.opensource.org/licenses/bsd-license.php}
# this file is a modified version of source code from the Accerciser project
# https://wiki.gnome.org/Apps/Accerciser
import gtk, gobject
import gi
gi.require_version('Gtk', '3.0')
from gi.repository import Gtk
from gi.repository import Gdk
from gi.repository import GLib
from gi.repository import Pango
from pkg_resources import parse_version
import re
import sys
import os
from gi.repository import Pango
from StringIO import StringIO
import IPython
from pkg_resources import parse_version
from io import StringIO
from functools import reduce
## Try to import IPython
try:
@@ -41,6 +47,12 @@ class IterableIPShell:
# history level
## @var complete_sep
# separators
## @var no_input_splitter
# no input splitter
## @var lines
# lines
## @var indent_spaces
# indent spaces
## @var prompt
# prompt
## @var header
@@ -52,8 +64,8 @@ class IterableIPShell:
## @var raw_input
# raw input
## Constructor
def __init__(self,argv=None,user_ns=None,user_global_ns=None,
cin=None, cout=None,cerr=None, input_func=None):
def __init__(self, argv=[], user_ns=None, user_global_ns=None,
cin=None, cout=None, cerr=None, input_func=None):
"""! Initializer
@param self: this object
@@ -67,45 +79,47 @@ class IterableIPShell:
"""
io = IPython.utils.io
if input_func:
if parse_version(IPython.release.version) >= parse_version("1.2.1"):
IPython.terminal.interactiveshell.raw_input_original = input_func
else:
IPython.frontend.terminal.interactiveshell.raw_input_original = input_func
if cin:
io.stdin = io.IOStream(cin)
if cout:
io.stdout = io.IOStream(cout)
if cerr:
io.stderr = io.IOStream(cerr)
IPython.terminal.interactiveshell.raw_input_original = input_func
if IPython.version_info < (8,):
if cin:
io.stdin = io.IOStream(cin)
if cout:
io.stdout = io.IOStream(cout)
if cerr:
io.stderr = io.IOStream(cerr)
else:
if cin:
sys.stdin = cin
if cout:
sys.stdout = cout
if cerr:
sys.stderr = cerr
# This is to get rid of the blockage that occurs during
# IPython.Shell.InteractiveShell.user_setup()
io.raw_input = lambda x: None
os.environ['TERM'] = 'dumb'
excepthook = sys.excepthook
from IPython.config.loader import Config
from traitlets.config.loader import Config
cfg = Config()
cfg.InteractiveShell.colors = "Linux"
cfg.Completer.use_jedi = False
# InteractiveShell's __init__ overwrites io.stdout,io.stderr with
# sys.stdout, sys.stderr, this makes sure they are right
#
old_stdout, old_stderr = sys.stdout, sys.stderr
sys.stdout, sys.stderr = io.stdout.stream, io.stderr.stream
if IPython.version_info < (8,):
# InteractiveShell's __init__ overwrites io.stdout,io.stderr with
# sys.stdout, sys.stderr, this makes sure they are right
old_stdout, old_stderr = sys.stdout, sys.stderr
sys.stdout, sys.stderr = io.stdout.stream, io.stderr.stream
# InteractiveShell inherits from SingletonConfigurable, so use instance()
#
if parse_version(IPython.release.version) >= parse_version("1.2.1"):
self.IP = IPython.terminal.embed.InteractiveShellEmbed.instance(\
config=cfg, user_ns=user_ns)
else:
self.IP = IPython.frontend.terminal.embed.InteractiveShellEmbed.instance(\
self.IP = IPython.terminal.embed.InteractiveShellEmbed.instance(\
config=cfg, user_ns=user_ns)
sys.stdout, sys.stderr = old_stdout, old_stderr
if IPython.version_info < (8,):
sys.stdout, sys.stderr = old_stdout, old_stderr
self.IP.system = lambda cmd: self.shell(self.IP.var_expand(cmd),
header='IPython system call: ')
@@ -120,11 +134,17 @@ class IterableIPShell:
self.complete_sep = re.compile('[\s\{\}\[\]\(\)]')
self.updateNamespace({'exit':lambda:None})
self.updateNamespace({'quit':lambda:None})
self.IP.readline_startup_hook(self.IP.pre_readline)
# Workaround for updating namespace with sys.modules
#
self.__update_namespace()
# Avoid using input splitter when not really needed.
# Perhaps it could work even before 5.8.0
# But it definitely does not work any more with >= 7.0.0
self.no_input_splitter = parse_version(IPython.release.version) >= parse_version('5.8.0')
self.lines = []
self.indent_spaces = ''
def __update_namespace(self):
"""!
Update self.IP namespace for autocompletion with sys.modules
@@ -140,11 +160,17 @@ class IterableIPShell:
@return none
"""
self.history_level = 0
orig_stdout = sys.stdout
sys.stdout = IPython.utils.io.stdout
orig_stdin = sys.stdin
sys.stdin = IPython.utils.io.stdin;
if IPython.version_info < (8,):
# this is needed because some functions in IPython use 'print' to print
# output (like 'who')
orig_stdout = sys.stdout
sys.stdout = IPython.utils.io.stdout
orig_stdin = sys.stdin
sys.stdin = IPython.utils.io.stdin
self.prompt = self.generatePrompt(self.iter_more)
self.IP.hooks.pre_prompt_hook()
@@ -160,21 +186,26 @@ class IterableIPShell:
line = self.IP.raw_input(self.prompt)
except KeyboardInterrupt:
self.IP.write('\nKeyboardInterrupt\n')
self.IP.input_splitter.reset()
if self.no_input_splitter:
self.lines = []
else:
self.IP.input_splitter.reset()
except:
self.IP.showtraceback()
else:
self.IP.input_splitter.push(line)
self.iter_more = self.IP.input_splitter.push_accepts_more()
self.prompt = self.generatePrompt(self.iter_more)
if (self.IP.SyntaxTB.last_syntax_error and
self.IP.autoedit_syntax):
self.IP.edit_syntax_error()
if self.no_input_splitter:
self.lines.append(line)
(status, self.indent_spaces) = self.IP.check_complete('\n'.join(self.lines))
self.iter_more = status == 'incomplete'
else:
self.IP.input_splitter.push(line)
self.iter_more = self.IP.input_splitter.push_accepts_more()
if not self.iter_more:
if parse_version(IPython.release.version) >= parse_version("2.0.0-dev"):
source_raw = self.IP.input_splitter.raw_reset()
if self.no_input_splitter:
source_raw = '\n'.join(self.lines)
self.lines = []
else:
source_raw = self.IP.input_splitter.source_raw_reset()[1]
source_raw = self.IP.input_splitter.raw_reset()
self.IP.run_cell(source_raw, store_history=True)
self.IP.rl_do_indent = False
else:
@@ -182,9 +213,11 @@ class IterableIPShell:
#
self.IP.rl_do_indent = True
pass
self.prompt = self.generatePrompt(self.iter_more)
sys.stdout = orig_stdout
sys.stdin = orig_stdin
if IPython.version_info < (8,):
sys.stdout = orig_stdout
sys.stdin = orig_stdin
def generatePrompt(self, is_continuation):
"""!
@@ -195,17 +228,10 @@ class IterableIPShell:
"""
# Backwards compatibility with ipyton-0.11
#
ver = IPython.__version__
if '0.11' in ver:
prompt = self.IP.hooks.generate_prompt(is_continuation)
if is_continuation:
prompt = '... '
else:
if is_continuation:
prompt = self.IP.prompt_manager.render('in2')
else:
prompt = self.IP.prompt_manager.render('in')
prompt = '>>> '
return prompt
@@ -281,7 +307,7 @@ class IterableIPShell:
return str1[:i]
return str1
if possibilities[1]:
common_prefix = reduce(_commonPrefix, possibilities[1]) or line[-1]
common_prefix = reduce(_commonPrefix, possibilities[1]) or split_line[-1]
completed = line[:-len(split_line[-1])]+common_prefix
else:
completed = line
@@ -324,7 +350,8 @@ class ConsoleView(Gtk.TextView):
"""
Specialized text view for console-like workflow.
@cvar ANSI_COLORS: Mapping of terminal colors to X11 names.
@cvar ANSI_COLORS: Mapping of terminal control sequence values to
tuples containing foreground and background color names.
@type ANSI_COLORS: dictionary
@ivar text_buffer: Widget's text buffer.
@@ -336,20 +363,35 @@ class ConsoleView(Gtk.TextView):
@ivar line_start: Start of command line mark.
@type line_start: Gtk.TextMark
"""
ANSI_COLORS = {'0;30': 'Black', '0;31': 'Red',
'0;32': 'Green', '0;33': 'Brown',
'0;34': 'Blue', '0;35': 'Purple',
'0;36': 'Cyan', '0;37': 'LightGray',
'1;30': 'DarkGray', '1;31': 'DarkRed',
'1;32': 'SeaGreen', '1;33': 'Yellow',
'1;34': 'LightBlue', '1;35': 'MediumPurple',
'1;36': 'LightCyan', '1;37': 'White'}
ANSI_COLORS = {'0;30': ('Black', None),
'0;31': ('Red', None),
'0;32': ('Green', None),
'0;33': ('Brown', None),
'0;34': ('Blue', None),
'0;35': ('Purple', None),
'0;36': ('Cyan', None),
'0;37': ('LightGray', None),
'1;30': ('DarkGray', None),
'1;31': ('DarkRed', None),
'1;32': ('SeaGreen', None),
'1;33': ('Yellow', None),
'1;34': ('LightBlue', None),
'1;35': ('MediumPurple', None),
'1;36': ('LightCyan', None),
'1;37': ('White', None),
'38;5;124;43': ('DarkRed', 'Yellow'),
'38;5;241': ('Gray', None),
'38;5;241;43': ('Gray', 'Yellow'),
'39': ('Black', None),
'39;49': ('Red', 'White'),
'43': (None, 'Yellow'),
'49': (None, 'White')}
def __init__(self):
"""
Initialize console view.
"""
GObject.GObject.__init__(self)
Gtk.TextView.__init__(self)
self.modify_font(Pango.FontDescription('Mono'))
self.set_cursor_visible(True)
self.text_buffer = self.get_buffer()
@@ -358,7 +400,8 @@ class ConsoleView(Gtk.TextView):
False)
for code in self.ANSI_COLORS:
self.text_buffer.create_tag(code,
foreground=self.ANSI_COLORS[code],
foreground=self.ANSI_COLORS[code][0],
background=self.ANSI_COLORS[code][1],
weight=700)
self.text_buffer.create_tag('0')
self.text_buffer.create_tag('notouch', editable=False)
@@ -376,7 +419,7 @@ class ConsoleView(Gtk.TextView):
@param editable: If true, added text is editable.
@return none
"""
GObject.idle_add(self._write, text, editable)
GLib.idle_add(self._write, text, editable)
def _write(self, text, editable=False):
"""!
@@ -414,7 +457,7 @@ class ConsoleView(Gtk.TextView):
@param prompt: Prompt to print.
@return none
"""
GObject.idle_add(self._showPrompt, prompt)
GLib.idle_add(self._showPrompt, prompt)
def _showPrompt(self, prompt):
"""!
@@ -434,7 +477,7 @@ class ConsoleView(Gtk.TextView):
@param text: Text to use as replacement.
@return none
"""
GObject.idle_add(self._changeLine, text)
GLib.idle_add(self._changeLine, text)
def _changeLine(self, text):
"""!
@@ -466,7 +509,7 @@ class ConsoleView(Gtk.TextView):
@param text: Text to show.
@return none
"""
GObject.idle_add(self._showReturned, text)
GLib.idle_add(self._showReturned, text)
def _showReturned(self, text):
"""!
@@ -485,11 +528,14 @@ class ConsoleView(Gtk.TextView):
if text:
self._write('\n')
self._showPrompt(self.prompt)
self.text_buffer.move_mark(self.line_start,self.text_buffer.get_end_iter())
self.text_buffer.move_mark(self.line_start, self.text_buffer.get_end_iter())
self.text_buffer.place_cursor(self.text_buffer.get_end_iter())
if self.IP.rl_do_indent:
indentation = self.IP.input_splitter.indent_spaces * ' '
if self.no_input_splitter:
indentation = self.indent_spaces
else:
indentation = self.IP.input_splitter.indent_spaces * ' '
self.text_buffer.insert_at_cursor(indentation)
def onKeyPress(self, widget, event):
@@ -508,9 +554,9 @@ class ConsoleView(Gtk.TextView):
selection_iter = self.text_buffer.get_iter_at_mark(selection_mark)
start_iter = self.text_buffer.get_iter_at_mark(self.line_start)
if event.keyval == Gdk.KEY_Home:
if event.get_state() & Gdk.ModifierType.CONTROL_MASK or event.get_state() & Gdk.ModifierType.MOD1_MASK:
if event.state & Gdk.ModifierType.CONTROL_MASK or event.state & Gdk.ModifierType.MOD1_MASK:
pass
elif event.get_state() & Gdk.ModifierType.SHIFT_MASK:
elif event.state & Gdk.ModifierType.SHIFT_MASK:
self.text_buffer.move_mark(insert_mark, start_iter)
return True
else:
@@ -520,6 +566,24 @@ class ConsoleView(Gtk.TextView):
insert_iter.backward_cursor_position()
if not insert_iter.editable(True):
return True
elif event.state & Gdk.ModifierType.CONTROL_MASK and event.keyval in [ord('L'), ord('l')]:
# clear previous output on Ctrl+L, but remember current input line + cursor position
cursor_offset = self.text_buffer.get_property('cursor-position')
cursor_pos_in_line = cursor_offset - start_iter.get_offset() + len(self.prompt)
current_input = self.text_buffer.get_text(start_iter, self.text_buffer.get_end_iter(), False)
self.text_buffer.set_text(self.prompt + current_input)
self.text_buffer.move_mark(self.line_start, self.text_buffer.get_iter_at_offset(len(self.prompt)))
self.text_buffer.place_cursor(self.text_buffer.get_iter_at_offset(cursor_pos_in_line))
return True
elif event.state & Gdk.ModifierType.CONTROL_MASK and event.keyval in [Gdk.KEY_k, Gdk.KEY_K]:
# clear text after input cursor on Ctrl+K
if insert_iter.editable(True):
self.text_buffer.delete(insert_iter, self.text_buffer.get_end_iter())
return True
elif event.state & Gdk.ModifierType.CONTROL_MASK and event.keyval == Gdk.KEY_C:
# copy selection on Ctrl+C (upper-case 'C' only)
self.text_buffer.copy_clipboard(Gtk.Clipboard.get(Gdk.SELECTION_CLIPBOARD))
return True
elif not event.string:
pass
elif start_iter.compare(insert_iter) <= 0 and \
@@ -569,7 +633,7 @@ class IPythonView(ConsoleView, IterableIPShell):
"""
ConsoleView.__init__(self)
self.cout = StringIO()
IterableIPShell.__init__(self, cout=self.cout,cerr=self.cout,
IterableIPShell.__init__(self, cout=self.cout, cerr=self.cout,
input_func=self.raw_input)
self.interrupt = False
self.execute()