interactiveshell.py
342 lines
| 13.6 KiB
| text/x-python
|
PythonLexer
MinRK
|
r5600 | # -*- coding: utf-8 -*- | ||
"""Frontend of ipython working with python-zmq | ||||
Ipython's frontend, is a ipython interface that send request to kernel and proccess the kernel's outputs. | ||||
For more details, see the ipython-zmq design | ||||
""" | ||||
#----------------------------------------------------------------------------- | ||||
# Copyright (C) 2011 The IPython Development Team | ||||
# | ||||
# 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 | ||
MinRK
|
r5600 | import sys | ||
MinRK
|
r5616 | import time | ||
MinRK
|
r5600 | |||
from Queue import Empty | ||||
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 | ||||
from IPython.frontend.terminal.interactiveshell import TerminalInteractiveShell | ||||
MinRK
|
r5611 | from IPython.frontend.terminal.console.completer import ZMQCompleter | ||
MinRK
|
r5600 | |||
class ZMQTerminalInteractiveShell(TerminalInteractiveShell): | ||||
MinRK
|
r5616 | """A subclass of TerminalInteractiveShell that uses the 0MQ kernel""" | ||
_executing = False | ||||
MinRK
|
r5600 | |||
def __init__(self, *args, **kwargs): | ||||
self.km = kwargs.pop('kernel_manager') | ||||
self.session_id = self.km.session.session | ||||
super(ZMQTerminalInteractiveShell, self).__init__(*args, **kwargs) | ||||
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) | ||||
self.Completer = ZMQCompleter(self, self.km) | ||||
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 | ||||
while self.km.shell_channel.msg_ready(): | ||||
self.km.shell_channel.get_msg() | ||||
MinRK
|
r5600 | # shell_channel.execute takes 'hidden', which is the inverse of store_hist | ||
msg_id = self.km.shell_channel.execute(cell, not store_history) | ||||
MinRK
|
r5616 | while not self.km.shell_channel.msg_ready() and self.km.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
|
r5616 | if self.km.shell_channel.msg_ready(): | ||
self.handle_execute_reply(msg_id) | ||||
self._executing = False | ||||
MinRK
|
r5600 | |||
#----------------- | ||||
# message handlers | ||||
#----------------- | ||||
def handle_execute_reply(self, msg_id): | ||||
msg = self.km.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. | ||||
""" | ||||
while self.km.sub_channel.msg_ready(): | ||||
sub_msg = self.km.sub_channel.get_msg() | ||||
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"] | ||
# 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() | ||||
def handle_stdin_request(self, timeout=0.1): | ||||
""" Method to capture raw_input | ||||
""" | ||||
msg_rep = self.km.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. | ||||
if not (self.km.stdin_channel.msg_ready() or self.km.shell_channel.msg_ready()): | ||||
self.km.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() | ||||
self.km.hb_channel.unpause() | ||||
while True: | ||||
self.run_cell('1', False) | ||||
if self.km.hb_channel.is_beating(): | ||||
# 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: | ||||
if not self.km.is_alive: | ||||
MinRK
|
r5616 | # kernel died, prompt for action or exit | ||
action = "restart" if self.km.has_kernel else "wait for restart" | ||||
ans = self.ask_yes_no("kernel died, %s ([y]/n)?" % action, default='y') | ||||
if ans: | ||||
if self.km.has_kernel: | ||||
self.km.restart_kernel(True) | ||||
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 | ||||