ipkernel.py
335 lines
| 12.7 KiB
| text/x-python
|
PythonLexer
Thomas Kluyver
|
r16857 | """The IPython kernel implementation""" | |
import getpass | |||
import sys | |||
import traceback | |||
from IPython.core import release | |||
from IPython.utils.py3compat import builtin_mod, PY3 | |||
MinRK
|
r18478 | from IPython.utils.tokenutil import token_at_cursor, line_at_cursor | |
Thomas Kluyver
|
r16857 | from IPython.utils.traitlets import Instance, Type, Any | |
Thomas Kluyver
|
r17095 | from IPython.utils.decorators import undoc | |
Thomas Kluyver
|
r16857 | ||
Thomas Kluyver
|
r17987 | from ..comm import CommManager | |
Thomas Kluyver
|
r17095 | from .kernelbase import Kernel as KernelBase | |
Thomas Kluyver
|
r16857 | from .serialize import serialize_object, unpack_apply_message | |
from .zmqshell import ZMQInteractiveShell | |||
Thomas Kluyver
|
r16856 | ||
Bussonnier Matthias
|
r19281 | ||
def lazy_import_handle_comm_opened(*args, **kwargs): | |||
from IPython.html.widgets import Widget | |||
Widget.handle_comm_opened(*args, **kwargs) | |||
Thomas Kluyver
|
r17095 | class IPythonKernel(KernelBase): | |
Thomas Kluyver
|
r16857 | shell = Instance('IPython.core.interactiveshell.InteractiveShellABC') | |
shell_class = Type(ZMQInteractiveShell) | |||
user_module = Any() | |||
def _user_module_changed(self, name, old, new): | |||
if self.shell is not None: | |||
self.shell.user_module = new | |||
user_ns = Instance(dict, args=None, allow_none=True) | |||
def _user_ns_changed(self, name, old, new): | |||
if self.shell is not None: | |||
self.shell.user_ns = new | |||
self.shell.init_user_ns() | |||
# A reference to the Python builtin 'raw_input' function. | |||
# (i.e., __builtin__.raw_input for Python 2.7, builtins.input for Python 3) | |||
_sys_raw_input = Any() | |||
_sys_eval_input = Any() | |||
def __init__(self, **kwargs): | |||
Thomas Kluyver
|
r17095 | super(IPythonKernel, self).__init__(**kwargs) | |
Thomas Kluyver
|
r16857 | ||
# Initialize the InteractiveShell subclass | |||
self.shell = self.shell_class.instance(parent=self, | |||
profile_dir = self.profile_dir, | |||
user_module = self.user_module, | |||
user_ns = self.user_ns, | |||
kernel = self, | |||
) | |||
self.shell.displayhook.session = self.session | |||
self.shell.displayhook.pub_socket = self.iopub_socket | |||
self.shell.displayhook.topic = self._topic('execute_result') | |||
self.shell.display_pub.session = self.session | |||
self.shell.display_pub.pub_socket = self.iopub_socket | |||
self.shell.data_pub.session = self.session | |||
self.shell.data_pub.pub_socket = self.iopub_socket | |||
# TMP - hack while developing | |||
self.shell._reply_content = None | |||
Thomas Kluyver
|
r17987 | self.comm_manager = CommManager(shell=self.shell, parent=self, | |
kernel=self) | |||
Bussonnier Matthias
|
r19281 | self.comm_manager.register_target('ipython.widget', lazy_import_handle_comm_opened) | |
Jonathan Frederic
|
r18510 | ||
Thomas Kluyver
|
r17987 | self.shell.configurables.append(self.comm_manager) | |
Thomas Kluyver
|
r16857 | comm_msg_types = [ 'comm_open', 'comm_msg', 'comm_close' ] | |
for msg_type in comm_msg_types: | |||
Thomas Kluyver
|
r17987 | self.shell_handlers[msg_type] = getattr(self.comm_manager, msg_type) | |
Thomas Kluyver
|
r16857 | ||
# Kernel info fields | |||
implementation = 'ipython' | |||
implementation_version = release.version | |||
Min RK
|
r19022 | language_info = { | |
'name': 'python', | |||
'version': sys.version.split()[0], | |||
'mimetype': 'text/x-python', | |||
Thomas Kluyver
|
r18468 | 'codemirror_mode': {'name': 'ipython', | |
'version': sys.version_info[0]}, | |||
'pygments_lexer': 'ipython%d' % (3 if PY3 else 2), | |||
Thomas Kluyver
|
r18967 | 'nbconvert_exporter': 'python', | |
Thomas Kluyver
|
r19027 | 'file_extension': '.py' | |
Thomas Kluyver
|
r18468 | } | |
Thomas Kluyver
|
r16857 | @property | |
def banner(self): | |||
return self.shell.banner | |||
Thomas Kluyver
|
r16858 | def start(self): | |
self.shell.exit_now = False | |||
Thomas Kluyver
|
r17095 | super(IPythonKernel, self).start() | |
Thomas Kluyver
|
r16858 | ||
Thomas Kluyver
|
r16857 | def set_parent(self, ident, parent): | |
"""Overridden from parent to tell the display hook and output streams | |||
about the parent message. | |||
""" | |||
Thomas Kluyver
|
r17095 | super(IPythonKernel, self).set_parent(ident, parent) | |
Thomas Kluyver
|
r16857 | self.shell.set_parent(parent) | |
def _forward_input(self, allow_stdin=False): | |||
"""Forward raw_input and getpass to the current frontend. | |||
via input_request | |||
""" | |||
self._allow_stdin = allow_stdin | |||
if PY3: | |||
self._sys_raw_input = builtin_mod.input | |||
builtin_mod.input = self.raw_input | |||
else: | |||
self._sys_raw_input = builtin_mod.raw_input | |||
self._sys_eval_input = builtin_mod.input | |||
builtin_mod.raw_input = self.raw_input | |||
builtin_mod.input = lambda prompt='': eval(self.raw_input(prompt)) | |||
self._save_getpass = getpass.getpass | |||
getpass.getpass = self.getpass | |||
def _restore_input(self): | |||
"""Restore raw_input, getpass""" | |||
if PY3: | |||
builtin_mod.input = self._sys_raw_input | |||
else: | |||
builtin_mod.raw_input = self._sys_raw_input | |||
builtin_mod.input = self._sys_eval_input | |||
getpass.getpass = self._save_getpass | |||
@property | |||
def execution_count(self): | |||
return self.shell.execution_count | |||
@execution_count.setter | |||
def execution_count(self, value): | |||
# Ignore the incrememnting done by KernelBase, in favour of our shell's | |||
# execution counter. | |||
pass | |||
def do_execute(self, code, silent, store_history=True, | |||
user_expressions=None, allow_stdin=False): | |||
shell = self.shell # we'll need this a lot here | |||
self._forward_input(allow_stdin) | |||
reply_content = {} | |||
# FIXME: the shell calls the exception handler itself. | |||
shell._reply_content = None | |||
try: | |||
shell.run_cell(code, store_history=store_history, silent=silent) | |||
except: | |||
status = u'error' | |||
# FIXME: this code right now isn't being used yet by default, | |||
# because the run_cell() call above directly fires off exception | |||
# reporting. This code, therefore, is only active in the scenario | |||
# where runlines itself has an unhandled exception. We need to | |||
# uniformize this, for all exception construction to come from a | |||
# single location in the codbase. | |||
etype, evalue, tb = sys.exc_info() | |||
tb_list = traceback.format_exception(etype, evalue, tb) | |||
reply_content.update(shell._showtraceback(etype, evalue, tb_list)) | |||
else: | |||
status = u'ok' | |||
finally: | |||
self._restore_input() | |||
reply_content[u'status'] = status | |||
# Return the execution counter so clients can display prompts | |||
reply_content['execution_count'] = shell.execution_count - 1 | |||
# FIXME - fish exception info out of shell, possibly left there by | |||
# runlines. We'll need to clean up this logic later. | |||
if shell._reply_content is not None: | |||
reply_content.update(shell._reply_content) | |||
e_info = dict(engine_uuid=self.ident, engine_id=self.int_id, method='execute') | |||
reply_content['engine_info'] = e_info | |||
# reset after use | |||
shell._reply_content = None | |||
if 'traceback' in reply_content: | |||
self.log.info("Exception in execute request:\n%s", '\n'.join(reply_content['traceback'])) | |||
# At this point, we can tell whether the main code execution succeeded | |||
# or not. If it did, we proceed to evaluate user_expressions | |||
if reply_content['status'] == 'ok': | |||
reply_content[u'user_expressions'] = \ | |||
shell.user_expressions(user_expressions or {}) | |||
else: | |||
# If there was an error, don't even try to compute expressions | |||
reply_content[u'user_expressions'] = {} | |||
# Payloads should be retrieved regardless of outcome, so we can both | |||
# recover partial output (that could have been generated early in a | |||
# block, before an error) and clear the payload system always. | |||
reply_content[u'payload'] = shell.payload_manager.read_payload() | |||
# Be agressive about clearing the payload because we don't want | |||
# it to sit in memory until the next execute_request comes in. | |||
shell.payload_manager.clear_payload() | |||
return reply_content | |||
def do_complete(self, code, cursor_pos): | |||
MinRK
|
r18478 | # FIXME: IPython completers currently assume single line, | |
# but completion messages give multi-line context | |||
# For now, extract line from cell, based on cursor_pos: | |||
if cursor_pos is None: | |||
cursor_pos = len(code) | |||
line, offset = line_at_cursor(code, cursor_pos) | |||
line_cursor = cursor_pos - offset | |||
txt, matches = self.shell.complete('', line, line_cursor) | |||
Thomas Kluyver
|
r16857 | return {'matches' : matches, | |
'cursor_end' : cursor_pos, | |||
'cursor_start' : cursor_pos - len(txt), | |||
'metadata' : {}, | |||
'status' : 'ok'} | |||
def do_inspect(self, code, cursor_pos, detail_level=0): | |||
name = token_at_cursor(code, cursor_pos) | |||
info = self.shell.object_inspect(name) | |||
reply_content = {'status' : 'ok'} | |||
reply_content['data'] = data = {} | |||
reply_content['metadata'] = {} | |||
reply_content['found'] = info['found'] | |||
if info['found']: | |||
info_text = self.shell.object_inspect_text( | |||
name, | |||
detail_level=detail_level, | |||
) | |||
data['text/plain'] = info_text | |||
return reply_content | |||
def do_history(self, hist_access_type, output, raw, session=None, start=None, | |||
stop=None, n=None, pattern=None, unique=False): | |||
if hist_access_type == 'tail': | |||
hist = self.shell.history_manager.get_tail(n, raw=raw, output=output, | |||
include_latest=True) | |||
elif hist_access_type == 'range': | |||
hist = self.shell.history_manager.get_range(session, start, stop, | |||
raw=raw, output=output) | |||
elif hist_access_type == 'search': | |||
hist = self.shell.history_manager.search( | |||
pattern, raw=raw, output=output, n=n, unique=unique) | |||
else: | |||
hist = [] | |||
return {'history' : list(hist)} | |||
def do_shutdown(self, restart): | |||
self.shell.exit_now = True | |||
return dict(status='ok', restart=restart) | |||
Thomas Kluyver
|
r17624 | def do_is_complete(self, code): | |
Thomas Kluyver
|
r17804 | status, indent_spaces = self.shell.input_transformer_manager.check_complete(code) | |
r = {'status': status} | |||
if status == 'incomplete': | |||
r['indent'] = ' ' * indent_spaces | |||
return r | |||
Thomas Kluyver
|
r17624 | ||
Thomas Kluyver
|
r16857 | def do_apply(self, content, bufs, msg_id, reply_metadata): | |
shell = self.shell | |||
try: | |||
working = shell.user_ns | |||
prefix = "_"+str(msg_id).replace("-","")+"_" | |||
f,args,kwargs = unpack_apply_message(bufs, working, copy=False) | |||
fname = getattr(f, '__name__', 'f') | |||
fname = prefix+"f" | |||
argname = prefix+"args" | |||
kwargname = prefix+"kwargs" | |||
resultname = prefix+"result" | |||
ns = { fname : f, argname : args, kwargname : kwargs , resultname : None } | |||
# print ns | |||
working.update(ns) | |||
code = "%s = %s(*%s,**%s)" % (resultname, fname, argname, kwargname) | |||
try: | |||
exec(code, shell.user_global_ns, shell.user_ns) | |||
result = working.get(resultname) | |||
finally: | |||
for key in ns: | |||
working.pop(key) | |||
result_buf = serialize_object(result, | |||
buffer_threshold=self.session.buffer_threshold, | |||
item_threshold=self.session.item_threshold, | |||
) | |||
except: | |||
# invoke IPython traceback formatting | |||
shell.showtraceback() | |||
# FIXME - fish exception info out of shell, possibly left there by | |||
# run_code. We'll need to clean up this logic later. | |||
reply_content = {} | |||
if shell._reply_content is not None: | |||
reply_content.update(shell._reply_content) | |||
e_info = dict(engine_uuid=self.ident, engine_id=self.int_id, method='apply') | |||
reply_content['engine_info'] = e_info | |||
# reset after use | |||
shell._reply_content = None | |||
self.send_response(self.iopub_socket, u'error', reply_content, | |||
ident=self._topic('error')) | |||
self.log.info("Exception in apply request:\n%s", '\n'.join(reply_content['traceback'])) | |||
result_buf = [] | |||
if reply_content['ename'] == 'UnmetDependency': | |||
reply_metadata['dependencies_met'] = False | |||
else: | |||
reply_content = {'status' : 'ok'} | |||
return reply_content, result_buf | |||
def do_clear(self): | |||
self.shell.reset(False) | |||
return dict(status='ok') | |||
Thomas Kluyver
|
r17095 | ||
# This exists only for backwards compatibility - use IPythonKernel instead | |||
@undoc | |||
class Kernel(IPythonKernel): | |||
def __init__(self, *args, **kwargs): | |||
import warnings | |||
warnings.warn('Kernel is a deprecated alias of IPython.kernel.zmq.ipkernel.IPythonKernel', | |||
DeprecationWarning) | |||
super(Kernel, self).__init__(*args, **kwargs) |