interactiveshell.py
465 lines
| 18.2 KiB
| text/x-python
|
PythonLexer
MinRK
|
r5600 | # -*- coding: utf-8 -*- | ||
MinRK
|
r10331 | """terminal client to the IPython kernel | ||
MinRK
|
r5600 | |||
""" | ||||
#----------------------------------------------------------------------------- | ||||
MinRK
|
r10331 | # Copyright (C) 2013 The IPython Development Team | ||
MinRK
|
r5600 | # | ||
# Distributed under the terms of the BSD License. The full license is in | ||||
# the file COPYING, distributed as part of this software. | ||||
#----------------------------------------------------------------------------- | ||||
#----------------------------------------------------------------------------- | ||||
# Imports | ||||
#----------------------------------------------------------------------------- | ||||
from __future__ import print_function | ||||
import bdb | ||||
MinRK
|
r5622 | import signal | ||
Takafumi Arakaki
|
r8244 | import os | ||
MinRK
|
r5600 | import sys | ||
MinRK
|
r5616 | import time | ||
Takafumi Arakaki
|
r8240 | import subprocess | ||
Takafumi Arakaki
|
r8239 | from io import BytesIO | ||
Takafumi Arakaki
|
r8238 | import base64 | ||
MinRK
|
r5600 | |||
from Queue import Empty | ||||
Takafumi Arakaki
|
r8245 | try: | ||
from contextlib import nested | ||||
except: | ||||
from IPython.utils.nested_context import nested | ||||
MinRK
|
r5600 | from IPython.core.alias import AliasManager, AliasError | ||
MinRK
|
r5616 | from IPython.core import page | ||
MinRK
|
r5600 | from IPython.utils.warn import warn, error, fatal | ||
from IPython.utils import io | ||||
MinRK
|
r10331 | from IPython.utils.traitlets import List, Enum, Any, Instance, Unicode | ||
Takafumi Arakaki
|
r8246 | from IPython.utils.tempdir import NamedFileInTemporaryDirectory | ||
MinRK
|
r5600 | |||
Fernando Perez
|
r11021 | from IPython.terminal.interactiveshell import TerminalInteractiveShell | ||
from IPython.terminal.console.completer import ZMQCompleter | ||||
MinRK
|
r5600 | |||
class ZMQTerminalInteractiveShell(TerminalInteractiveShell): | ||||
MinRK
|
r5616 | """A subclass of TerminalInteractiveShell that uses the 0MQ kernel""" | ||
_executing = False | ||||
Takafumi Arakaki
|
r8238 | |||
Takafumi Arakaki
|
r8240 | image_handler = Enum(('PIL', 'stream', 'tempfile', 'callable'), | ||
config=True, help= | ||||
Takafumi Arakaki
|
r8238 | """ | ||
Takafumi Arakaki
|
r8240 | Handler for image type output. This is useful, for example, | ||
Takafumi Arakaki
|
r8238 | when connecting to the kernel in which pylab inline backend is | ||
Takafumi Arakaki
|
r8240 | activated. There are four handlers defined. 'PIL': Use | ||
Python Imaging Library to popup image; 'stream': Use an | ||||
external program to show the image. Image will be fed into | ||||
the STDIN of the program. You will need to configure | ||||
`stream_image_handler`; 'tempfile': Use an external program to | ||||
show the image. Image will be saved in a temporally file and | ||||
the program is called with the temporally file. You will need | ||||
to configure `tempfile_image_handler`; 'callable': You can set | ||||
any Python callable which is called with the image data. You | ||||
will need to configure `callable_image_handler`. | ||||
""" | ||||
) | ||||
stream_image_handler = List(config=True, help= | ||||
""" | ||||
Command to invoke an image viewer program when you are using | ||||
'stream' image handler. This option is a list of string where | ||||
the first element is the command itself and reminders are the | ||||
options for the command. Raw image data is given as STDIN to | ||||
the program. | ||||
""" | ||||
) | ||||
tempfile_image_handler = List(config=True, help= | ||||
""" | ||||
Command to invoke an image viewer program when you are using | ||||
'tempfile' image handler. This option is a list of string | ||||
where the first element is the command itself and reminders | ||||
Takafumi Arakaki
|
r8242 | are the options for the command. You can use {file} and | ||
{format} in the string to represent the location of the | ||||
generated image file and image format. | ||||
Takafumi Arakaki
|
r8240 | """ | ||
) | ||||
callable_image_handler = Any(config=True, help= | ||||
""" | ||||
Callable object called via 'callable' image handler with one | ||||
argument, `data`, which is `msg["content"]["data"]` where | ||||
`msg` is the message from iopub channel. For exmaple, you can | ||||
Takafumi Arakaki
|
r8249 | find base64 encoded PNG data as `data['image/png']`. | ||
Takafumi Arakaki
|
r8238 | """ | ||
) | ||||
Takafumi Arakaki
|
r8243 | mime_preference = List( | ||
default_value=['image/png', 'image/jpeg', 'image/svg+xml'], | ||||
config=True, allow_none=False, help= | ||||
""" | ||||
Preferred object representation MIME type in order. First | ||||
matched MIME type will be used. | ||||
""" | ||||
) | ||||
MinRK
|
r10331 | manager = Instance('IPython.kernel.KernelManager') | ||
client = Instance('IPython.kernel.KernelClient') | ||||
def _client_changed(self, name, old, new): | ||||
self.session_id = new.session.session | ||||
session_id = Unicode() | ||||
MinRK
|
r5600 | def init_completer(self): | ||
"""Initialize the completion machinery. | ||||
This creates completion machinery that can be used by client code, | ||||
either interactively in-process (typically triggered by the readline | ||||
library), programatically (such as in test suites) or out-of-prcess | ||||
(typically over the network by remote frontends). | ||||
""" | ||||
from IPython.core.completerlib import (module_completer, | ||||
magic_run_completer, cd_completer) | ||||
MinRK
|
r10287 | self.Completer = ZMQCompleter(self, self.client) | ||
MinRK
|
r5600 | |||
self.set_hook('complete_command', module_completer, str_key = 'import') | ||||
self.set_hook('complete_command', module_completer, str_key = 'from') | ||||
self.set_hook('complete_command', magic_run_completer, str_key = '%run') | ||||
self.set_hook('complete_command', cd_completer, str_key = '%cd') | ||||
# Only configure readline if we truly are using readline. IPython can | ||||
# do tab-completion over the network, in GUIs, etc, where readline | ||||
# itself may be absent | ||||
if self.has_readline: | ||||
self.set_readline_completer() | ||||
def run_cell(self, cell, store_history=True): | ||||
"""Run a complete IPython cell. | ||||
Parameters | ||||
---------- | ||||
cell : str | ||||
The code (including IPython code such as %magic functions) to run. | ||||
store_history : bool | ||||
If True, the raw and translated cell will be stored in IPython's | ||||
history. For user code calling back into IPython's machinery, this | ||||
should be set to False. | ||||
""" | ||||
if (not cell) or cell.isspace(): | ||||
return | ||||
MinRK
|
r5632 | if cell.strip() == 'exit': | ||
# explicitly handle 'exit' command | ||||
return self.ask_exit() | ||||
MinRK
|
r5616 | self._executing = True | ||
# flush stale replies, which could have been ignored, due to missed heartbeats | ||||
MinRK
|
r10287 | while self.client.shell_channel.msg_ready(): | ||
self.client.shell_channel.get_msg() | ||||
MinRK
|
r5600 | # shell_channel.execute takes 'hidden', which is the inverse of store_hist | ||
MinRK
|
r10287 | msg_id = self.client.shell_channel.execute(cell, not store_history) | ||
while not self.client.shell_channel.msg_ready() and self.client.is_alive(): | ||||
MinRK
|
r5600 | try: | ||
self.handle_stdin_request(timeout=0.05) | ||||
except Empty: | ||||
MinRK
|
r5616 | # display intermediate print statements, etc. | ||
self.handle_iopub() | ||||
MinRK
|
r5600 | pass | ||
MinRK
|
r10287 | if self.client.shell_channel.msg_ready(): | ||
MinRK
|
r5616 | self.handle_execute_reply(msg_id) | ||
self._executing = False | ||||
MinRK
|
r5600 | |||
#----------------- | ||||
# message handlers | ||||
#----------------- | ||||
def handle_execute_reply(self, msg_id): | ||||
MinRK
|
r10287 | msg = self.client.shell_channel.get_msg() | ||
MinRK
|
r5616 | if msg["parent_header"].get("msg_id", None) == msg_id: | ||
self.handle_iopub() | ||||
content = msg["content"] | ||||
status = content['status'] | ||||
if status == 'aborted': | ||||
self.write('Aborted\n') | ||||
return | ||||
elif status == 'ok': | ||||
# print execution payloads as well: | ||||
for item in content["payload"]: | ||||
text = item.get('text', None) | ||||
if text: | ||||
page.page(text) | ||||
MinRK
|
r5600 | |||
MinRK
|
r5616 | elif status == 'error': | ||
for frame in content["traceback"]: | ||||
MinRK
|
r5600 | print(frame, file=io.stderr) | ||
MinRK
|
r5616 | self.execution_count = int(content["execution_count"] + 1) | ||
MinRK
|
r5600 | |||
def handle_iopub(self): | ||||
""" Method to procces subscribe channel's messages | ||||
This method reads a message and processes the content in different | ||||
outputs like stdout, stderr, pyout and status | ||||
Arguments: | ||||
sub_msg: message receive from kernel in the sub socket channel | ||||
capture by kernel manager. | ||||
""" | ||||
MinRK
|
r10287 | while self.client.iopub_channel.msg_ready(): | ||
sub_msg = self.client.iopub_channel.get_msg() | ||||
MinRK
|
r5600 | msg_type = sub_msg['header']['msg_type'] | ||
MinRK
|
r5631 | parent = sub_msg["parent_header"] | ||
if (not parent) or self.session_id == parent['session']: | ||||
MinRK
|
r5600 | if msg_type == 'status' : | ||
if sub_msg["content"]["execution_state"] == "busy" : | ||||
pass | ||||
elif msg_type == 'stream' : | ||||
if sub_msg["content"]["name"] == "stdout": | ||||
print(sub_msg["content"]["data"], file=io.stdout, end="") | ||||
io.stdout.flush() | ||||
elif sub_msg["content"]["name"] == "stderr" : | ||||
print(sub_msg["content"]["data"], file=io.stderr, end="") | ||||
io.stderr.flush() | ||||
Paul Ivanov
|
r5606 | |||
MinRK
|
r5600 | elif msg_type == 'pyout': | ||
Paul Ivanov
|
r5606 | self.execution_count = int(sub_msg["content"]["execution_count"]) | ||
MinRK
|
r5600 | format_dict = sub_msg["content"]["data"] | ||
Takafumi Arakaki
|
r8238 | self.handle_rich_data(format_dict) | ||
MinRK
|
r5600 | # taken from DisplayHook.__call__: | ||
hook = self.displayhook | ||||
hook.start_displayhook() | ||||
hook.write_output_prompt() | ||||
hook.write_format_data(format_dict) | ||||
hook.log_output(format_dict) | ||||
hook.finish_displayhook() | ||||
Takafumi Arakaki
|
r8238 | elif msg_type == 'display_data': | ||
self.handle_rich_data(sub_msg["content"]["data"]) | ||||
Takafumi Arakaki
|
r8242 | _imagemime = { | ||
'image/png': 'png', | ||||
'image/jpeg': 'jpeg', | ||||
'image/svg+xml': 'svg', | ||||
} | ||||
Takafumi Arakaki
|
r8238 | def handle_rich_data(self, data): | ||
Takafumi Arakaki
|
r8243 | for mime in self.mime_preference: | ||
if mime in data and mime in self._imagemime: | ||||
Takafumi Arakaki
|
r8240 | self.handle_image(data, mime) | ||
Takafumi Arakaki
|
r8241 | return | ||
Takafumi Arakaki
|
r8240 | |||
def handle_image(self, data, mime): | ||||
handler = getattr( | ||||
self, 'handle_image_{0}'.format(self.image_handler), None) | ||||
if handler: | ||||
handler(data, mime) | ||||
def handle_image_PIL(self, data, mime): | ||||
if mime not in ('image/png', 'image/jpeg'): | ||||
return | ||||
Takafumi Arakaki
|
r8258 | import PIL.Image | ||
Takafumi Arakaki
|
r8252 | raw = base64.decodestring(data[mime].encode('ascii')) | ||
Takafumi Arakaki
|
r8240 | img = PIL.Image.open(BytesIO(raw)) | ||
img.show() | ||||
def handle_image_stream(self, data, mime): | ||||
Takafumi Arakaki
|
r8252 | raw = base64.decodestring(data[mime].encode('ascii')) | ||
Takafumi Arakaki
|
r8242 | imageformat = self._imagemime[mime] | ||
fmt = dict(format=imageformat) | ||||
args = [s.format(**fmt) for s in self.stream_image_handler] | ||||
Takafumi Arakaki
|
r8244 | with open(os.devnull, 'w') as devnull: | ||
proc = subprocess.Popen( | ||||
args, stdin=subprocess.PIPE, | ||||
stdout=devnull, stderr=devnull) | ||||
proc.communicate(raw) | ||||
Takafumi Arakaki
|
r8240 | |||
def handle_image_tempfile(self, data, mime): | ||||
Takafumi Arakaki
|
r8252 | raw = base64.decodestring(data[mime].encode('ascii')) | ||
Takafumi Arakaki
|
r8242 | imageformat = self._imagemime[mime] | ||
Takafumi Arakaki
|
r8248 | filename = 'tmp.{0}'.format(imageformat) | ||
with nested(NamedFileInTemporaryDirectory(filename), | ||||
Takafumi Arakaki
|
r8245 | open(os.devnull, 'w')) as (f, devnull): | ||
Takafumi Arakaki
|
r8240 | f.write(raw) | ||
f.flush() | ||||
Takafumi Arakaki
|
r8242 | fmt = dict(file=f.name, format=imageformat) | ||
Takafumi Arakaki
|
r8240 | args = [s.format(**fmt) for s in self.tempfile_image_handler] | ||
Takafumi Arakaki
|
r8244 | subprocess.call(args, stdout=devnull, stderr=devnull) | ||
Takafumi Arakaki
|
r8240 | |||
def handle_image_callable(self, data, mime): | ||||
self.callable_image_handler(data) | ||||
Takafumi Arakaki
|
r8238 | |||
MinRK
|
r5600 | def handle_stdin_request(self, timeout=0.1): | ||
""" Method to capture raw_input | ||||
""" | ||||
MinRK
|
r10287 | msg_rep = self.client.stdin_channel.get_msg(timeout=timeout) | ||
MinRK
|
r5622 | # in case any iopub came while we were waiting: | ||
self.handle_iopub() | ||||
MinRK
|
r5631 | if self.session_id == msg_rep["parent_header"].get("session"): | ||
MinRK
|
r5622 | # wrap SIGINT handler | ||
real_handler = signal.getsignal(signal.SIGINT) | ||||
def double_int(sig,frame): | ||||
# call real handler (forwards sigint to kernel), | ||||
# then raise local interrupt, stopping local raw_input | ||||
real_handler(sig,frame) | ||||
raise KeyboardInterrupt | ||||
signal.signal(signal.SIGINT, double_int) | ||||
try: | ||||
raw_data = raw_input(msg_rep["content"]["prompt"]) | ||||
except EOFError: | ||||
# turn EOFError into EOF character | ||||
raw_data = '\x04' | ||||
except KeyboardInterrupt: | ||||
sys.stdout.write('\n') | ||||
return | ||||
finally: | ||||
# restore SIGINT handler | ||||
signal.signal(signal.SIGINT, real_handler) | ||||
# only send stdin reply if there *was not* another request | ||||
# or execution finished while we were reading. | ||||
MinRK
|
r10287 | if not (self.client.stdin_channel.msg_ready() or self.client.shell_channel.msg_ready()): | ||
self.client.stdin_channel.input(raw_data) | ||||
MinRK
|
r5600 | |||
def mainloop(self, display_banner=False): | ||||
while True: | ||||
try: | ||||
self.interact(display_banner=display_banner) | ||||
#self.interact_with_readline() | ||||
# XXX for testing of a readline-decoupled repl loop, call | ||||
# interact_with_readline above | ||||
break | ||||
except KeyboardInterrupt: | ||||
# this should not be necessary, but KeyboardInterrupt | ||||
# handling seems rather unpredictable... | ||||
self.write("\nKeyboardInterrupt in interact()\n") | ||||
MinRK
|
r5616 | def wait_for_kernel(self, timeout=None): | ||
"""method to wait for a kernel to be ready""" | ||||
tic = time.time() | ||||
MinRK
|
r10287 | self.client.hb_channel.unpause() | ||
MinRK
|
r5616 | while True: | ||
self.run_cell('1', False) | ||||
MinRK
|
r10287 | if self.client.hb_channel.is_beating(): | ||
MinRK
|
r5616 | # heart failure was not the reason this returned | ||
break | ||||
else: | ||||
# heart failed | ||||
if timeout is not None and (time.time() - tic) > timeout: | ||||
return False | ||||
return True | ||||
MinRK
|
r5600 | def interact(self, display_banner=None): | ||
"""Closely emulate the interactive Python console.""" | ||||
# batch run -> do not interact | ||||
if self.exit_now: | ||||
return | ||||
if display_banner is None: | ||||
display_banner = self.display_banner | ||||
if isinstance(display_banner, basestring): | ||||
self.show_banner(display_banner) | ||||
elif display_banner: | ||||
self.show_banner() | ||||
more = False | ||||
MinRK
|
r5616 | # run a non-empty no-op, so that we don't get a prompt until | ||
# we know the kernel is ready. This keeps the connection | ||||
# message above the first prompt. | ||||
if not self.wait_for_kernel(3): | ||||
error("Kernel did not respond\n") | ||||
return | ||||
MinRK
|
r5600 | if self.has_readline: | ||
self.readline_startup_hook(self.pre_readline) | ||||
MinRK
|
r5617 | hlen_b4_cell = self.readline.get_current_history_length() | ||
else: | ||||
hlen_b4_cell = 0 | ||||
MinRK
|
r5600 | # exit_now is set by a call to %Exit or %Quit, through the | ||
# ask_exit callback. | ||||
while not self.exit_now: | ||||
MinRK
|
r10287 | if not self.client.is_alive(): | ||
MinRK
|
r5616 | # kernel died, prompt for action or exit | ||
MinRK
|
r10287 | |||
action = "restart" if self.manager else "wait for restart" | ||||
MinRK
|
r5616 | ans = self.ask_yes_no("kernel died, %s ([y]/n)?" % action, default='y') | ||
if ans: | ||||
MinRK
|
r10287 | if self.manager: | ||
self.manager.restart_kernel(True) | ||||
MinRK
|
r5616 | self.wait_for_kernel(3) | ||
MinRK
|
r5600 | else: | ||
MinRK
|
r5616 | self.exit_now = True | ||
MinRK
|
r5600 | continue | ||
try: | ||||
MinRK
|
r5616 | # protect prompt block from KeyboardInterrupt | ||
# when sitting on ctrl-C | ||||
self.hooks.pre_prompt_hook() | ||||
if more: | ||||
try: | ||||
MinRK
|
r5628 | prompt = self.prompt_manager.render('in2') | ||
MinRK
|
r5616 | except Exception: | ||
self.showtraceback() | ||||
if self.autoindent: | ||||
self.rl_do_indent = True | ||||
else: | ||||
try: | ||||
MinRK
|
r5628 | prompt = self.separate_in + self.prompt_manager.render('in') | ||
MinRK
|
r5616 | except Exception: | ||
self.showtraceback() | ||||
MinRK
|
r5600 | line = self.raw_input(prompt) | ||
if self.exit_now: | ||||
# quick exit on sys.std[in|out] close | ||||
break | ||||
if self.autoindent: | ||||
self.rl_do_indent = False | ||||
except KeyboardInterrupt: | ||||
#double-guard against keyboardinterrupts during kbdint handling | ||||
try: | ||||
self.write('\nKeyboardInterrupt\n') | ||||
MinRK
|
r5617 | source_raw = self.input_splitter.source_raw_reset()[1] | ||
hlen_b4_cell = self._replace_rlhist_multiline(source_raw, hlen_b4_cell) | ||||
MinRK
|
r5600 | more = False | ||
except KeyboardInterrupt: | ||||
pass | ||||
except EOFError: | ||||
if self.autoindent: | ||||
self.rl_do_indent = False | ||||
if self.has_readline: | ||||
self.readline_startup_hook(None) | ||||
self.write('\n') | ||||
self.exit() | ||||
except bdb.BdbQuit: | ||||
warn('The Python debugger has exited with a BdbQuit exception.\n' | ||||
'Because of how pdb handles the stack, it is impossible\n' | ||||
'for IPython to properly format this particular exception.\n' | ||||
'IPython will resume normal operation.') | ||||
except: | ||||
# exceptions here are VERY RARE, but they can be triggered | ||||
# asynchronously by signal handlers, for example. | ||||
self.showtraceback() | ||||
else: | ||||
self.input_splitter.push(line) | ||||
more = self.input_splitter.push_accepts_more() | ||||
if (self.SyntaxTB.last_syntax_error and | ||||
self.autoedit_syntax): | ||||
self.edit_syntax_error() | ||||
if not more: | ||||
Paul Ivanov
|
r5646 | source_raw = self.input_splitter.source_raw_reset()[1] | ||
MinRK
|
r5617 | hlen_b4_cell = self._replace_rlhist_multiline(source_raw, hlen_b4_cell) | ||
MinRK
|
r5600 | self.run_cell(source_raw) | ||
# Turn off the exit flag, so the mainloop can be restarted if desired | ||||
self.exit_now = False | ||||