diff --git a/examples/Embedding/inprocess_qtconsole.py b/examples/Embedding/inprocess_qtconsole.py deleted file mode 100644 index 3fc6629..0000000 --- a/examples/Embedding/inprocess_qtconsole.py +++ /dev/null @@ -1,46 +0,0 @@ -from __future__ import print_function -import os - -from IPython.qt.console.rich_ipython_widget import RichIPythonWidget -from IPython.qt.inprocess import QtInProcessKernelManager -from IPython.lib import guisupport - - -def print_process_id(): - print('Process ID is:', os.getpid()) - - -def main(): - # Print the ID of the main process - print_process_id() - - app = guisupport.get_app_qt4() - - # Create an in-process kernel - # >>> print_process_id() - # will print the same process ID as the main process - kernel_manager = QtInProcessKernelManager() - kernel_manager.start_kernel() - kernel = kernel_manager.kernel - kernel.gui = 'qt4' - kernel.shell.push({'foo': 43, 'print_process_id': print_process_id}) - - kernel_client = kernel_manager.client() - kernel_client.start_channels() - - def stop(): - kernel_client.stop_channels() - kernel_manager.shutdown_kernel() - app.exit() - - control = RichIPythonWidget() - control.kernel_manager = kernel_manager - control.kernel_client = kernel_client - control.exit_requested.connect(stop) - control.show() - - guisupport.start_event_loop_qt4(app) - - -if __name__ == '__main__': - main() diff --git a/examples/Embedding/inprocess_terminal.py b/examples/Embedding/inprocess_terminal.py deleted file mode 100644 index a295c0a..0000000 --- a/examples/Embedding/inprocess_terminal.py +++ /dev/null @@ -1,31 +0,0 @@ -from __future__ import print_function -import os - -from IPython.kernel.inprocess import InProcessKernelManager -from IPython.terminal.console.interactiveshell import ZMQTerminalInteractiveShell - - -def print_process_id(): - print('Process ID is:', os.getpid()) - - -def main(): - print_process_id() - - # Create an in-process kernel - # >>> print_process_id() - # will print the same process ID as the main process - kernel_manager = InProcessKernelManager() - kernel_manager.start_kernel() - kernel = kernel_manager.kernel - kernel.gui = 'qt4' - kernel.shell.push({'foo': 43, 'print_process_id': print_process_id}) - client = kernel_manager.client() - client.start_channels() - - shell = ZMQTerminalInteractiveShell(manager=kernel_manager, client=client) - shell.mainloop() - - -if __name__ == '__main__': - main() diff --git a/examples/Embedding/internal_ipkernel.py b/examples/Embedding/internal_ipkernel.py deleted file mode 100644 index db434bd..0000000 --- a/examples/Embedding/internal_ipkernel.py +++ /dev/null @@ -1,55 +0,0 @@ -#----------------------------------------------------------------------------- -# Imports -#----------------------------------------------------------------------------- - -import sys - -from IPython.lib.kernel import connect_qtconsole -from IPython.kernel.zmq.kernelapp import IPKernelApp - -#----------------------------------------------------------------------------- -# Functions and classes -#----------------------------------------------------------------------------- -def mpl_kernel(gui): - """Launch and return an IPython kernel with matplotlib support for the desired gui - """ - kernel = IPKernelApp.instance() - kernel.initialize(['python', '--matplotlib=%s' % gui, - #'--log-level=10' - ]) - return kernel - - -class InternalIPKernel(object): - - def init_ipkernel(self, backend): - # Start IPython kernel with GUI event loop and mpl support - self.ipkernel = mpl_kernel(backend) - # To create and track active qt consoles - self.consoles = [] - - # This application will also act on the shell user namespace - self.namespace = self.ipkernel.shell.user_ns - - # Example: a variable that will be seen by the user in the shell, and - # that the GUI modifies (the 'Counter++' button increments it): - self.namespace['app_counter'] = 0 - #self.namespace['ipkernel'] = self.ipkernel # dbg - - def print_namespace(self, evt=None): - print("\n***Variables in User namespace***") - for k, v in self.namespace.items(): - if not k.startswith('_'): - print('%s -> %r' % (k, v)) - sys.stdout.flush() - - def new_qt_console(self, evt=None): - """start a new qtconsole connected to our kernel""" - return connect_qtconsole(self.ipkernel.connection_file, profile=self.ipkernel.profile) - - def count(self, evt=None): - self.namespace['app_counter'] += 1 - - def cleanup_consoles(self, evt=None): - for c in self.consoles: - c.kill() diff --git a/examples/Embedding/ipkernel_qtapp.py b/examples/Embedding/ipkernel_qtapp.py deleted file mode 100755 index 93ab0c0..0000000 --- a/examples/Embedding/ipkernel_qtapp.py +++ /dev/null @@ -1,75 +0,0 @@ -#!/usr/bin/env python -"""Example integrating an IPython kernel into a GUI App. - -This trivial GUI application internally starts an IPython kernel, to which Qt -consoles can be connected either by the user at the command line or started -from the GUI itself, via a button. The GUI can also manipulate one variable in -the kernel's namespace, and print the namespace to the console. - -Play with it by running the script and then opening one or more consoles, and -pushing the 'Counter++' and 'Namespace' buttons. - -Upon exit, it should automatically close all consoles opened from the GUI. - -Consoles attached separately from a terminal will not be terminated, though -they will notice that their kernel died. -""" -#----------------------------------------------------------------------------- -# Imports -#----------------------------------------------------------------------------- - -from PyQt4 import Qt - -from internal_ipkernel import InternalIPKernel - -#----------------------------------------------------------------------------- -# Functions and classes -#----------------------------------------------------------------------------- -class SimpleWindow(Qt.QWidget, InternalIPKernel): - - def __init__(self, app): - Qt.QWidget.__init__(self) - self.app = app - self.add_widgets() - self.init_ipkernel('qt') - - def add_widgets(self): - self.setGeometry(300, 300, 400, 70) - self.setWindowTitle('IPython in your app') - - # Add simple buttons: - console = Qt.QPushButton('Qt Console', self) - console.setGeometry(10, 10, 100, 35) - self.connect(console, Qt.SIGNAL('clicked()'), self.new_qt_console) - - namespace = Qt.QPushButton('Namespace', self) - namespace.setGeometry(120, 10, 100, 35) - self.connect(namespace, Qt.SIGNAL('clicked()'), self.print_namespace) - - count = Qt.QPushButton('Count++', self) - count.setGeometry(230, 10, 80, 35) - self.connect(count, Qt.SIGNAL('clicked()'), self.count) - - # Quit and cleanup - quit = Qt.QPushButton('Quit', self) - quit.setGeometry(320, 10, 60, 35) - self.connect(quit, Qt.SIGNAL('clicked()'), Qt.qApp, Qt.SLOT('quit()')) - - self.app.connect(self.app, Qt.SIGNAL("lastWindowClosed()"), - self.app, Qt.SLOT("quit()")) - - self.app.aboutToQuit.connect(self.cleanup_consoles) - -#----------------------------------------------------------------------------- -# Main script -#----------------------------------------------------------------------------- - -if __name__ == "__main__": - app = Qt.QApplication([]) - # Create our window - win = SimpleWindow(app) - win.show() - - # Very important, IPython-specific step: this gets GUI event loop - # integration going, and it replaces calling app.exec_() - win.ipkernel.start() diff --git a/examples/Embedding/ipkernel_wxapp.py b/examples/Embedding/ipkernel_wxapp.py deleted file mode 100755 index a85de7b..0000000 --- a/examples/Embedding/ipkernel_wxapp.py +++ /dev/null @@ -1,119 +0,0 @@ -#!/usr/bin/env python -"""Example integrating an IPython kernel into a GUI App. - -This trivial GUI application internally starts an IPython kernel, to which Qt -consoles can be connected either by the user at the command line or started -from the GUI itself, via a button. The GUI can also manipulate one variable in -the kernel's namespace, and print the namespace to the console. - -Play with it by running the script and then opening one or more consoles, and -pushing the 'Counter++' and 'Namespace' buttons. - -Upon exit, it should automatically close all consoles opened from the GUI. - -Consoles attached separately from a terminal will not be terminated, though -they will notice that their kernel died. - -Ref: Modified from wxPython source code wxPython/samples/simple/simple.py -""" -#----------------------------------------------------------------------------- -# Imports -#----------------------------------------------------------------------------- -import sys - -import wx - -from internal_ipkernel import InternalIPKernel - -#----------------------------------------------------------------------------- -# Functions and classes -#----------------------------------------------------------------------------- - -class MyFrame(wx.Frame, InternalIPKernel): - """ - This is MyFrame. It just shows a few controls on a wxPanel, - and has a simple menu. - """ - - def __init__(self, parent, title): - wx.Frame.__init__(self, parent, -1, title, - pos=(150, 150), size=(350, 285)) - - # Create the menubar - menuBar = wx.MenuBar() - - # and a menu - menu = wx.Menu() - - # add an item to the menu, using \tKeyName automatically - # creates an accelerator, the third param is some help text - # that will show up in the statusbar - menu.Append(wx.ID_EXIT, "E&xit\tAlt-X", "Exit this simple sample") - - # bind the menu event to an event handler - self.Bind(wx.EVT_MENU, self.OnTimeToClose, id=wx.ID_EXIT) - - # and put the menu on the menubar - menuBar.Append(menu, "&File") - self.SetMenuBar(menuBar) - - self.CreateStatusBar() - - # Now create the Panel to put the other controls on. - panel = wx.Panel(self) - - # and a few controls - text = wx.StaticText(panel, -1, "Hello World!") - text.SetFont(wx.Font(14, wx.SWISS, wx.NORMAL, wx.BOLD)) - text.SetSize(text.GetBestSize()) - qtconsole_btn = wx.Button(panel, -1, "Qt Console") - ns_btn = wx.Button(panel, -1, "Namespace") - count_btn = wx.Button(panel, -1, "Count++") - close_btn = wx.Button(panel, -1, "Quit") - - # bind the button events to handlers - self.Bind(wx.EVT_BUTTON, self.new_qt_console, qtconsole_btn) - self.Bind(wx.EVT_BUTTON, self.print_namespace, ns_btn) - self.Bind(wx.EVT_BUTTON, self.count, count_btn) - self.Bind(wx.EVT_BUTTON, self.OnTimeToClose, close_btn) - - # Use a sizer to layout the controls, stacked vertically and with - # a 10 pixel border around each - sizer = wx.BoxSizer(wx.VERTICAL) - for ctrl in [text, qtconsole_btn, ns_btn, count_btn, close_btn]: - sizer.Add(ctrl, 0, wx.ALL, 10) - panel.SetSizer(sizer) - panel.Layout() - - # Start the IPython kernel with gui support - self.init_ipkernel('wx') - - def OnTimeToClose(self, evt): - """Event handler for the button click.""" - print("See ya later!") - sys.stdout.flush() - self.cleanup_consoles(evt) - self.Close() - # Not sure why, but our IPython kernel seems to prevent normal WX - # shutdown, so an explicit exit() call is needed. - sys.exit() - - -class MyApp(wx.App): - def OnInit(self): - frame = MyFrame(None, "Simple wxPython App") - self.SetTopWindow(frame) - frame.Show(True) - self.ipkernel = frame.ipkernel - return True - -#----------------------------------------------------------------------------- -# Main script -#----------------------------------------------------------------------------- - -if __name__ == '__main__': - app = MyApp(redirect=False, clearSigInt=False) - - # Very important, IPython-specific step: this gets GUI event loop - # integration going, and it replaces calling app.MainLoop() - app.ipkernel.start() diff --git a/ipython_kernel/__init__.py b/ipython_kernel/__init__.py deleted file mode 100644 index 14c5df6..0000000 --- a/ipython_kernel/__init__.py +++ /dev/null @@ -1 +0,0 @@ -from .connect import * \ No newline at end of file diff --git a/ipython_kernel/__main__.py b/ipython_kernel/__main__.py deleted file mode 100644 index 4b8d4ef..0000000 --- a/ipython_kernel/__main__.py +++ /dev/null @@ -1,3 +0,0 @@ -if __name__ == '__main__': - from ipython_kernel import kernelapp as app - app.launch_new_instance() diff --git a/ipython_kernel/codeutil.py b/ipython_kernel/codeutil.py deleted file mode 100644 index 887bd90..0000000 --- a/ipython_kernel/codeutil.py +++ /dev/null @@ -1,35 +0,0 @@ -# encoding: utf-8 - -"""Utilities to enable code objects to be pickled. - -Any process that import this module will be able to pickle code objects. This -includes the func_code attribute of any function. Once unpickled, new -functions can be built using new.function(code, globals()). Eventually -we need to automate all of this so that functions themselves can be pickled. - -Reference: A. Tremols, P Cogolo, "Python Cookbook," p 302-305 -""" - -# Copyright (c) IPython Development Team. -# Distributed under the terms of the Modified BSD License. - -import sys -import types -try: - import copyreg # Py 3 -except ImportError: - import copy_reg as copyreg # Py 2 - -def code_ctor(*args): - return types.CodeType(*args) - -def reduce_code(co): - args = [co.co_argcount, co.co_nlocals, co.co_stacksize, - co.co_flags, co.co_code, co.co_consts, co.co_names, - co.co_varnames, co.co_filename, co.co_name, co.co_firstlineno, - co.co_lnotab, co.co_freevars, co.co_cellvars] - if sys.version_info[0] >= 3: - args.insert(1, co.co_kwonlyargcount) - return code_ctor, tuple(args) - -copyreg.pickle(types.CodeType, reduce_code) \ No newline at end of file diff --git a/ipython_kernel/comm/__init__.py b/ipython_kernel/comm/__init__.py deleted file mode 100644 index 1faa164..0000000 --- a/ipython_kernel/comm/__init__.py +++ /dev/null @@ -1,2 +0,0 @@ -from .manager import * -from .comm import * diff --git a/ipython_kernel/comm/comm.py b/ipython_kernel/comm/comm.py deleted file mode 100644 index f795b99..0000000 --- a/ipython_kernel/comm/comm.py +++ /dev/null @@ -1,169 +0,0 @@ -"""Base class for a Comm""" - -# Copyright (c) IPython Development Team. -# Distributed under the terms of the Modified BSD License. - -import threading -import uuid - -from zmq.eventloop.ioloop import IOLoop - -from IPython.config import LoggingConfigurable -from ipython_kernel.kernelbase import Kernel - -from jupyter_client.jsonutil import json_clean -from IPython.utils.traitlets import Instance, Unicode, Bytes, Bool, Dict, Any - - -class Comm(LoggingConfigurable): - """Class for communicating between a Frontend and a Kernel""" - # If this is instantiated by a non-IPython kernel, shell will be None - shell = Instance('IPython.core.interactiveshell.InteractiveShellABC', - allow_none=True) - kernel = Instance('ipython_kernel.kernelbase.Kernel') - def _kernel_default(self): - if Kernel.initialized(): - return Kernel.instance() - - iopub_socket = Any() - def _iopub_socket_default(self): - return self.kernel.iopub_socket - session = Instance('ipython_kernel.session.Session') - def _session_default(self): - if self.kernel is not None: - return self.kernel.session - - target_name = Unicode('comm') - target_module = Unicode(None, allow_none=True, help="""requirejs module from - which to load comm target.""") - - topic = Bytes() - def _topic_default(self): - return ('comm-%s' % self.comm_id).encode('ascii') - - _open_data = Dict(help="data dict, if any, to be included in comm_open") - _close_data = Dict(help="data dict, if any, to be included in comm_close") - - _msg_callback = Any() - _close_callback = Any() - - _closed = Bool(True) - comm_id = Unicode() - def _comm_id_default(self): - return uuid.uuid4().hex - - primary = Bool(True, help="Am I the primary or secondary Comm?") - - def __init__(self, target_name='', data=None, **kwargs): - if target_name: - kwargs['target_name'] = target_name - super(Comm, self).__init__(**kwargs) - if self.primary: - # I am primary, open my peer. - self.open(data) - else: - self._closed = False - - def _publish_msg(self, msg_type, data=None, metadata=None, buffers=None, **keys): - """Helper for sending a comm message on IOPub""" - if threading.current_thread().name != 'MainThread' and IOLoop.initialized(): - # make sure we never send on a zmq socket outside the main IOLoop thread - IOLoop.instance().add_callback(lambda : self._publish_msg(msg_type, data, metadata, buffers, **keys)) - return - data = {} if data is None else data - metadata = {} if metadata is None else metadata - content = json_clean(dict(data=data, comm_id=self.comm_id, **keys)) - self.session.send(self.iopub_socket, msg_type, - content, - metadata=json_clean(metadata), - parent=self.kernel._parent_header, - ident=self.topic, - buffers=buffers, - ) - - def __del__(self): - """trigger close on gc""" - self.close() - - # publishing messages - - def open(self, data=None, metadata=None, buffers=None): - """Open the frontend-side version of this comm""" - if data is None: - data = self._open_data - comm_manager = getattr(self.kernel, 'comm_manager', None) - if comm_manager is None: - raise RuntimeError("Comms cannot be opened without a kernel " - "and a comm_manager attached to that kernel.") - - comm_manager.register_comm(self) - try: - self._publish_msg('comm_open', - data=data, metadata=metadata, buffers=buffers, - target_name=self.target_name, - target_module=self.target_module, - ) - self._closed = False - except: - comm_manager.unregister_comm(self) - raise - - def close(self, data=None, metadata=None, buffers=None): - """Close the frontend-side version of this comm""" - if self._closed: - # only close once - return - self._closed = True - if data is None: - data = self._close_data - self._publish_msg('comm_close', - data=data, metadata=metadata, buffers=buffers, - ) - self.kernel.comm_manager.unregister_comm(self) - - def send(self, data=None, metadata=None, buffers=None): - """Send a message to the frontend-side version of this comm""" - self._publish_msg('comm_msg', - data=data, metadata=metadata, buffers=buffers, - ) - - # registering callbacks - - def on_close(self, callback): - """Register a callback for comm_close - - Will be called with the `data` of the close message. - - Call `on_close(None)` to disable an existing callback. - """ - self._close_callback = callback - - def on_msg(self, callback): - """Register a callback for comm_msg - - Will be called with the `data` of any comm_msg messages. - - Call `on_msg(None)` to disable an existing callback. - """ - self._msg_callback = callback - - # handling of incoming messages - - def handle_close(self, msg): - """Handle a comm_close message""" - self.log.debug("handle_close[%s](%s)", self.comm_id, msg) - if self._close_callback: - self._close_callback(msg) - - def handle_msg(self, msg): - """Handle a comm_msg message""" - self.log.debug("handle_msg[%s](%s)", self.comm_id, msg) - if self._msg_callback: - if self.shell: - self.shell.events.trigger('pre_execute') - self._msg_callback(msg) - if self.shell: - self.shell.events.trigger('post_execute') - - -__all__ = ['Comm'] diff --git a/ipython_kernel/comm/manager.py b/ipython_kernel/comm/manager.py deleted file mode 100644 index acba849..0000000 --- a/ipython_kernel/comm/manager.py +++ /dev/null @@ -1,157 +0,0 @@ -"""Base class to manage comms""" - -# Copyright (c) IPython Development Team. -# Distributed under the terms of the Modified BSD License. - -import sys - -from IPython.config import LoggingConfigurable -from IPython.core.prompts import LazyEvaluate -from IPython.core.getipython import get_ipython - -from IPython.utils.importstring import import_item -from IPython.utils.py3compat import string_types -from IPython.utils.traitlets import Instance, Unicode, Dict, Any - -from .comm import Comm - - -def lazy_keys(dikt): - """Return lazy-evaluated string representation of a dictionary's keys - - Key list is only constructed if it will actually be used. - Used for debug-logging. - """ - return LazyEvaluate(lambda d: list(d.keys())) - - -class CommManager(LoggingConfigurable): - """Manager for Comms in the Kernel""" - - # If this is instantiated by a non-IPython kernel, shell will be None - shell = Instance('IPython.core.interactiveshell.InteractiveShellABC', - allow_none=True) - kernel = Instance('ipython_kernel.kernelbase.Kernel') - - iopub_socket = Any() - def _iopub_socket_default(self): - return self.kernel.iopub_socket - session = Instance('ipython_kernel.session.Session') - def _session_default(self): - return self.kernel.session - - comms = Dict() - targets = Dict() - - # Public APIs - - def register_target(self, target_name, f): - """Register a callable f for a given target name - - f will be called with two arguments when a comm_open message is received with `target`: - - - the Comm instance - - the `comm_open` message itself. - - f can be a Python callable or an import string for one. - """ - if isinstance(f, string_types): - f = import_item(f) - - self.targets[target_name] = f - - def unregister_target(self, target_name, f): - """Unregister a callable registered with register_target""" - return self.targets.pop(target_name); - - def register_comm(self, comm): - """Register a new comm""" - comm_id = comm.comm_id - comm.shell = self.shell - comm.kernel = self.kernel - comm.iopub_socket = self.iopub_socket - self.comms[comm_id] = comm - return comm_id - - def unregister_comm(self, comm): - """Unregister a comm, and close its counterpart""" - # unlike get_comm, this should raise a KeyError - comm = self.comms.pop(comm.comm_id) - - def get_comm(self, comm_id): - """Get a comm with a particular id - - Returns the comm if found, otherwise None. - - This will not raise an error, - it will log messages if the comm cannot be found. - """ - if comm_id not in self.comms: - self.log.error("No such comm: %s", comm_id) - self.log.debug("Current comms: %s", lazy_keys(self.comms)) - return - # call, because we store weakrefs - comm = self.comms[comm_id] - return comm - - # Message handlers - def comm_open(self, stream, ident, msg): - """Handler for comm_open messages""" - content = msg['content'] - comm_id = content['comm_id'] - target_name = content['target_name'] - f = self.targets.get(target_name, None) - comm = Comm(comm_id=comm_id, - shell=self.shell, - kernel=self.kernel, - iopub_socket=self.iopub_socket, - primary=False, - ) - self.register_comm(comm) - if f is None: - self.log.error("No such comm target registered: %s", target_name) - else: - try: - f(comm, msg) - return - except Exception: - self.log.error("Exception opening comm with target: %s", target_name, exc_info=True) - - # Failure. - try: - comm.close() - except: - self.log.error("""Could not close comm during `comm_open` failure - clean-up. The comm may not have been opened yet.""", exc_info=True) - - def comm_msg(self, stream, ident, msg): - """Handler for comm_msg messages""" - content = msg['content'] - comm_id = content['comm_id'] - comm = self.get_comm(comm_id) - if comm is None: - # no such comm - return - try: - comm.handle_msg(msg) - except Exception: - self.log.error("Exception in comm_msg for %s", comm_id, exc_info=True) - - def comm_close(self, stream, ident, msg): - """Handler for comm_close messages""" - content = msg['content'] - comm_id = content['comm_id'] - comm = self.get_comm(comm_id) - if comm is None: - # no such comm - self.log.debug("No such comm to close: %s", comm_id) - return - del self.comms[comm_id] - - try: - comm.handle_close(msg) - except Exception: - self.log.error("Exception handling comm_close for %s", comm_id, exc_info=True) - - -__all__ = ['CommManager'] diff --git a/ipython_kernel/connect.py b/ipython_kernel/connect.py deleted file mode 100644 index 005f366..0000000 --- a/ipython_kernel/connect.py +++ /dev/null @@ -1,179 +0,0 @@ -"""Connection file-related utilities for the kernel -""" -# Copyright (c) IPython Development Team. -# Distributed under the terms of the Modified BSD License. - -from __future__ import absolute_import - -import json -import sys -from subprocess import Popen, PIPE - -from IPython.core.profiledir import ProfileDir -from IPython.utils.path import filefind, get_ipython_dir -from IPython.utils.py3compat import str_to_bytes - -import jupyter_client -from jupyter_client import write_connection_file - - - -def get_connection_file(app=None): - """Return the path to the connection file of an app - - Parameters - ---------- - app : IPKernelApp instance [optional] - If unspecified, the currently running app will be used - """ - if app is None: - from ipython_kernel.kernelapp import IPKernelApp - if not IPKernelApp.initialized(): - raise RuntimeError("app not specified, and not in a running Kernel") - - app = IPKernelApp.instance() - return filefind(app.connection_file, ['.', app.profile_dir.security_dir]) - - -def find_connection_file(filename='kernel-*.json', profile=None): - """find a connection file, and return its absolute path. - - The current working directory and the profile's security - directory will be searched for the file if it is not given by - absolute path. - - If profile is unspecified, then the current running application's - profile will be used, or 'default', if not run from IPython. - - If the argument does not match an existing file, it will be interpreted as a - fileglob, and the matching file in the profile's security dir with - the latest access time will be used. - - Parameters - ---------- - filename : str - The connection file or fileglob to search for. - profile : str [optional] - The name of the profile to use when searching for the connection file, - if different from the current IPython session or 'default'. - - Returns - ------- - str : The absolute path of the connection file. - """ - from IPython.core.application import BaseIPythonApplication as IPApp - try: - # quick check for absolute path, before going through logic - return filefind(filename) - except IOError: - pass - - if profile is None: - # profile unspecified, check if running from an IPython app - if IPApp.initialized(): - app = IPApp.instance() - profile_dir = app.profile_dir - else: - # not running in IPython, use default profile - profile_dir = ProfileDir.find_profile_dir_by_name(get_ipython_dir(), 'default') - else: - # find profiledir by profile name: - profile_dir = ProfileDir.find_profile_dir_by_name(get_ipython_dir(), profile) - security_dir = profile_dir.security_dir - - return jupyter_client.find_connection_file(filename, path=['.', security_dir]) - - -def get_connection_info(connection_file=None, unpack=False, profile=None): - """Return the connection information for the current Kernel. - - Parameters - ---------- - connection_file : str [optional] - The connection file to be used. Can be given by absolute path, or - IPython will search in the security directory of a given profile. - If run from IPython, - - If unspecified, the connection file for the currently running - IPython Kernel will be used, which is only allowed from inside a kernel. - unpack : bool [default: False] - if True, return the unpacked dict, otherwise just the string contents - of the file. - profile : str [optional] - The name of the profile to use when searching for the connection file, - if different from the current IPython session or 'default'. - - - Returns - ------- - The connection dictionary of the current kernel, as string or dict, - depending on `unpack`. - """ - if connection_file is None: - # get connection file from current kernel - cf = get_connection_file() - else: - # connection file specified, allow shortnames: - cf = find_connection_file(connection_file, profile=profile) - - with open(cf) as f: - info = f.read() - - if unpack: - info = json.loads(info) - # ensure key is bytes: - info['key'] = str_to_bytes(info.get('key', '')) - return info - - -def connect_qtconsole(connection_file=None, argv=None, profile=None): - """Connect a qtconsole to the current kernel. - - This is useful for connecting a second qtconsole to a kernel, or to a - local notebook. - - Parameters - ---------- - connection_file : str [optional] - The connection file to be used. Can be given by absolute path, or - IPython will search in the security directory of a given profile. - If run from IPython, - - If unspecified, the connection file for the currently running - IPython Kernel will be used, which is only allowed from inside a kernel. - argv : list [optional] - Any extra args to be passed to the console. - profile : str [optional] - The name of the profile to use when searching for the connection file, - if different from the current IPython session or 'default'. - - - Returns - ------- - :class:`subprocess.Popen` instance running the qtconsole frontend - """ - argv = [] if argv is None else argv - - if connection_file is None: - # get connection file from current kernel - cf = get_connection_file() - else: - cf = find_connection_file(connection_file, profile=profile) - - cmd = ';'.join([ - "from IPython.qt.console import qtconsoleapp", - "qtconsoleapp.main()" - ]) - - return Popen([sys.executable, '-c', cmd, '--existing', cf] + argv, - stdout=PIPE, stderr=PIPE, close_fds=(sys.platform != 'win32'), - ) - - -__all__ = [ - 'write_connection_file', - 'get_connection_file', - 'find_connection_file', - 'get_connection_info', - 'connect_qtconsole', -] diff --git a/ipython_kernel/datapub.py b/ipython_kernel/datapub.py deleted file mode 100644 index d8ebc1a..0000000 --- a/ipython_kernel/datapub.py +++ /dev/null @@ -1,70 +0,0 @@ -"""Publishing native (typically pickled) objects. -""" - -#----------------------------------------------------------------------------- -# Copyright (C) 2012 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 IPython.config import Configurable -from ipython_kernel.inprocess.socket import SocketABC -from jupyter_client.jsonutil import json_clean -from IPython.utils.traitlets import Instance, Dict, CBytes -from ipython_kernel.serialize import serialize_object -from ipython_kernel.session import Session, extract_header - -#----------------------------------------------------------------------------- -# Code -#----------------------------------------------------------------------------- - - -class ZMQDataPublisher(Configurable): - - topic = topic = CBytes(b'datapub') - session = Instance(Session, allow_none=True) - pub_socket = Instance(SocketABC, allow_none=True) - parent_header = Dict({}) - - def set_parent(self, parent): - """Set the parent for outbound messages.""" - self.parent_header = extract_header(parent) - - def publish_data(self, data): - """publish a data_message on the IOPub channel - - Parameters - ---------- - - data : dict - The data to be published. Think of it as a namespace. - """ - session = self.session - buffers = serialize_object(data, - buffer_threshold=session.buffer_threshold, - item_threshold=session.item_threshold, - ) - content = json_clean(dict(keys=data.keys())) - session.send(self.pub_socket, 'data_message', content=content, - parent=self.parent_header, - buffers=buffers, - ident=self.topic, - ) - - -def publish_data(data): - """publish a data_message on the IOPub channel - - Parameters - ---------- - - data : dict - The data to be published. Think of it as a namespace. - """ - from ipython_kernel.zmqshell import ZMQInteractiveShell - ZMQInteractiveShell.instance().data_pub.publish_data(data) diff --git a/ipython_kernel/displayhook.py b/ipython_kernel/displayhook.py deleted file mode 100644 index a781b80..0000000 --- a/ipython_kernel/displayhook.py +++ /dev/null @@ -1,73 +0,0 @@ -"""Replacements for sys.displayhook that publish over ZMQ.""" - -# Copyright (c) IPython Development Team. -# Distributed under the terms of the Modified BSD License. - -import sys - -from IPython.core.displayhook import DisplayHook -from ipython_kernel.inprocess.socket import SocketABC -from jupyter_client.jsonutil import encode_images -from IPython.utils.py3compat import builtin_mod -from IPython.utils.traitlets import Instance, Dict -from .session import extract_header, Session - -class ZMQDisplayHook(object): - """A simple displayhook that publishes the object's repr over a ZeroMQ - socket.""" - topic=b'execute_result' - - def __init__(self, session, pub_socket): - self.session = session - self.pub_socket = pub_socket - self.parent_header = {} - - def __call__(self, obj): - if obj is None: - return - - builtin_mod._ = obj - sys.stdout.flush() - sys.stderr.flush() - msg = self.session.send(self.pub_socket, u'execute_result', {u'data':repr(obj)}, - parent=self.parent_header, ident=self.topic) - - def set_parent(self, parent): - self.parent_header = extract_header(parent) - - -class ZMQShellDisplayHook(DisplayHook): - """A displayhook subclass that publishes data using ZeroMQ. This is intended - to work with an InteractiveShell instance. It sends a dict of different - representations of the object.""" - topic=None - - session = Instance(Session, allow_none=True) - pub_socket = Instance(SocketABC, allow_none=True) - parent_header = Dict({}) - - def set_parent(self, parent): - """Set the parent for outbound messages.""" - self.parent_header = extract_header(parent) - - def start_displayhook(self): - self.msg = self.session.msg(u'execute_result', { - 'data': {}, - 'metadata': {}, - }, parent=self.parent_header) - - def write_output_prompt(self): - """Write the output prompt.""" - self.msg['content']['execution_count'] = self.prompt_count - - def write_format_data(self, format_dict, md_dict=None): - self.msg['content']['data'] = encode_images(format_dict) - self.msg['content']['metadata'] = md_dict - - def finish_displayhook(self): - """Finish up all displayhook activities.""" - sys.stdout.flush() - sys.stderr.flush() - if self.msg['content']['data']: - self.session.send(self.pub_socket, self.msg, ident=self.topic) - self.msg = None diff --git a/ipython_kernel/embed.py b/ipython_kernel/embed.py deleted file mode 100644 index 6641cd6..0000000 --- a/ipython_kernel/embed.py +++ /dev/null @@ -1,57 +0,0 @@ -"""Simple function for embedding an IPython kernel -""" -#----------------------------------------------------------------------------- -# Imports -#----------------------------------------------------------------------------- - -import sys - -from IPython.utils.frame import extract_module_locals - -from .kernelapp import IPKernelApp - -#----------------------------------------------------------------------------- -# Code -#----------------------------------------------------------------------------- - -def embed_kernel(module=None, local_ns=None, **kwargs): - """Embed and start an IPython kernel in a given scope. - - Parameters - ---------- - module : ModuleType, optional - The module to load into IPython globals (default: caller) - local_ns : dict, optional - The namespace to load into IPython user namespace (default: caller) - - kwargs : various, optional - Further keyword args are relayed to the IPKernelApp constructor, - allowing configuration of the Kernel. Will only have an effect - on the first embed_kernel call for a given process. - - """ - # get the app if it exists, or set it up if it doesn't - if IPKernelApp.initialized(): - app = IPKernelApp.instance() - else: - app = IPKernelApp.instance(**kwargs) - app.initialize([]) - # Undo unnecessary sys module mangling from init_sys_modules. - # This would not be necessary if we could prevent it - # in the first place by using a different InteractiveShell - # subclass, as in the regular embed case. - main = app.kernel.shell._orig_sys_modules_main_mod - if main is not None: - sys.modules[app.kernel.shell._orig_sys_modules_main_name] = main - - # load the calling scope if not given - (caller_module, caller_locals) = extract_module_locals(1) - if module is None: - module = caller_module - if local_ns is None: - local_ns = caller_locals - - app.kernel.user_module = module - app.kernel.user_ns = local_ns - app.shell.set_completer_frame() - app.start() diff --git a/ipython_kernel/eventloops.py b/ipython_kernel/eventloops.py deleted file mode 100644 index 25ba87c..0000000 --- a/ipython_kernel/eventloops.py +++ /dev/null @@ -1,273 +0,0 @@ -# encoding: utf-8 -"""Event loop integration for the ZeroMQ-based kernels.""" - -# Copyright (c) IPython Development Team. -# Distributed under the terms of the Modified BSD License. - -import os -import sys - -import zmq - -from IPython.config.application import Application -from IPython.utils import io -from IPython.lib.inputhook import _use_appnope - -def _notify_stream_qt(kernel, stream): - - from IPython.external.qt_for_kernel import QtCore - - if _use_appnope() and kernel._darwin_app_nap: - from appnope import nope_scope as context - else: - from IPython.core.interactiveshell import NoOpContext as context - - def process_stream_events(): - while stream.getsockopt(zmq.EVENTS) & zmq.POLLIN: - with context(): - kernel.do_one_iteration() - - fd = stream.getsockopt(zmq.FD) - notifier = QtCore.QSocketNotifier(fd, QtCore.QSocketNotifier.Read, kernel.app) - notifier.activated.connect(process_stream_events) - -# mapping of keys to loop functions -loop_map = { - 'inline': None, - 'nbagg': None, - 'notebook': None, - None : None, -} - -def register_integration(*toolkitnames): - """Decorator to register an event loop to integrate with the IPython kernel - - The decorator takes names to register the event loop as for the %gui magic. - You can provide alternative names for the same toolkit. - - The decorated function should take a single argument, the IPython kernel - instance, arrange for the event loop to call ``kernel.do_one_iteration()`` - at least every ``kernel._poll_interval`` seconds, and start the event loop. - - :mod:`ipython_kernel.eventloops` provides and registers such functions - for a few common event loops. - """ - def decorator(func): - for name in toolkitnames: - loop_map[name] = func - return func - - return decorator - - -@register_integration('qt', 'qt4') -def loop_qt4(kernel): - """Start a kernel with PyQt4 event loop integration.""" - - from IPython.lib.guisupport import get_app_qt4, start_event_loop_qt4 - - kernel.app = get_app_qt4([" "]) - kernel.app.setQuitOnLastWindowClosed(False) - - for s in kernel.shell_streams: - _notify_stream_qt(kernel, s) - - start_event_loop_qt4(kernel.app) - -@register_integration('qt5') -def loop_qt5(kernel): - """Start a kernel with PyQt5 event loop integration.""" - os.environ['QT_API'] = 'pyqt5' - return loop_qt4(kernel) - - -@register_integration('wx') -def loop_wx(kernel): - """Start a kernel with wx event loop support.""" - - import wx - from IPython.lib.guisupport import start_event_loop_wx - - if _use_appnope() and kernel._darwin_app_nap: - # we don't hook up App Nap contexts for Wx, - # just disable it outright. - from appnope import nope - nope() - - doi = kernel.do_one_iteration - # Wx uses milliseconds - poll_interval = int(1000*kernel._poll_interval) - - # We have to put the wx.Timer in a wx.Frame for it to fire properly. - # We make the Frame hidden when we create it in the main app below. - class TimerFrame(wx.Frame): - def __init__(self, func): - wx.Frame.__init__(self, None, -1) - self.timer = wx.Timer(self) - # Units for the timer are in milliseconds - self.timer.Start(poll_interval) - self.Bind(wx.EVT_TIMER, self.on_timer) - self.func = func - - def on_timer(self, event): - self.func() - - # We need a custom wx.App to create our Frame subclass that has the - # wx.Timer to drive the ZMQ event loop. - class IPWxApp(wx.App): - def OnInit(self): - self.frame = TimerFrame(doi) - self.frame.Show(False) - return True - - # The redirect=False here makes sure that wx doesn't replace - # sys.stdout/stderr with its own classes. - kernel.app = IPWxApp(redirect=False) - - # The import of wx on Linux sets the handler for signal.SIGINT - # to 0. This is a bug in wx or gtk. We fix by just setting it - # back to the Python default. - import signal - if not callable(signal.getsignal(signal.SIGINT)): - signal.signal(signal.SIGINT, signal.default_int_handler) - - start_event_loop_wx(kernel.app) - - -@register_integration('tk') -def loop_tk(kernel): - """Start a kernel with the Tk event loop.""" - - try: - from tkinter import Tk # Py 3 - except ImportError: - from Tkinter import Tk # Py 2 - doi = kernel.do_one_iteration - # Tk uses milliseconds - poll_interval = int(1000*kernel._poll_interval) - # For Tkinter, we create a Tk object and call its withdraw method. - class Timer(object): - def __init__(self, func): - self.app = Tk() - self.app.withdraw() - self.func = func - - def on_timer(self): - self.func() - self.app.after(poll_interval, self.on_timer) - - def start(self): - self.on_timer() # Call it once to get things going. - self.app.mainloop() - - kernel.timer = Timer(doi) - kernel.timer.start() - - -@register_integration('gtk') -def loop_gtk(kernel): - """Start the kernel, coordinating with the GTK event loop""" - from .gui.gtkembed import GTKEmbed - - gtk_kernel = GTKEmbed(kernel) - gtk_kernel.start() - - -@register_integration('gtk3') -def loop_gtk3(kernel): - """Start the kernel, coordinating with the GTK event loop""" - from .gui.gtk3embed import GTKEmbed - - gtk_kernel = GTKEmbed(kernel) - gtk_kernel.start() - - -@register_integration('osx') -def loop_cocoa(kernel): - """Start the kernel, coordinating with the Cocoa CFRunLoop event loop - via the matplotlib MacOSX backend. - """ - import matplotlib - if matplotlib.__version__ < '1.1.0': - kernel.log.warn( - "MacOSX backend in matplotlib %s doesn't have a Timer, " - "falling back on Tk for CFRunLoop integration. Note that " - "even this won't work if Tk is linked against X11 instead of " - "Cocoa (e.g. EPD). To use the MacOSX backend in the kernel, " - "you must use matplotlib >= 1.1.0, or a native libtk." - ) - return loop_tk(kernel) - - from matplotlib.backends.backend_macosx import TimerMac, show - - # scale interval for sec->ms - poll_interval = int(1000*kernel._poll_interval) - - real_excepthook = sys.excepthook - def handle_int(etype, value, tb): - """don't let KeyboardInterrupts look like crashes""" - if etype is KeyboardInterrupt: - io.raw_print("KeyboardInterrupt caught in CFRunLoop") - else: - real_excepthook(etype, value, tb) - - # add doi() as a Timer to the CFRunLoop - def doi(): - # restore excepthook during IPython code - sys.excepthook = real_excepthook - kernel.do_one_iteration() - # and back: - sys.excepthook = handle_int - - t = TimerMac(poll_interval) - t.add_callback(doi) - t.start() - - # but still need a Poller for when there are no active windows, - # during which time mainloop() returns immediately - poller = zmq.Poller() - if kernel.control_stream: - poller.register(kernel.control_stream.socket, zmq.POLLIN) - for stream in kernel.shell_streams: - poller.register(stream.socket, zmq.POLLIN) - - while True: - try: - # double nested try/except, to properly catch KeyboardInterrupt - # due to pyzmq Issue #130 - try: - # don't let interrupts during mainloop invoke crash_handler: - sys.excepthook = handle_int - show.mainloop() - sys.excepthook = real_excepthook - # use poller if mainloop returned (no windows) - # scale by extra factor of 10, since it's a real poll - poller.poll(10*poll_interval) - kernel.do_one_iteration() - except: - raise - except KeyboardInterrupt: - # Ctrl-C shouldn't crash the kernel - io.raw_print("KeyboardInterrupt caught in kernel") - finally: - # ensure excepthook is restored - sys.excepthook = real_excepthook - - - -def enable_gui(gui, kernel=None): - """Enable integration with a given GUI""" - if gui not in loop_map: - e = "Invalid GUI request %r, valid ones are:%s" % (gui, loop_map.keys()) - raise ValueError(e) - if kernel is None: - if Application.initialized(): - kernel = getattr(Application.instance(), 'kernel', None) - if kernel is None: - raise RuntimeError("You didn't specify a kernel," - " and no IPython Application with a kernel appears to be running." - ) - loop = loop_map[gui] - if loop and kernel.eventloop is not None and kernel.eventloop is not loop: - raise RuntimeError("Cannot activate multiple GUI eventloops") - kernel.eventloop = loop diff --git a/ipython_kernel/gui/__init__.py b/ipython_kernel/gui/__init__.py deleted file mode 100644 index 1351f3c..0000000 --- a/ipython_kernel/gui/__init__.py +++ /dev/null @@ -1,15 +0,0 @@ -"""GUI support for the IPython ZeroMQ kernel. - -This package contains the various toolkit-dependent utilities we use to enable -coordination between the IPython kernel and the event loops of the various GUI -toolkits. -""" - -#----------------------------------------------------------------------------- -# Copyright (C) 2010-2011 The IPython Development Team. -# -# Distributed under the terms of the BSD License. -# -# The full license is in the file COPYING.txt, distributed as part of this -# software. -#----------------------------------------------------------------------------- diff --git a/ipython_kernel/gui/gtk3embed.py b/ipython_kernel/gui/gtk3embed.py deleted file mode 100644 index f70a6f4..0000000 --- a/ipython_kernel/gui/gtk3embed.py +++ /dev/null @@ -1,85 +0,0 @@ -"""GUI support for the IPython ZeroMQ kernel - GTK toolkit support. -""" -#----------------------------------------------------------------------------- -# Copyright (C) 2010-2011 The IPython Development Team -# -# Distributed under the terms of the BSD License. The full license is in -# the file COPYING.txt, distributed as part of this software. -#----------------------------------------------------------------------------- - -#----------------------------------------------------------------------------- -# Imports -#----------------------------------------------------------------------------- -# stdlib -import sys - -# Third-party -from gi.repository import GObject, Gtk - -#----------------------------------------------------------------------------- -# Classes and functions -#----------------------------------------------------------------------------- - -class GTKEmbed(object): - """A class to embed a kernel into the GTK main event loop. - """ - def __init__(self, kernel): - self.kernel = kernel - # These two will later store the real gtk functions when we hijack them - self.gtk_main = None - self.gtk_main_quit = None - - def start(self): - """Starts the GTK main event loop and sets our kernel startup routine. - """ - # Register our function to initiate the kernel and start gtk - GObject.idle_add(self._wire_kernel) - Gtk.main() - - def _wire_kernel(self): - """Initializes the kernel inside GTK. - - This is meant to run only once at startup, so it does its job and - returns False to ensure it doesn't get run again by GTK. - """ - self.gtk_main, self.gtk_main_quit = self._hijack_gtk() - GObject.timeout_add(int(1000*self.kernel._poll_interval), - self.iterate_kernel) - return False - - def iterate_kernel(self): - """Run one iteration of the kernel and return True. - - GTK timer functions must return True to be called again, so we make the - call to :meth:`do_one_iteration` and then return True for GTK. - """ - self.kernel.do_one_iteration() - return True - - def stop(self): - # FIXME: this one isn't getting called because we have no reliable - # kernel shutdown. We need to fix that: once the kernel has a - # shutdown mechanism, it can call this. - self.gtk_main_quit() - sys.exit() - - def _hijack_gtk(self): - """Hijack a few key functions in GTK for IPython integration. - - Modifies pyGTK's main and main_quit with a dummy so user code does not - block IPython. This allows us to use %run to run arbitrary pygtk - scripts from a long-lived IPython session, and when they attempt to - start or stop - - Returns - ------- - The original functions that have been hijacked: - - Gtk.main - - Gtk.main_quit - """ - def dummy(*args, **kw): - pass - # save and trap main and main_quit from gtk - orig_main, Gtk.main = Gtk.main, dummy - orig_main_quit, Gtk.main_quit = Gtk.main_quit, dummy - return orig_main, orig_main_quit diff --git a/ipython_kernel/gui/gtkembed.py b/ipython_kernel/gui/gtkembed.py deleted file mode 100644 index d9dc7e6..0000000 --- a/ipython_kernel/gui/gtkembed.py +++ /dev/null @@ -1,86 +0,0 @@ -"""GUI support for the IPython ZeroMQ kernel - GTK toolkit support. -""" -#----------------------------------------------------------------------------- -# Copyright (C) 2010-2011 The IPython Development Team -# -# Distributed under the terms of the BSD License. The full license is in -# the file COPYING.txt, distributed as part of this software. -#----------------------------------------------------------------------------- - -#----------------------------------------------------------------------------- -# Imports -#----------------------------------------------------------------------------- -# stdlib -import sys - -# Third-party -import gobject -import gtk - -#----------------------------------------------------------------------------- -# Classes and functions -#----------------------------------------------------------------------------- - -class GTKEmbed(object): - """A class to embed a kernel into the GTK main event loop. - """ - def __init__(self, kernel): - self.kernel = kernel - # These two will later store the real gtk functions when we hijack them - self.gtk_main = None - self.gtk_main_quit = None - - def start(self): - """Starts the GTK main event loop and sets our kernel startup routine. - """ - # Register our function to initiate the kernel and start gtk - gobject.idle_add(self._wire_kernel) - gtk.main() - - def _wire_kernel(self): - """Initializes the kernel inside GTK. - - This is meant to run only once at startup, so it does its job and - returns False to ensure it doesn't get run again by GTK. - """ - self.gtk_main, self.gtk_main_quit = self._hijack_gtk() - gobject.timeout_add(int(1000*self.kernel._poll_interval), - self.iterate_kernel) - return False - - def iterate_kernel(self): - """Run one iteration of the kernel and return True. - - GTK timer functions must return True to be called again, so we make the - call to :meth:`do_one_iteration` and then return True for GTK. - """ - self.kernel.do_one_iteration() - return True - - def stop(self): - # FIXME: this one isn't getting called because we have no reliable - # kernel shutdown. We need to fix that: once the kernel has a - # shutdown mechanism, it can call this. - self.gtk_main_quit() - sys.exit() - - def _hijack_gtk(self): - """Hijack a few key functions in GTK for IPython integration. - - Modifies pyGTK's main and main_quit with a dummy so user code does not - block IPython. This allows us to use %run to run arbitrary pygtk - scripts from a long-lived IPython session, and when they attempt to - start or stop - - Returns - ------- - The original functions that have been hijacked: - - gtk.main - - gtk.main_quit - """ - def dummy(*args, **kw): - pass - # save and trap main and main_quit from gtk - orig_main, gtk.main = gtk.main, dummy - orig_main_quit, gtk.main_quit = gtk.main_quit, dummy - return orig_main, orig_main_quit diff --git a/ipython_kernel/heartbeat.py b/ipython_kernel/heartbeat.py deleted file mode 100644 index 2ee6dc2..0000000 --- a/ipython_kernel/heartbeat.py +++ /dev/null @@ -1,68 +0,0 @@ -"""The client and server for a basic ping-pong style heartbeat. -""" - -#----------------------------------------------------------------------------- -# Copyright (C) 2008-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 -#----------------------------------------------------------------------------- - -import errno -import os -import socket -from threading import Thread - -import zmq - -from IPython.utils.localinterfaces import localhost - -#----------------------------------------------------------------------------- -# Code -#----------------------------------------------------------------------------- - - -class Heartbeat(Thread): - "A simple ping-pong style heartbeat that runs in a thread." - - def __init__(self, context, addr=None): - if addr is None: - addr = ('tcp', localhost(), 0) - Thread.__init__(self) - self.context = context - self.transport, self.ip, self.port = addr - if self.port == 0: - if addr[0] == 'tcp': - s = socket.socket() - # '*' means all interfaces to 0MQ, which is '' to socket.socket - s.bind(('' if self.ip == '*' else self.ip, 0)) - self.port = s.getsockname()[1] - s.close() - elif addr[0] == 'ipc': - self.port = 1 - while os.path.exists("%s-%s" % (self.ip, self.port)): - self.port = self.port + 1 - else: - raise ValueError("Unrecognized zmq transport: %s" % addr[0]) - self.addr = (self.ip, self.port) - self.daemon = True - - def run(self): - self.socket = self.context.socket(zmq.REP) - self.socket.linger = 1000 - c = ':' if self.transport == 'tcp' else '-' - self.socket.bind('%s://%s' % (self.transport, self.ip) + c + str(self.port)) - while True: - try: - zmq.device(zmq.FORWARDER, self.socket, self.socket) - except zmq.ZMQError as e: - if e.errno == errno.EINTR: - continue - else: - raise - else: - break diff --git a/ipython_kernel/inprocess/__init__.py b/ipython_kernel/inprocess/__init__.py deleted file mode 100644 index 8da7561..0000000 --- a/ipython_kernel/inprocess/__init__.py +++ /dev/null @@ -1,8 +0,0 @@ -from .channels import ( - InProcessChannel, - InProcessHBChannel, -) - -from .client import InProcessKernelClient -from .manager import InProcessKernelManager -from .blocking import BlockingInProcessKernelClient diff --git a/ipython_kernel/inprocess/blocking.py b/ipython_kernel/inprocess/blocking.py deleted file mode 100644 index a90b9ce..0000000 --- a/ipython_kernel/inprocess/blocking.py +++ /dev/null @@ -1,93 +0,0 @@ -""" Implements a fully blocking kernel client. - -Useful for test suites and blocking terminal interfaces. -""" -#----------------------------------------------------------------------------- -# Copyright (C) 2012 The IPython Development Team -# -# Distributed under the terms of the BSD License. The full license is in -# the file COPYING.txt, distributed as part of this software. -#----------------------------------------------------------------------------- - -try: - from queue import Queue, Empty # Py 3 -except ImportError: - from Queue import Queue, Empty # Py 2 - -# IPython imports -from IPython.utils.io import raw_print -from IPython.utils.traitlets import Type - -# Local imports -from .channels import ( - InProcessChannel, -) -from .client import InProcessKernelClient - -class BlockingInProcessChannel(InProcessChannel): - - def __init__(self, *args, **kwds): - super(BlockingInProcessChannel, self).__init__(*args, **kwds) - self._in_queue = Queue() - - def call_handlers(self, msg): - self._in_queue.put(msg) - - def get_msg(self, block=True, timeout=None): - """ Gets a message if there is one that is ready. """ - if timeout is None: - # Queue.get(timeout=None) has stupid uninteruptible - # behavior, so wait for a week instead - timeout = 604800 - return self._in_queue.get(block, timeout) - - def get_msgs(self): - """ Get all messages that are currently ready. """ - msgs = [] - while True: - try: - msgs.append(self.get_msg(block=False)) - except Empty: - break - return msgs - - def msg_ready(self): - """ Is there a message that has been received? """ - return not self._in_queue.empty() - - -class BlockingInProcessStdInChannel(BlockingInProcessChannel): - def call_handlers(self, msg): - """ Overridden for the in-process channel. - - This methods simply calls raw_input directly. - """ - msg_type = msg['header']['msg_type'] - if msg_type == 'input_request': - _raw_input = self.client.kernel._sys_raw_input - prompt = msg['content']['prompt'] - raw_print(prompt, end='') - self.client.input(_raw_input()) - -class BlockingInProcessKernelClient(InProcessKernelClient): - - # The classes to use for the various channels. - shell_channel_class = Type(BlockingInProcessChannel) - iopub_channel_class = Type(BlockingInProcessChannel) - stdin_channel_class = Type(BlockingInProcessStdInChannel) - - def wait_for_ready(self): - # Wait for kernel info reply on shell channel - while True: - msg = self.shell_channel.get_msg(block=True) - if msg['msg_type'] == 'kernel_info_reply': - self._handle_kernel_info_reply(msg) - break - - # Flush IOPub channel - while True: - try: - msg = self.iopub_channel.get_msg(block=True, timeout=0.2) - print(msg['msg_type']) - except Empty: - break diff --git a/ipython_kernel/inprocess/channels.py b/ipython_kernel/inprocess/channels.py deleted file mode 100644 index 0b78d99..0000000 --- a/ipython_kernel/inprocess/channels.py +++ /dev/null @@ -1,97 +0,0 @@ -"""A kernel client for in-process kernels.""" - -# Copyright (c) IPython Development Team. -# Distributed under the terms of the Modified BSD License. - -from jupyter_client.channelsabc import HBChannelABC - -from .socket import DummySocket - -#----------------------------------------------------------------------------- -# Channel classes -#----------------------------------------------------------------------------- - -class InProcessChannel(object): - """Base class for in-process channels.""" - proxy_methods = [] - - def __init__(self, client=None): - super(InProcessChannel, self).__init__() - self.client = client - self._is_alive = False - - def is_alive(self): - return self._is_alive - - def start(self): - self._is_alive = True - - def stop(self): - self._is_alive = False - - def call_handlers(self, msg): - """ This method is called in the main thread when a message arrives. - - Subclasses should override this method to handle incoming messages. - """ - raise NotImplementedError('call_handlers must be defined in a subclass.') - - def flush(self, timeout=1.0): - pass - - - def call_handlers_later(self, *args, **kwds): - """ Call the message handlers later. - - The default implementation just calls the handlers immediately, but this - method exists so that GUI toolkits can defer calling the handlers until - after the event loop has run, as expected by GUI frontends. - """ - self.call_handlers(*args, **kwds) - - def process_events(self): - """ Process any pending GUI events. - - This method will be never be called from a frontend without an event - loop (e.g., a terminal frontend). - """ - raise NotImplementedError - - - -class InProcessHBChannel(object): - """A dummy heartbeat channel interface for in-process kernels. - - Normally we use the heartbeat to check that the kernel process is alive. - When the kernel is in-process, that doesn't make sense, but clients still - expect this interface. - """ - - time_to_dead = 3.0 - - def __init__(self, client=None): - super(InProcessHBChannel, self).__init__() - self.client = client - self._is_alive = False - self._pause = True - - def is_alive(self): - return self._is_alive - - def start(self): - self._is_alive = True - - def stop(self): - self._is_alive = False - - def pause(self): - self._pause = True - - def unpause(self): - self._pause = False - - def is_beating(self): - return not self._pause - - -HBChannelABC.register(InProcessHBChannel) diff --git a/ipython_kernel/inprocess/client.py b/ipython_kernel/inprocess/client.py deleted file mode 100644 index 3e2b8e8..0000000 --- a/ipython_kernel/inprocess/client.py +++ /dev/null @@ -1,157 +0,0 @@ -"""A client for in-process kernels.""" - -#----------------------------------------------------------------------------- -# Copyright (C) 2012 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 -#----------------------------------------------------------------------------- - -# IPython imports -from ipython_kernel.inprocess.socket import DummySocket -from IPython.utils.traitlets import Type, Instance -from jupyter_client.clientabc import KernelClientABC -from jupyter_client.client import KernelClient - -# Local imports -from .channels import ( - InProcessChannel, - InProcessHBChannel, - -) - -#----------------------------------------------------------------------------- -# Main kernel Client class -#----------------------------------------------------------------------------- - -class InProcessKernelClient(KernelClient): - """A client for an in-process kernel. - - This class implements the interface of - `jupyter_client.clientabc.KernelClientABC` and allows - (asynchronous) frontends to be used seamlessly with an in-process kernel. - - See `jupyter_client.client.KernelClient` for docstrings. - """ - - # The classes to use for the various channels. - shell_channel_class = Type(InProcessChannel) - iopub_channel_class = Type(InProcessChannel) - stdin_channel_class = Type(InProcessChannel) - hb_channel_class = Type(InProcessHBChannel) - - kernel = Instance('ipython_kernel.inprocess.ipkernel.InProcessKernel', - allow_none=True) - - #-------------------------------------------------------------------------- - # Channel management methods - #-------------------------------------------------------------------------- - - def start_channels(self, *args, **kwargs): - super(InProcessKernelClient, self).start_channels(self) - self.kernel.frontends.append(self) - - @property - def shell_channel(self): - if self._shell_channel is None: - self._shell_channel = self.shell_channel_class(self) - return self._shell_channel - - @property - def iopub_channel(self): - if self._iopub_channel is None: - self._iopub_channel = self.iopub_channel_class(self) - return self._iopub_channel - - @property - def stdin_channel(self): - if self._stdin_channel is None: - self._stdin_channel = self.stdin_channel_class(self) - return self._stdin_channel - - @property - def hb_channel(self): - if self._hb_channel is None: - self._hb_channel = self.hb_channel_class(self) - return self._hb_channel - - # Methods for sending specific messages - # ------------------------------------- - - def execute(self, code, silent=False, store_history=True, - user_expressions={}, allow_stdin=None): - if allow_stdin is None: - allow_stdin = self.allow_stdin - content = dict(code=code, silent=silent, store_history=store_history, - user_expressions=user_expressions, - allow_stdin=allow_stdin) - msg = self.session.msg('execute_request', content) - self._dispatch_to_kernel(msg) - return msg['header']['msg_id'] - - def complete(self, code, cursor_pos=None): - if cursor_pos is None: - cursor_pos = len(code) - content = dict(code=code, cursor_pos=cursor_pos) - msg = self.session.msg('complete_request', content) - self._dispatch_to_kernel(msg) - return msg['header']['msg_id'] - - def inspect(self, code, cursor_pos=None, detail_level=0): - if cursor_pos is None: - cursor_pos = len(code) - content = dict(code=code, cursor_pos=cursor_pos, - detail_level=detail_level, - ) - msg = self.session.msg('inspect_request', content) - self._dispatch_to_kernel(msg) - return msg['header']['msg_id'] - - def history(self, raw=True, output=False, hist_access_type='range', **kwds): - content = dict(raw=raw, output=output, - hist_access_type=hist_access_type, **kwds) - msg = self.session.msg('history_request', content) - self._dispatch_to_kernel(msg) - return msg['header']['msg_id'] - - def shutdown(self, restart=False): - # FIXME: What to do here? - raise NotImplementedError('Cannot shutdown in-process kernel') - - def kernel_info(self): - """Request kernel info.""" - msg = self.session.msg('kernel_info_request') - self._dispatch_to_kernel(msg) - return msg['header']['msg_id'] - - def input(self, string): - if self.kernel is None: - raise RuntimeError('Cannot send input reply. No kernel exists.') - self.kernel.raw_input_str = string - - - def _dispatch_to_kernel(self, msg): - """ Send a message to the kernel and handle a reply. - """ - kernel = self.kernel - if kernel is None: - raise RuntimeError('Cannot send request. No kernel exists.') - - stream = DummySocket() - self.session.send(stream, msg) - msg_parts = stream.recv_multipart() - kernel.dispatch_shell(stream, msg_parts) - - idents, reply_msg = self.session.recv(stream, copy=False) - self.shell_channel.call_handlers_later(reply_msg) - - -#----------------------------------------------------------------------------- -# ABC Registration -#----------------------------------------------------------------------------- - -KernelClientABC.register(InProcessKernelClient) diff --git a/ipython_kernel/inprocess/ipkernel.py b/ipython_kernel/inprocess/ipkernel.py deleted file mode 100644 index b00503d..0000000 --- a/ipython_kernel/inprocess/ipkernel.py +++ /dev/null @@ -1,171 +0,0 @@ -"""An in-process kernel""" - -# Copyright (c) IPython Development Team. -# Distributed under the terms of the Modified BSD License. - -from contextlib import contextmanager -import logging -import sys - -from IPython.core.interactiveshell import InteractiveShellABC -from jupyter_client.jsonutil import json_clean -from IPython.utils.traitlets import Any, Enum, Instance, List, Type -from ipython_kernel.ipkernel import IPythonKernel -from ipython_kernel.zmqshell import ZMQInteractiveShell - -from .socket import DummySocket - -#----------------------------------------------------------------------------- -# Main kernel class -#----------------------------------------------------------------------------- - -class InProcessKernel(IPythonKernel): - - #------------------------------------------------------------------------- - # InProcessKernel interface - #------------------------------------------------------------------------- - - # The frontends connected to this kernel. - frontends = List( - Instance('ipython_kernel.inprocess.client.InProcessKernelClient', - allow_none=True) - ) - - # The GUI environment that the kernel is running under. This need not be - # specified for the normal operation for the kernel, but is required for - # IPython's GUI support (including pylab). The default is 'inline' because - # it is safe under all GUI toolkits. - gui = Enum(('tk', 'gtk', 'wx', 'qt', 'qt4', 'inline'), - default_value='inline') - - raw_input_str = Any() - stdout = Any() - stderr = Any() - - #------------------------------------------------------------------------- - # Kernel interface - #------------------------------------------------------------------------- - - shell_class = Type(allow_none=True) - shell_streams = List() - control_stream = Any() - iopub_socket = Instance(DummySocket, ()) - stdin_socket = Instance(DummySocket, ()) - - def __init__(self, **traits): - super(InProcessKernel, self).__init__(**traits) - - self.iopub_socket.on_trait_change(self._io_dispatch, 'message_sent') - self.shell.kernel = self - - def execute_request(self, stream, ident, parent): - """ Override for temporary IO redirection. """ - with self._redirected_io(): - super(InProcessKernel, self).execute_request(stream, ident, parent) - - def start(self): - """ Override registration of dispatchers for streams. """ - self.shell.exit_now = False - - def _abort_queue(self, stream): - """ The in-process kernel doesn't abort requests. """ - pass - - def _input_request(self, prompt, ident, parent, password=False): - # Flush output before making the request. - self.raw_input_str = None - sys.stderr.flush() - sys.stdout.flush() - - # Send the input request. - content = json_clean(dict(prompt=prompt, password=password)) - msg = self.session.msg(u'input_request', content, parent) - for frontend in self.frontends: - if frontend.session.session == parent['header']['session']: - frontend.stdin_channel.call_handlers(msg) - break - else: - logging.error('No frontend found for raw_input request') - return str() - - # Await a response. - while self.raw_input_str is None: - frontend.stdin_channel.process_events() - return self.raw_input_str - - #------------------------------------------------------------------------- - # Protected interface - #------------------------------------------------------------------------- - - @contextmanager - def _redirected_io(self): - """ Temporarily redirect IO to the kernel. - """ - sys_stdout, sys_stderr = sys.stdout, sys.stderr - sys.stdout, sys.stderr = self.stdout, self.stderr - yield - sys.stdout, sys.stderr = sys_stdout, sys_stderr - - #------ Trait change handlers -------------------------------------------- - - def _io_dispatch(self): - """ Called when a message is sent to the IO socket. - """ - ident, msg = self.session.recv(self.iopub_socket, copy=False) - for frontend in self.frontends: - frontend.iopub_channel.call_handlers(msg) - - #------ Trait initializers ----------------------------------------------- - - def _log_default(self): - return logging.getLogger(__name__) - - def _session_default(self): - from ipython_kernel.session import Session - return Session(parent=self, key=b'') - - def _shell_class_default(self): - return InProcessInteractiveShell - - def _stdout_default(self): - from ipython_kernel.iostream import OutStream - return OutStream(self.session, self.iopub_socket, u'stdout', pipe=False) - - def _stderr_default(self): - from ipython_kernel.iostream import OutStream - return OutStream(self.session, self.iopub_socket, u'stderr', pipe=False) - -#----------------------------------------------------------------------------- -# Interactive shell subclass -#----------------------------------------------------------------------------- - -class InProcessInteractiveShell(ZMQInteractiveShell): - - kernel = Instance('ipython_kernel.inprocess.ipkernel.InProcessKernel', - allow_none=True) - - #------------------------------------------------------------------------- - # InteractiveShell interface - #------------------------------------------------------------------------- - - def enable_gui(self, gui=None): - """Enable GUI integration for the kernel.""" - from ipython_kernel.eventloops import enable_gui - if not gui: - gui = self.kernel.gui - return enable_gui(gui, kernel=self.kernel) - - def enable_matplotlib(self, gui=None): - """Enable matplotlib integration for the kernel.""" - if not gui: - gui = self.kernel.gui - return super(InProcessInteractiveShell, self).enable_matplotlib(gui) - - def enable_pylab(self, gui=None, import_all=True, welcome_message=False): - """Activate pylab support at runtime.""" - if not gui: - gui = self.kernel.gui - return super(InProcessInteractiveShell, self).enable_pylab(gui, import_all, - welcome_message) - -InteractiveShellABC.register(InProcessInteractiveShell) diff --git a/ipython_kernel/inprocess/manager.py b/ipython_kernel/inprocess/manager.py deleted file mode 100644 index 1dc7eb5..0000000 --- a/ipython_kernel/inprocess/manager.py +++ /dev/null @@ -1,72 +0,0 @@ -"""A kernel manager for in-process kernels.""" - -# Copyright (c) IPython Development Team. -# Distributed under the terms of the Modified BSD License. - -from IPython.utils.traitlets import Instance, DottedObjectName -from jupyter_client.managerabc import KernelManagerABC -from jupyter_client.manager import KernelManager -from jupyter_client.session import Session - - -class InProcessKernelManager(KernelManager): - """A manager for an in-process kernel. - - This class implements the interface of - `jupyter_client.kernelmanagerabc.KernelManagerABC` and allows - (asynchronous) frontends to be used seamlessly with an in-process kernel. - - See `jupyter_client.kernelmanager.KernelManager` for docstrings. - """ - - # The kernel process with which the KernelManager is communicating. - kernel = Instance('ipython_kernel.inprocess.ipkernel.InProcessKernel', - allow_none=True) - # the client class for KM.client() shortcut - client_class = DottedObjectName('ipython_kernel.inprocess.BlockingInProcessKernelClient') - - def _session_default(self): - # don't sign in-process messages - return Session(key=b'', parent=self) - - #-------------------------------------------------------------------------- - # Kernel management methods - #-------------------------------------------------------------------------- - - def start_kernel(self, **kwds): - from ipython_kernel.inprocess.ipkernel import InProcessKernel - self.kernel = InProcessKernel(parent=self, session=self.session) - - def shutdown_kernel(self): - self._kill_kernel() - - def restart_kernel(self, now=False, **kwds): - self.shutdown_kernel() - self.start_kernel(**kwds) - - @property - def has_kernel(self): - return self.kernel is not None - - def _kill_kernel(self): - self.kernel = None - - def interrupt_kernel(self): - raise NotImplementedError("Cannot interrupt in-process kernel.") - - def signal_kernel(self, signum): - raise NotImplementedError("Cannot signal in-process kernel.") - - def is_alive(self): - return self.kernel is not None - - def client(self, **kwargs): - kwargs['kernel'] = self.kernel - return super(InProcessKernelManager, self).client(**kwargs) - - -#----------------------------------------------------------------------------- -# ABC Registration -#----------------------------------------------------------------------------- - -KernelManagerABC.register(InProcessKernelManager) diff --git a/ipython_kernel/inprocess/socket.py b/ipython_kernel/inprocess/socket.py deleted file mode 100644 index d68f3fc..0000000 --- a/ipython_kernel/inprocess/socket.py +++ /dev/null @@ -1,65 +0,0 @@ -""" Defines a dummy socket implementing (part of) the zmq.Socket interface. """ - -#----------------------------------------------------------------------------- -# Copyright (C) 2012 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 -#----------------------------------------------------------------------------- - -# Standard library imports. -import abc -try: - from queue import Queue # Py 3 -except ImportError: - from Queue import Queue # Py 2 - -# System library imports. -import zmq - -# Local imports. -from IPython.utils.traitlets import HasTraits, Instance, Int -from IPython.utils.py3compat import with_metaclass - -#----------------------------------------------------------------------------- -# Generic socket interface -#----------------------------------------------------------------------------- - -class SocketABC(with_metaclass(abc.ABCMeta, object)): - @abc.abstractmethod - def recv_multipart(self, flags=0, copy=True, track=False): - raise NotImplementedError - - @abc.abstractmethod - def send_multipart(self, msg_parts, flags=0, copy=True, track=False): - raise NotImplementedError - -SocketABC.register(zmq.Socket) - -#----------------------------------------------------------------------------- -# Dummy socket class -#----------------------------------------------------------------------------- - -class DummySocket(HasTraits): - """ A dummy socket implementing (part of) the zmq.Socket interface. """ - - queue = Instance(Queue, ()) - message_sent = Int(0) # Should be an Event - - #------------------------------------------------------------------------- - # Socket interface - #------------------------------------------------------------------------- - - def recv_multipart(self, flags=0, copy=True, track=False): - return self.queue.get_nowait() - - def send_multipart(self, msg_parts, flags=0, copy=True, track=False): - msg_parts = list(map(zmq.Message, msg_parts)) - self.queue.put_nowait(msg_parts) - self.message_sent += 1 - -SocketABC.register(DummySocket) diff --git a/ipython_kernel/inprocess/tests/__init__.py b/ipython_kernel/inprocess/tests/__init__.py deleted file mode 100644 index e69de29..0000000 --- a/ipython_kernel/inprocess/tests/__init__.py +++ /dev/null diff --git a/ipython_kernel/inprocess/tests/test_kernel.py b/ipython_kernel/inprocess/tests/test_kernel.py deleted file mode 100644 index c5b84e0..0000000 --- a/ipython_kernel/inprocess/tests/test_kernel.py +++ /dev/null @@ -1,68 +0,0 @@ -# Copyright (c) IPython Development Team. -# Distributed under the terms of the Modified BSD License. - -from __future__ import print_function - -import sys -import unittest - -from ipython_kernel.inprocess.blocking import BlockingInProcessKernelClient -from ipython_kernel.inprocess.manager import InProcessKernelManager -from ipython_kernel.inprocess.ipkernel import InProcessKernel -from ipython_kernel.tests.utils import assemble_output -from IPython.testing.decorators import skipif_not_matplotlib -from IPython.utils.io import capture_output -from IPython.utils import py3compat - -if py3compat.PY3: - from io import StringIO -else: - from StringIO import StringIO - - -class InProcessKernelTestCase(unittest.TestCase): - - def setUp(self): - self.km = InProcessKernelManager() - self.km.start_kernel() - self.kc = self.km.client() - self.kc.start_channels() - self.kc.wait_for_ready() - - @skipif_not_matplotlib - def test_pylab(self): - """Does %pylab work in the in-process kernel?""" - kc = self.kc - kc.execute('%pylab') - out, err = assemble_output(kc.iopub_channel) - self.assertIn('matplotlib', out) - - def test_raw_input(self): - """ Does the in-process kernel handle raw_input correctly? - """ - io = StringIO('foobar\n') - sys_stdin = sys.stdin - sys.stdin = io - try: - if py3compat.PY3: - self.kc.execute('x = input()') - else: - self.kc.execute('x = raw_input()') - finally: - sys.stdin = sys_stdin - self.assertEqual(self.km.kernel.shell.user_ns.get('x'), 'foobar') - - def test_stdout(self): - """ Does the in-process kernel correctly capture IO? - """ - kernel = InProcessKernel() - - with capture_output() as io: - kernel.shell.run_cell('print("foo")') - self.assertEqual(io.stdout, 'foo\n') - - kc = BlockingInProcessKernelClient(kernel=kernel, session=kernel.session) - kernel.frontends.append(kc) - kc.execute('print("bar")') - out, err = assemble_output(kc.iopub_channel) - self.assertEqual(out, 'bar\n') diff --git a/ipython_kernel/inprocess/tests/test_kernelmanager.py b/ipython_kernel/inprocess/tests/test_kernelmanager.py deleted file mode 100644 index a26181d..0000000 --- a/ipython_kernel/inprocess/tests/test_kernelmanager.py +++ /dev/null @@ -1,108 +0,0 @@ -# Copyright (c) IPython Development Team. -# Distributed under the terms of the Modified BSD License. - -from __future__ import print_function - -import unittest - -from ipython_kernel.inprocess.blocking import BlockingInProcessKernelClient -from ipython_kernel.inprocess.manager import InProcessKernelManager - -#----------------------------------------------------------------------------- -# Test case -#----------------------------------------------------------------------------- - -class InProcessKernelManagerTestCase(unittest.TestCase): - - def test_interface(self): - """ Does the in-process kernel manager implement the basic KM interface? - """ - km = InProcessKernelManager() - self.assert_(not km.has_kernel) - - km.start_kernel() - self.assert_(km.has_kernel) - self.assert_(km.kernel is not None) - - kc = km.client() - self.assert_(not kc.channels_running) - - kc.start_channels() - self.assert_(kc.channels_running) - - old_kernel = km.kernel - km.restart_kernel() - self.assertIsNotNone(km.kernel) - self.assertNotEquals(km.kernel, old_kernel) - - km.shutdown_kernel() - self.assert_(not km.has_kernel) - - self.assertRaises(NotImplementedError, km.interrupt_kernel) - self.assertRaises(NotImplementedError, km.signal_kernel, 9) - - kc.stop_channels() - self.assert_(not kc.channels_running) - - def test_execute(self): - """ Does executing code in an in-process kernel work? - """ - km = InProcessKernelManager() - km.start_kernel() - kc = km.client() - kc.start_channels() - kc.wait_for_ready() - kc.execute('foo = 1') - self.assertEquals(km.kernel.shell.user_ns['foo'], 1) - - def test_complete(self): - """ Does requesting completion from an in-process kernel work? - """ - km = InProcessKernelManager() - km.start_kernel() - kc = km.client() - kc.start_channels() - kc.wait_for_ready() - km.kernel.shell.push({'my_bar': 0, 'my_baz': 1}) - kc.complete('my_ba', 5) - msg = kc.get_shell_msg() - self.assertEqual(msg['header']['msg_type'], 'complete_reply') - self.assertEqual(sorted(msg['content']['matches']), - ['my_bar', 'my_baz']) - - def test_inspect(self): - """ Does requesting object information from an in-process kernel work? - """ - km = InProcessKernelManager() - km.start_kernel() - kc = km.client() - kc.start_channels() - kc.wait_for_ready() - km.kernel.shell.user_ns['foo'] = 1 - kc.inspect('foo') - msg = kc.get_shell_msg() - self.assertEqual(msg['header']['msg_type'], 'inspect_reply') - content = msg['content'] - assert content['found'] - text = content['data']['text/plain'] - self.assertIn('int', text) - - def test_history(self): - """ Does requesting history from an in-process kernel work? - """ - km = InProcessKernelManager() - km.start_kernel() - kc = km.client() - kc.start_channels() - kc.wait_for_ready() - kc.execute('%who') - kc.history(hist_access_type='tail', n=1) - msg = kc.shell_channel.get_msgs()[-1] - self.assertEquals(msg['header']['msg_type'], 'history_reply') - history = msg['content']['history'] - self.assertEquals(len(history), 1) - self.assertEquals(history[0][2], '%who') - - -if __name__ == '__main__': - unittest.main() diff --git a/ipython_kernel/iostream.py b/ipython_kernel/iostream.py deleted file mode 100644 index d03fd59..0000000 --- a/ipython_kernel/iostream.py +++ /dev/null @@ -1,249 +0,0 @@ -"""Wrappers for forwarding stdout/stderr over zmq""" - -# Copyright (c) IPython Development Team. -# Distributed under the terms of the Modified BSD License. - -import os -import threading -import time -import uuid -from io import StringIO, UnsupportedOperation - -import zmq -from zmq.eventloop.ioloop import IOLoop - -from .session import extract_header - -from IPython.utils import py3compat -from IPython.utils.py3compat import unicode_type -from IPython.utils.warn import warn - -#----------------------------------------------------------------------------- -# Globals -#----------------------------------------------------------------------------- - -MASTER = 0 -CHILD = 1 - -#----------------------------------------------------------------------------- -# Stream classes -#----------------------------------------------------------------------------- - -class OutStream(object): - """A file like object that publishes the stream to a 0MQ PUB socket.""" - - # The time interval between automatic flushes, in seconds. - _subprocess_flush_limit = 256 - flush_interval = 0.05 - topic=None - - def __init__(self, session, pub_socket, name, pipe=True): - self.encoding = 'UTF-8' - self.session = session - self.pub_socket = pub_socket - self.name = name - self.topic = b'stream.' + py3compat.cast_bytes(name) - self.parent_header = {} - self._new_buffer() - self._buffer_lock = threading.Lock() - self._master_pid = os.getpid() - self._master_thread = threading.current_thread().ident - self._pipe_pid = os.getpid() - self._pipe_flag = pipe - if pipe: - self._setup_pipe_in() - - def _setup_pipe_in(self): - """setup listening pipe for subprocesses""" - ctx = self.pub_socket.context - - # use UUID to authenticate pipe messages - self._pipe_uuid = uuid.uuid4().bytes - - self._pipe_in = ctx.socket(zmq.PULL) - self._pipe_in.linger = 0 - try: - self._pipe_port = self._pipe_in.bind_to_random_port("tcp://127.0.0.1") - except zmq.ZMQError as e: - warn("Couldn't bind IOStream to 127.0.0.1: %s" % e + - "\nsubprocess output will be unavailable." - ) - self._pipe_flag = False - self._pipe_in.close() - del self._pipe_in - return - self._pipe_poller = zmq.Poller() - self._pipe_poller.register(self._pipe_in, zmq.POLLIN) - if IOLoop.initialized(): - # subprocess flush should trigger flush - # if kernel is idle - IOLoop.instance().add_handler(self._pipe_in, - lambda s, event: self.flush(), - IOLoop.READ, - ) - - def _setup_pipe_out(self): - # must be new context after fork - ctx = zmq.Context() - self._pipe_pid = os.getpid() - self._pipe_out = ctx.socket(zmq.PUSH) - self._pipe_out_lock = threading.Lock() - self._pipe_out.connect("tcp://127.0.0.1:%i" % self._pipe_port) - - def _is_master_process(self): - return os.getpid() == self._master_pid - - def _is_master_thread(self): - return threading.current_thread().ident == self._master_thread - - def _have_pipe_out(self): - return os.getpid() == self._pipe_pid - - def _check_mp_mode(self): - """check for forks, and switch to zmq pipeline if necessary""" - if not self._pipe_flag or self._is_master_process(): - return MASTER - else: - if not self._have_pipe_out(): - self._flush_buffer() - # setup a new out pipe - self._setup_pipe_out() - return CHILD - - def set_parent(self, parent): - self.parent_header = extract_header(parent) - - def close(self): - self.pub_socket = None - - @property - def closed(self): - return self.pub_socket is None - - def _flush_from_subprocesses(self): - """flush possible pub data from subprocesses into my buffer""" - if not self._pipe_flag or not self._is_master_process(): - return - for i in range(self._subprocess_flush_limit): - if self._pipe_poller.poll(0): - msg = self._pipe_in.recv_multipart() - if msg[0] != self._pipe_uuid: - continue - else: - self._buffer.write(msg[1].decode(self.encoding, 'replace')) - # this always means a flush, - # so reset our timer - self._start = 0 - else: - break - - def _schedule_flush(self): - """schedule a flush in the main thread - - only works with a tornado/pyzmq eventloop running - """ - if IOLoop.initialized(): - IOLoop.instance().add_callback(self.flush) - else: - # no async loop, at least force the timer - self._start = 0 - - def flush(self): - """trigger actual zmq send""" - if self.pub_socket is None: - raise ValueError(u'I/O operation on closed file') - - mp_mode = self._check_mp_mode() - - if mp_mode != CHILD: - # we are master - if not self._is_master_thread(): - # sub-threads must not trigger flush directly, - # but at least they can schedule an async flush, or force the timer. - self._schedule_flush() - return - - self._flush_from_subprocesses() - data = self._flush_buffer() - - if data: - content = {u'name':self.name, u'text':data} - msg = self.session.send(self.pub_socket, u'stream', content=content, - parent=self.parent_header, ident=self.topic) - - if hasattr(self.pub_socket, 'flush'): - # socket itself has flush (presumably ZMQStream) - self.pub_socket.flush() - else: - with self._pipe_out_lock: - string = self._flush_buffer() - tracker = self._pipe_out.send_multipart([ - self._pipe_uuid, - string.encode(self.encoding, 'replace'), - ], copy=False, track=True) - try: - tracker.wait(1) - except: - pass - - def isatty(self): - return False - - def __next__(self): - raise IOError('Read not supported on a write only stream.') - - if not py3compat.PY3: - next = __next__ - - def read(self, size=-1): - raise IOError('Read not supported on a write only stream.') - - def readline(self, size=-1): - raise IOError('Read not supported on a write only stream.') - - def fileno(self): - raise UnsupportedOperation("IOStream has no fileno.") - - def write(self, string): - if self.pub_socket is None: - raise ValueError('I/O operation on closed file') - else: - # Make sure that we're handling unicode - if not isinstance(string, unicode_type): - string = string.decode(self.encoding, 'replace') - - is_child = (self._check_mp_mode() == CHILD) - self._buffer.write(string) - if is_child: - # newlines imply flush in subprocesses - # mp.Pool cannot be trusted to flush promptly (or ever), - # and this helps. - if '\n' in string: - self.flush() - # do we want to check subprocess flushes on write? - # self._flush_from_subprocesses() - current_time = time.time() - if self._start < 0: - self._start = current_time - elif current_time - self._start > self.flush_interval: - self.flush() - - def writelines(self, sequence): - if self.pub_socket is None: - raise ValueError('I/O operation on closed file') - else: - for string in sequence: - self.write(string) - - def _flush_buffer(self): - """clear the current buffer and return the current buffer data""" - data = u'' - if self._buffer is not None: - data = self._buffer.getvalue() - self._buffer.close() - self._new_buffer() - return data - - def _new_buffer(self): - self._buffer = StringIO() - self._start = -1 diff --git a/ipython_kernel/ipkernel.py b/ipython_kernel/ipkernel.py deleted file mode 100644 index 1d31d11..0000000 --- a/ipython_kernel/ipkernel.py +++ /dev/null @@ -1,368 +0,0 @@ -"""The IPython kernel implementation""" - -import getpass -import sys -import traceback - -from IPython.core import release -from IPython.utils.py3compat import builtin_mod, PY3 -from IPython.utils.tokenutil import token_at_cursor, line_at_cursor -from IPython.utils.traitlets import Instance, Type, Any, List -from IPython.utils.decorators import undoc - -from .comm import CommManager -from .kernelbase import Kernel as KernelBase -from .serialize import serialize_object, unpack_apply_message -from .zmqshell import ZMQInteractiveShell - - -def lazy_import_handle_comm_opened(*args, **kwargs): - from IPython.html.widgets import Widget - Widget.handle_comm_opened(*args, **kwargs) - - -class IPythonKernel(KernelBase): - shell = Instance('IPython.core.interactiveshell.InteractiveShellABC', - allow_none=True) - 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): - super(IPythonKernel, self).__init__(**kwargs) - - # 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 - - self.comm_manager = CommManager(shell=self.shell, parent=self, - kernel=self) - self.comm_manager.register_target('ipython.widget', lazy_import_handle_comm_opened) - - self.shell.configurables.append(self.comm_manager) - comm_msg_types = [ 'comm_open', 'comm_msg', 'comm_close' ] - for msg_type in comm_msg_types: - self.shell_handlers[msg_type] = getattr(self.comm_manager, msg_type) - - help_links = List([ - { - 'text': "Python", - 'url': "http://docs.python.org/%i.%i" % sys.version_info[:2], - }, - { - 'text': "IPython", - 'url': "http://ipython.org/documentation.html", - }, - { - 'text': "NumPy", - 'url': "http://docs.scipy.org/doc/numpy/reference/", - }, - { - 'text': "SciPy", - 'url': "http://docs.scipy.org/doc/scipy/reference/", - }, - { - 'text': "Matplotlib", - 'url': "http://matplotlib.org/contents.html", - }, - { - 'text': "SymPy", - 'url': "http://docs.sympy.org/latest/index.html", - }, - { - 'text': "pandas", - 'url': "http://pandas.pydata.org/pandas-docs/stable/", - }, - ]) - - # Kernel info fields - implementation = 'ipython' - implementation_version = release.version - language_info = { - 'name': 'python', - 'version': sys.version.split()[0], - 'mimetype': 'text/x-python', - 'codemirror_mode': {'name': 'ipython', - 'version': sys.version_info[0]}, - 'pygments_lexer': 'ipython%d' % (3 if PY3 else 2), - 'nbconvert_exporter': 'python', - 'file_extension': '.py' - } - @property - def banner(self): - return self.shell.banner - - def start(self): - self.shell.exit_now = False - super(IPythonKernel, self).start() - - def set_parent(self, ident, parent): - """Overridden from parent to tell the display hook and output streams - about the parent message. - """ - super(IPythonKernel, self).set_parent(ident, parent) - 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): - # 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) - 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) - - def do_is_complete(self, code): - status, indent_spaces = self.shell.input_transformer_manager.check_complete(code) - r = {'status': status} - if status == 'incomplete': - r['indent'] = ' ' * indent_spaces - return r - - 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') - - -# 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.ipkernel.IPythonKernel', - DeprecationWarning) - super(Kernel, self).__init__(*args, **kwargs) diff --git a/ipython_kernel/kernelapp.py b/ipython_kernel/kernelapp.py deleted file mode 100644 index 0d5258e..0000000 --- a/ipython_kernel/kernelapp.py +++ /dev/null @@ -1,387 +0,0 @@ -"""An Application for launching a kernel""" - -# Copyright (c) IPython Development Team. -# Distributed under the terms of the Modified BSD License. - -from __future__ import print_function - -import atexit -import os -import sys -import signal - -import zmq -from zmq.eventloop import ioloop -from zmq.eventloop.zmqstream import ZMQStream - -from IPython.core.ultratb import FormattedTB -from IPython.core.application import ( - BaseIPythonApplication, base_flags, base_aliases, catch_config_error -) -from IPython.core.profiledir import ProfileDir -from IPython.core.shellapp import ( - InteractiveShellApp, shell_flags, shell_aliases -) -from IPython.utils import io -from IPython.utils.path import filefind -from IPython.utils.traitlets import ( - Any, Instance, Dict, Unicode, Integer, Bool, DottedObjectName, Type, -) -from IPython.utils.importstring import import_item -from jupyter_client import write_connection_file -from jupyter_client.connect import ConnectionFileMixin - -# local imports -from .heartbeat import Heartbeat -from .ipkernel import IPythonKernel -from .parentpoller import ParentPollerUnix, ParentPollerWindows -from .session import ( - Session, session_flags, session_aliases, -) -from .zmqshell import ZMQInteractiveShell - -#----------------------------------------------------------------------------- -# Flags and Aliases -#----------------------------------------------------------------------------- - -kernel_aliases = dict(base_aliases) -kernel_aliases.update({ - 'ip' : 'IPKernelApp.ip', - 'hb' : 'IPKernelApp.hb_port', - 'shell' : 'IPKernelApp.shell_port', - 'iopub' : 'IPKernelApp.iopub_port', - 'stdin' : 'IPKernelApp.stdin_port', - 'control' : 'IPKernelApp.control_port', - 'f' : 'IPKernelApp.connection_file', - 'transport': 'IPKernelApp.transport', -}) - -kernel_flags = dict(base_flags) -kernel_flags.update({ - 'no-stdout' : ( - {'IPKernelApp' : {'no_stdout' : True}}, - "redirect stdout to the null device"), - 'no-stderr' : ( - {'IPKernelApp' : {'no_stderr' : True}}, - "redirect stderr to the null device"), - 'pylab' : ( - {'IPKernelApp' : {'pylab' : 'auto'}}, - """Pre-load matplotlib and numpy for interactive use with - the default matplotlib backend."""), -}) - -# inherit flags&aliases for any IPython shell apps -kernel_aliases.update(shell_aliases) -kernel_flags.update(shell_flags) - -# inherit flags&aliases for Sessions -kernel_aliases.update(session_aliases) -kernel_flags.update(session_flags) - -_ctrl_c_message = """\ -NOTE: When using the `ipython kernel` entry point, Ctrl-C will not work. - -To exit, you will have to explicitly quit this process, by either sending -"quit" from a client, or using Ctrl-\\ in UNIX-like environments. - -To read more about this, see https://github.com/ipython/ipython/issues/2049 - -""" - -#----------------------------------------------------------------------------- -# Application class for starting an IPython Kernel -#----------------------------------------------------------------------------- - -class IPKernelApp(BaseIPythonApplication, InteractiveShellApp, - ConnectionFileMixin): - name='ipython-kernel' - aliases = Dict(kernel_aliases) - flags = Dict(kernel_flags) - classes = [IPythonKernel, ZMQInteractiveShell, ProfileDir, Session] - # the kernel class, as an importstring - kernel_class = Type('ipython_kernel.ipkernel.IPythonKernel', config=True, - klass='ipython_kernel.kernelbase.Kernel', - help="""The Kernel subclass to be used. - - This should allow easy re-use of the IPKernelApp entry point - to configure and launch kernels other than IPython's own. - """) - kernel = Any() - poller = Any() # don't restrict this even though current pollers are all Threads - heartbeat = Instance(Heartbeat, allow_none=True) - ports = Dict() - - # connection info: - - @property - def abs_connection_file(self): - if os.path.basename(self.connection_file) == self.connection_file: - return os.path.join(self.profile_dir.security_dir, self.connection_file) - else: - return self.connection_file - - - # streams, etc. - no_stdout = Bool(False, config=True, help="redirect stdout to the null device") - no_stderr = Bool(False, config=True, help="redirect stderr to the null device") - outstream_class = DottedObjectName('ipython_kernel.iostream.OutStream', - config=True, help="The importstring for the OutStream factory") - displayhook_class = DottedObjectName('ipython_kernel.displayhook.ZMQDisplayHook', - config=True, help="The importstring for the DisplayHook factory") - - # polling - parent_handle = Integer(int(os.environ.get('JPY_PARENT_PID') or 0), config=True, - help="""kill this process if its parent dies. On Windows, the argument - specifies the HANDLE of the parent process, otherwise it is simply boolean. - """) - interrupt = Integer(int(os.environ.get('JPY_INTERRUPT_EVENT') or 0), config=True, - help="""ONLY USED ON WINDOWS - Interrupt this process when the parent is signaled. - """) - - def init_crash_handler(self): - # Install minimal exception handling - sys.excepthook = FormattedTB(mode='Verbose', color_scheme='NoColor', - ostream=sys.__stdout__) - - def init_poller(self): - if sys.platform == 'win32': - if self.interrupt or self.parent_handle: - self.poller = ParentPollerWindows(self.interrupt, self.parent_handle) - elif self.parent_handle: - self.poller = ParentPollerUnix() - - def _bind_socket(self, s, port): - iface = '%s://%s' % (self.transport, self.ip) - if self.transport == 'tcp': - if port <= 0: - port = s.bind_to_random_port(iface) - else: - s.bind("tcp://%s:%i" % (self.ip, port)) - elif self.transport == 'ipc': - if port <= 0: - port = 1 - path = "%s-%i" % (self.ip, port) - while os.path.exists(path): - port = port + 1 - path = "%s-%i" % (self.ip, port) - else: - path = "%s-%i" % (self.ip, port) - s.bind("ipc://%s" % path) - return port - - def write_connection_file(self): - """write connection info to JSON file""" - cf = self.abs_connection_file - self.log.debug("Writing connection file: %s", cf) - write_connection_file(cf, ip=self.ip, key=self.session.key, transport=self.transport, - shell_port=self.shell_port, stdin_port=self.stdin_port, hb_port=self.hb_port, - iopub_port=self.iopub_port, control_port=self.control_port) - - def cleanup_connection_file(self): - cf = self.abs_connection_file - self.log.debug("Cleaning up connection file: %s", cf) - try: - os.remove(cf) - except (IOError, OSError): - pass - - self.cleanup_ipc_files() - - def init_connection_file(self): - if not self.connection_file: - self.connection_file = "kernel-%s.json"%os.getpid() - try: - self.connection_file = filefind(self.connection_file, ['.', self.profile_dir.security_dir]) - except IOError: - self.log.debug("Connection file not found: %s", self.connection_file) - # This means I own it, so I will clean it up: - atexit.register(self.cleanup_connection_file) - return - try: - self.load_connection_file() - except Exception: - self.log.error("Failed to load connection file: %r", self.connection_file, exc_info=True) - self.exit(1) - - def init_sockets(self): - # Create a context, a session, and the kernel sockets. - self.log.info("Starting the kernel at pid: %i", os.getpid()) - context = zmq.Context.instance() - # Uncomment this to try closing the context. - # atexit.register(context.term) - - self.shell_socket = context.socket(zmq.ROUTER) - self.shell_socket.linger = 1000 - self.shell_port = self._bind_socket(self.shell_socket, self.shell_port) - self.log.debug("shell ROUTER Channel on port: %i" % self.shell_port) - - self.iopub_socket = context.socket(zmq.PUB) - self.iopub_socket.linger = 1000 - self.iopub_port = self._bind_socket(self.iopub_socket, self.iopub_port) - self.log.debug("iopub PUB Channel on port: %i" % self.iopub_port) - - self.stdin_socket = context.socket(zmq.ROUTER) - self.stdin_socket.linger = 1000 - self.stdin_port = self._bind_socket(self.stdin_socket, self.stdin_port) - self.log.debug("stdin ROUTER Channel on port: %i" % self.stdin_port) - - self.control_socket = context.socket(zmq.ROUTER) - self.control_socket.linger = 1000 - self.control_port = self._bind_socket(self.control_socket, self.control_port) - self.log.debug("control ROUTER Channel on port: %i" % self.control_port) - - def init_heartbeat(self): - """start the heart beating""" - # heartbeat doesn't share context, because it mustn't be blocked - # by the GIL, which is accessed by libzmq when freeing zero-copy messages - hb_ctx = zmq.Context() - self.heartbeat = Heartbeat(hb_ctx, (self.transport, self.ip, self.hb_port)) - self.hb_port = self.heartbeat.port - self.log.debug("Heartbeat REP Channel on port: %i" % self.hb_port) - self.heartbeat.start() - - def log_connection_info(self): - """display connection info, and store ports""" - basename = os.path.basename(self.connection_file) - if basename == self.connection_file or \ - os.path.dirname(self.connection_file) == self.profile_dir.security_dir: - # use shortname - tail = basename - if self.profile != 'default': - tail += " --profile %s" % self.profile - else: - tail = self.connection_file - lines = [ - "To connect another client to this kernel, use:", - " --existing %s" % tail, - ] - # log connection info - # info-level, so often not shown. - # frontends should use the %connect_info magic - # to see the connection info - for line in lines: - self.log.info(line) - # also raw print to the terminal if no parent_handle (`ipython kernel`) - if not self.parent_handle: - io.rprint(_ctrl_c_message) - for line in lines: - io.rprint(line) - - self.ports = dict(shell=self.shell_port, iopub=self.iopub_port, - stdin=self.stdin_port, hb=self.hb_port, - control=self.control_port) - - def init_blackhole(self): - """redirects stdout/stderr to devnull if necessary""" - if self.no_stdout or self.no_stderr: - blackhole = open(os.devnull, 'w') - if self.no_stdout: - sys.stdout = sys.__stdout__ = blackhole - if self.no_stderr: - sys.stderr = sys.__stderr__ = blackhole - - def init_io(self): - """Redirect input streams and set a display hook.""" - if self.outstream_class: - outstream_factory = import_item(str(self.outstream_class)) - sys.stdout = outstream_factory(self.session, self.iopub_socket, u'stdout') - sys.stderr = outstream_factory(self.session, self.iopub_socket, u'stderr') - if self.displayhook_class: - displayhook_factory = import_item(str(self.displayhook_class)) - sys.displayhook = displayhook_factory(self.session, self.iopub_socket) - - def init_signal(self): - signal.signal(signal.SIGINT, signal.SIG_IGN) - - def init_kernel(self): - """Create the Kernel object itself""" - shell_stream = ZMQStream(self.shell_socket) - control_stream = ZMQStream(self.control_socket) - - kernel_factory = self.kernel_class.instance - - kernel = kernel_factory(parent=self, session=self.session, - shell_streams=[shell_stream, control_stream], - iopub_socket=self.iopub_socket, - stdin_socket=self.stdin_socket, - log=self.log, - profile_dir=self.profile_dir, - user_ns=self.user_ns, - ) - kernel.record_ports(self.ports) - self.kernel = kernel - - def init_gui_pylab(self): - """Enable GUI event loop integration, taking pylab into account.""" - - # Provide a wrapper for :meth:`InteractiveShellApp.init_gui_pylab` - # to ensure that any exception is printed straight to stderr. - # Normally _showtraceback associates the reply with an execution, - # which means frontends will never draw it, as this exception - # is not associated with any execute request. - - shell = self.shell - _showtraceback = shell._showtraceback - try: - # replace error-sending traceback with stderr - def print_tb(etype, evalue, stb): - print ("GUI event loop or pylab initialization failed", - file=io.stderr) - print (shell.InteractiveTB.stb2text(stb), file=io.stderr) - shell._showtraceback = print_tb - InteractiveShellApp.init_gui_pylab(self) - finally: - shell._showtraceback = _showtraceback - - def init_shell(self): - self.shell = getattr(self.kernel, 'shell', None) - if self.shell: - self.shell.configurables.append(self) - - @catch_config_error - def initialize(self, argv=None): - super(IPKernelApp, self).initialize(argv) - self.init_blackhole() - self.init_connection_file() - self.init_poller() - self.init_sockets() - self.init_heartbeat() - # writing/displaying connection info must be *after* init_sockets/heartbeat - self.log_connection_info() - self.write_connection_file() - self.init_io() - self.init_signal() - self.init_kernel() - # shell init steps - self.init_path() - self.init_shell() - if self.shell: - self.init_gui_pylab() - self.init_extensions() - self.init_code() - # flush stdout/stderr, so that anything written to these streams during - # initialization do not get associated with the first execution request - sys.stdout.flush() - sys.stderr.flush() - - def start(self): - if self.poller is not None: - self.poller.start() - self.kernel.start() - try: - ioloop.IOLoop.instance().start() - except KeyboardInterrupt: - pass - -launch_new_instance = IPKernelApp.launch_instance - -def main(): - """Run an IPKernel as an application""" - app = IPKernelApp.instance() - app.initialize() - app.start() - - -if __name__ == '__main__': - main() diff --git a/ipython_kernel/kernelbase.py b/ipython_kernel/kernelbase.py deleted file mode 100644 index ea296cc..0000000 --- a/ipython_kernel/kernelbase.py +++ /dev/null @@ -1,701 +0,0 @@ -"""Base class for a kernel that talks to frontends over 0MQ.""" - -# Copyright (c) IPython Development Team. -# Distributed under the terms of the Modified BSD License. - -from __future__ import print_function - -import sys -import time -import logging -import uuid - -from datetime import datetime -from signal import ( - signal, default_int_handler, SIGINT -) - -import zmq -from zmq.eventloop import ioloop -from zmq.eventloop.zmqstream import ZMQStream - -from IPython.config.configurable import SingletonConfigurable -from IPython.core.error import StdinNotImplementedError -from IPython.core import release -from IPython.utils import py3compat -from IPython.utils.py3compat import unicode_type, string_types -from jupyter_client.jsonutil import json_clean -from IPython.utils.traitlets import ( - Any, Instance, Float, Dict, List, Set, Integer, Unicode, Bool, -) - -from .session import Session - - -class Kernel(SingletonConfigurable): - - #--------------------------------------------------------------------------- - # Kernel interface - #--------------------------------------------------------------------------- - - # attribute to override with a GUI - eventloop = Any(None) - def _eventloop_changed(self, name, old, new): - """schedule call to eventloop from IOLoop""" - loop = ioloop.IOLoop.instance() - loop.add_callback(self.enter_eventloop) - - session = Instance(Session, allow_none=True) - profile_dir = Instance('IPython.core.profiledir.ProfileDir', allow_none=True) - shell_streams = List() - control_stream = Instance(ZMQStream, allow_none=True) - iopub_socket = Instance(zmq.Socket, allow_none=True) - stdin_socket = Instance(zmq.Socket, allow_none=True) - log = Instance(logging.Logger, allow_none=True) - - # identities: - int_id = Integer(-1) - ident = Unicode() - - def _ident_default(self): - return unicode_type(uuid.uuid4()) - - # This should be overridden by wrapper kernels that implement any real - # language. - language_info = {} - - # any links that should go in the help menu - help_links = List() - - # Private interface - - _darwin_app_nap = Bool(True, config=True, - help="""Whether to use appnope for compatiblity with OS X App Nap. - - Only affects OS X >= 10.9. - """ - ) - - # track associations with current request - _allow_stdin = Bool(False) - _parent_header = Dict() - _parent_ident = Any(b'') - # Time to sleep after flushing the stdout/err buffers in each execute - # cycle. While this introduces a hard limit on the minimal latency of the - # execute cycle, it helps prevent output synchronization problems for - # clients. - # Units are in seconds. The minimum zmq latency on local host is probably - # ~150 microseconds, set this to 500us for now. We may need to increase it - # a little if it's not enough after more interactive testing. - _execute_sleep = Float(0.0005, config=True) - - # Frequency of the kernel's event loop. - # Units are in seconds, kernel subclasses for GUI toolkits may need to - # adapt to milliseconds. - _poll_interval = Float(0.05, config=True) - - # If the shutdown was requested over the network, we leave here the - # necessary reply message so it can be sent by our registered atexit - # handler. This ensures that the reply is only sent to clients truly at - # the end of our shutdown process (which happens after the underlying - # IPython shell's own shutdown). - _shutdown_message = None - - # This is a dict of port number that the kernel is listening on. It is set - # by record_ports and used by connect_request. - _recorded_ports = Dict() - - # set of aborted msg_ids - aborted = Set() - - # Track execution count here. For IPython, we override this to use the - # execution count we store in the shell. - execution_count = 0 - - - def __init__(self, **kwargs): - super(Kernel, self).__init__(**kwargs) - - # Build dict of handlers for message types - msg_types = [ 'execute_request', 'complete_request', - 'inspect_request', 'history_request', - 'kernel_info_request', - 'connect_request', 'shutdown_request', - 'apply_request', 'is_complete_request', - ] - self.shell_handlers = {} - for msg_type in msg_types: - self.shell_handlers[msg_type] = getattr(self, msg_type) - - control_msg_types = msg_types + [ 'clear_request', 'abort_request' ] - self.control_handlers = {} - for msg_type in control_msg_types: - self.control_handlers[msg_type] = getattr(self, msg_type) - - - def dispatch_control(self, msg): - """dispatch control requests""" - idents,msg = self.session.feed_identities(msg, copy=False) - try: - msg = self.session.deserialize(msg, content=True, copy=False) - except: - self.log.error("Invalid Control Message", exc_info=True) - return - - self.log.debug("Control received: %s", msg) - - # Set the parent message for side effects. - self.set_parent(idents, msg) - self._publish_status(u'busy') - - header = msg['header'] - msg_type = header['msg_type'] - - handler = self.control_handlers.get(msg_type, None) - if handler is None: - self.log.error("UNKNOWN CONTROL MESSAGE TYPE: %r", msg_type) - else: - try: - handler(self.control_stream, idents, msg) - except Exception: - self.log.error("Exception in control handler:", exc_info=True) - - sys.stdout.flush() - sys.stderr.flush() - self._publish_status(u'idle') - - def dispatch_shell(self, stream, msg): - """dispatch shell requests""" - # flush control requests first - if self.control_stream: - self.control_stream.flush() - - idents,msg = self.session.feed_identities(msg, copy=False) - try: - msg = self.session.deserialize(msg, content=True, copy=False) - except: - self.log.error("Invalid Message", exc_info=True) - return - - # Set the parent message for side effects. - self.set_parent(idents, msg) - self._publish_status(u'busy') - - header = msg['header'] - msg_id = header['msg_id'] - msg_type = msg['header']['msg_type'] - - # Print some info about this message and leave a '--->' marker, so it's - # easier to trace visually the message chain when debugging. Each - # handler prints its message at the end. - self.log.debug('\n*** MESSAGE TYPE:%s***', msg_type) - self.log.debug(' Content: %s\n --->\n ', msg['content']) - - if msg_id in self.aborted: - self.aborted.remove(msg_id) - # is it safe to assume a msg_id will not be resubmitted? - reply_type = msg_type.split('_')[0] + '_reply' - status = {'status' : 'aborted'} - md = {'engine' : self.ident} - md.update(status) - self.session.send(stream, reply_type, metadata=md, - content=status, parent=msg, ident=idents) - return - - handler = self.shell_handlers.get(msg_type, None) - if handler is None: - self.log.error("UNKNOWN MESSAGE TYPE: %r", msg_type) - else: - # ensure default_int_handler during handler call - sig = signal(SIGINT, default_int_handler) - self.log.debug("%s: %s", msg_type, msg) - try: - handler(stream, idents, msg) - except Exception: - self.log.error("Exception in message handler:", exc_info=True) - finally: - signal(SIGINT, sig) - - sys.stdout.flush() - sys.stderr.flush() - self._publish_status(u'idle') - - def enter_eventloop(self): - """enter eventloop""" - self.log.info("entering eventloop %s", self.eventloop) - for stream in self.shell_streams: - # flush any pending replies, - # which may be skipped by entering the eventloop - stream.flush(zmq.POLLOUT) - # restore default_int_handler - signal(SIGINT, default_int_handler) - while self.eventloop is not None: - try: - self.eventloop(self) - except KeyboardInterrupt: - # Ctrl-C shouldn't crash the kernel - self.log.error("KeyboardInterrupt caught in kernel") - continue - else: - # eventloop exited cleanly, this means we should stop (right?) - self.eventloop = None - break - self.log.info("exiting eventloop") - - def start(self): - """register dispatchers for streams""" - if self.control_stream: - self.control_stream.on_recv(self.dispatch_control, copy=False) - - def make_dispatcher(stream): - def dispatcher(msg): - return self.dispatch_shell(stream, msg) - return dispatcher - - for s in self.shell_streams: - s.on_recv(make_dispatcher(s), copy=False) - - # publish idle status - self._publish_status('starting') - - def do_one_iteration(self): - """step eventloop just once""" - if self.control_stream: - self.control_stream.flush() - for stream in self.shell_streams: - # handle at most one request per iteration - stream.flush(zmq.POLLIN, 1) - stream.flush(zmq.POLLOUT) - - - def record_ports(self, ports): - """Record the ports that this kernel is using. - - The creator of the Kernel instance must call this methods if they - want the :meth:`connect_request` method to return the port numbers. - """ - self._recorded_ports = ports - - #--------------------------------------------------------------------------- - # Kernel request handlers - #--------------------------------------------------------------------------- - - def _make_metadata(self, other=None): - """init metadata dict, for execute/apply_reply""" - new_md = { - 'dependencies_met' : True, - 'engine' : self.ident, - 'started': datetime.now(), - } - if other: - new_md.update(other) - return new_md - - def _publish_execute_input(self, code, parent, execution_count): - """Publish the code request on the iopub stream.""" - - self.session.send(self.iopub_socket, u'execute_input', - {u'code':code, u'execution_count': execution_count}, - parent=parent, ident=self._topic('execute_input') - ) - - def _publish_status(self, status, parent=None): - """send status (busy/idle) on IOPub""" - self.session.send(self.iopub_socket, - u'status', - {u'execution_state': status}, - parent=parent or self._parent_header, - ident=self._topic('status'), - ) - - def set_parent(self, ident, parent): - """Set the current parent_header - - Side effects (IOPub messages) and replies are associated with - the request that caused them via the parent_header. - - The parent identity is used to route input_request messages - on the stdin channel. - """ - self._parent_ident = ident - self._parent_header = parent - - def send_response(self, stream, msg_or_type, content=None, ident=None, - buffers=None, track=False, header=None, metadata=None): - """Send a response to the message we're currently processing. - - This accepts all the parameters of :meth:`ipython_kernel.session.Session.send` - except ``parent``. - - This relies on :meth:`set_parent` having been called for the current - message. - """ - return self.session.send(stream, msg_or_type, content, self._parent_header, - ident, buffers, track, header, metadata) - - def execute_request(self, stream, ident, parent): - """handle an execute_request""" - - try: - content = parent[u'content'] - code = py3compat.cast_unicode_py2(content[u'code']) - silent = content[u'silent'] - store_history = content.get(u'store_history', not silent) - user_expressions = content.get('user_expressions', {}) - allow_stdin = content.get('allow_stdin', False) - except: - self.log.error("Got bad msg: ") - self.log.error("%s", parent) - return - - stop_on_error = content.get('stop_on_error', True) - - md = self._make_metadata(parent['metadata']) - - # Re-broadcast our input for the benefit of listening clients, and - # start computing output - if not silent: - self.execution_count += 1 - self._publish_execute_input(code, parent, self.execution_count) - - reply_content = self.do_execute(code, silent, store_history, - user_expressions, allow_stdin) - - # Flush output before sending the reply. - sys.stdout.flush() - sys.stderr.flush() - # FIXME: on rare occasions, the flush doesn't seem to make it to the - # clients... This seems to mitigate the problem, but we definitely need - # to better understand what's going on. - if self._execute_sleep: - time.sleep(self._execute_sleep) - - # Send the reply. - reply_content = json_clean(reply_content) - - md['status'] = reply_content['status'] - if reply_content['status'] == 'error' and \ - reply_content['ename'] == 'UnmetDependency': - md['dependencies_met'] = False - - reply_msg = self.session.send(stream, u'execute_reply', - reply_content, parent, metadata=md, - ident=ident) - - self.log.debug("%s", reply_msg) - - if not silent and reply_msg['content']['status'] == u'error' and stop_on_error: - self._abort_queues() - - def do_execute(self, code, silent, store_history=True, - user_expressions=None, allow_stdin=False): - """Execute user code. Must be overridden by subclasses. - """ - raise NotImplementedError - - def complete_request(self, stream, ident, parent): - content = parent['content'] - code = content['code'] - cursor_pos = content['cursor_pos'] - - matches = self.do_complete(code, cursor_pos) - matches = json_clean(matches) - completion_msg = self.session.send(stream, 'complete_reply', - matches, parent, ident) - self.log.debug("%s", completion_msg) - - def do_complete(self, code, cursor_pos): - """Override in subclasses to find completions. - """ - return {'matches' : [], - 'cursor_end' : cursor_pos, - 'cursor_start' : cursor_pos, - 'metadata' : {}, - 'status' : 'ok'} - - def inspect_request(self, stream, ident, parent): - content = parent['content'] - - reply_content = self.do_inspect(content['code'], content['cursor_pos'], - content.get('detail_level', 0)) - # Before we send this object over, we scrub it for JSON usage - reply_content = json_clean(reply_content) - msg = self.session.send(stream, 'inspect_reply', - reply_content, parent, ident) - self.log.debug("%s", msg) - - def do_inspect(self, code, cursor_pos, detail_level=0): - """Override in subclasses to allow introspection. - """ - return {'status': 'ok', 'data':{}, 'metadata':{}, 'found':False} - - def history_request(self, stream, ident, parent): - content = parent['content'] - - reply_content = self.do_history(**content) - - reply_content = json_clean(reply_content) - msg = self.session.send(stream, 'history_reply', - reply_content, parent, ident) - self.log.debug("%s", msg) - - def do_history(self, hist_access_type, output, raw, session=None, start=None, - stop=None, n=None, pattern=None, unique=False): - """Override in subclasses to access history. - """ - return {'history': []} - - def connect_request(self, stream, ident, parent): - if self._recorded_ports is not None: - content = self._recorded_ports.copy() - else: - content = {} - msg = self.session.send(stream, 'connect_reply', - content, parent, ident) - self.log.debug("%s", msg) - - @property - def kernel_info(self): - return { - 'protocol_version': release.kernel_protocol_version, - 'implementation': self.implementation, - 'implementation_version': self.implementation_version, - 'language_info': self.language_info, - 'banner': self.banner, - 'help_links': self.help_links, - } - - def kernel_info_request(self, stream, ident, parent): - msg = self.session.send(stream, 'kernel_info_reply', - self.kernel_info, parent, ident) - self.log.debug("%s", msg) - - def shutdown_request(self, stream, ident, parent): - content = self.do_shutdown(parent['content']['restart']) - self.session.send(stream, u'shutdown_reply', content, parent, ident=ident) - # same content, but different msg_id for broadcasting on IOPub - self._shutdown_message = self.session.msg(u'shutdown_reply', - content, parent - ) - - self._at_shutdown() - # call sys.exit after a short delay - loop = ioloop.IOLoop.instance() - loop.add_timeout(time.time()+0.1, loop.stop) - - def do_shutdown(self, restart): - """Override in subclasses to do things when the frontend shuts down the - kernel. - """ - return {'status': 'ok', 'restart': restart} - - def is_complete_request(self, stream, ident, parent): - content = parent['content'] - code = content['code'] - - reply_content = self.do_is_complete(code) - reply_content = json_clean(reply_content) - reply_msg = self.session.send(stream, 'is_complete_reply', - reply_content, parent, ident) - self.log.debug("%s", reply_msg) - - def do_is_complete(self, code): - """Override in subclasses to find completions. - """ - return {'status' : 'unknown', - } - - #--------------------------------------------------------------------------- - # Engine methods - #--------------------------------------------------------------------------- - - def apply_request(self, stream, ident, parent): - try: - content = parent[u'content'] - bufs = parent[u'buffers'] - msg_id = parent['header']['msg_id'] - except: - self.log.error("Got bad msg: %s", parent, exc_info=True) - return - - md = self._make_metadata(parent['metadata']) - - reply_content, result_buf = self.do_apply(content, bufs, msg_id, md) - - # put 'ok'/'error' status in header, for scheduler introspection: - md['status'] = reply_content['status'] - - # flush i/o - sys.stdout.flush() - sys.stderr.flush() - - self.session.send(stream, u'apply_reply', reply_content, - parent=parent, ident=ident,buffers=result_buf, metadata=md) - - def do_apply(self, content, bufs, msg_id, reply_metadata): - """Override in subclasses to support the IPython parallel framework. - """ - raise NotImplementedError - - #--------------------------------------------------------------------------- - # Control messages - #--------------------------------------------------------------------------- - - def abort_request(self, stream, ident, parent): - """abort a specific msg by id""" - msg_ids = parent['content'].get('msg_ids', None) - if isinstance(msg_ids, string_types): - msg_ids = [msg_ids] - if not msg_ids: - self._abort_queues() - for mid in msg_ids: - self.aborted.add(str(mid)) - - content = dict(status='ok') - reply_msg = self.session.send(stream, 'abort_reply', content=content, - parent=parent, ident=ident) - self.log.debug("%s", reply_msg) - - def clear_request(self, stream, idents, parent): - """Clear our namespace.""" - content = self.do_clear() - self.session.send(stream, 'clear_reply', ident=idents, parent=parent, - content = content) - - def do_clear(self): - """Override in subclasses to clear the namespace - - This is only required for IPython.parallel. - """ - raise NotImplementedError - - #--------------------------------------------------------------------------- - # Protected interface - #--------------------------------------------------------------------------- - - def _topic(self, topic): - """prefixed topic for IOPub messages""" - if self.int_id >= 0: - base = "engine.%i" % self.int_id - else: - base = "kernel.%s" % self.ident - - return py3compat.cast_bytes("%s.%s" % (base, topic)) - - def _abort_queues(self): - for stream in self.shell_streams: - if stream: - self._abort_queue(stream) - - def _abort_queue(self, stream): - poller = zmq.Poller() - poller.register(stream.socket, zmq.POLLIN) - while True: - idents,msg = self.session.recv(stream, zmq.NOBLOCK, content=True) - if msg is None: - return - - self.log.info("Aborting:") - self.log.info("%s", msg) - msg_type = msg['header']['msg_type'] - reply_type = msg_type.split('_')[0] + '_reply' - - status = {'status' : 'aborted'} - md = {'engine' : self.ident} - md.update(status) - reply_msg = self.session.send(stream, reply_type, metadata=md, - content=status, parent=msg, ident=idents) - self.log.debug("%s", reply_msg) - # We need to wait a bit for requests to come in. This can probably - # be set shorter for true asynchronous clients. - poller.poll(50) - - - def _no_raw_input(self): - """Raise StdinNotImplentedError if active frontend doesn't support - stdin.""" - raise StdinNotImplementedError("raw_input was called, but this " - "frontend does not support stdin.") - - def getpass(self, prompt=''): - """Forward getpass to frontends - - Raises - ------ - StdinNotImplentedError if active frontend doesn't support stdin. - """ - if not self._allow_stdin: - raise StdinNotImplementedError( - "getpass was called, but this frontend does not support input requests." - ) - return self._input_request(prompt, - self._parent_ident, - self._parent_header, - password=True, - ) - - def raw_input(self, prompt=''): - """Forward raw_input to frontends - - Raises - ------ - StdinNotImplentedError if active frontend doesn't support stdin. - """ - if not self._allow_stdin: - raise StdinNotImplementedError( - "raw_input was called, but this frontend does not support input requests." - ) - return self._input_request(prompt, - self._parent_ident, - self._parent_header, - password=False, - ) - - def _input_request(self, prompt, ident, parent, password=False): - # Flush output before making the request. - sys.stderr.flush() - sys.stdout.flush() - # flush the stdin socket, to purge stale replies - while True: - try: - self.stdin_socket.recv_multipart(zmq.NOBLOCK) - except zmq.ZMQError as e: - if e.errno == zmq.EAGAIN: - break - else: - raise - - # Send the input request. - content = json_clean(dict(prompt=prompt, password=password)) - self.session.send(self.stdin_socket, u'input_request', content, parent, - ident=ident) - - # Await a response. - while True: - try: - ident, reply = self.session.recv(self.stdin_socket, 0) - except Exception: - self.log.warn("Invalid Message:", exc_info=True) - except KeyboardInterrupt: - # re-raise KeyboardInterrupt, to truncate traceback - raise KeyboardInterrupt - else: - break - try: - value = py3compat.unicode_to_str(reply['content']['value']) - except: - self.log.error("Bad input_reply: %s", parent) - value = '' - if value == '\x04': - # EOF - raise EOFError - return value - - def _at_shutdown(self): - """Actions taken at shutdown by the kernel, called by python's atexit. - """ - # io.rprint("Kernel at_shutdown") # dbg - if self._shutdown_message is not None: - self.session.send(self.iopub_socket, self._shutdown_message, ident=self._topic('shutdown')) - self.log.debug("%s", self._shutdown_message) - [ s.flush(zmq.POLLOUT) for s in self.shell_streams ] diff --git a/ipython_kernel/kernelspec.py b/ipython_kernel/kernelspec.py deleted file mode 100644 index 54ec846..0000000 --- a/ipython_kernel/kernelspec.py +++ /dev/null @@ -1,95 +0,0 @@ -"""The IPython kernel spec for Jupyter""" - -# Copyright (c) IPython Development Team. -# Distributed under the terms of the Modified BSD License. - -import json -import os -import shutil -import sys -import tempfile - -from jupyter_client.kernelspec import KernelSpecManager - -pjoin = os.path.join - -KERNEL_NAME = 'python%i' % sys.version_info[0] - -# path to kernelspec resources -RESOURCES = pjoin(os.path.dirname(__file__), 'resources') - - -def make_ipkernel_cmd(mod='ipython_kernel', executable=None, extra_arguments=[], **kw): - """Build Popen command list for launching an IPython kernel. - - Parameters - ---------- - mod : str, optional (default 'ipython_kernel') - A string of an IPython module whose __main__ starts an IPython kernel - - executable : str, optional (default sys.executable) - The Python executable to use for the kernel process. - - extra_arguments : list, optional - A list of extra arguments to pass when executing the launch code. - - Returns - ------- - - A Popen command list - """ - if executable is None: - executable = sys.executable - arguments = [ executable, '-m', mod, '-f', '{connection_file}' ] - arguments.extend(extra_arguments) - - return arguments - - -def get_kernel_dict(): - """Construct dict for kernel.json""" - return { - 'argv': make_ipkernel_cmd(), - 'display_name': 'Python %i' % sys.version_info[0], - 'language': 'python', - } - - -def write_kernel_spec(path=None): - """Write a kernel spec directory to `path` - - If `path` is not specified, a temporary directory is created. - - The path to the kernelspec is always returned. - """ - if path is None: - path = os.path.join(tempfile.mkdtemp(suffix='_kernels'), KERNEL_NAME) - - # stage resources - shutil.copytree(RESOURCES, path) - # write kernel.json - with open(pjoin(path, 'kernel.json'), 'w') as f: - json.dump(get_kernel_dict(), f, indent=1) - - return path - - -def install(kernel_spec_manager=None, user=False): - """Install the IPython kernelspec for Jupyter - - Parameters - ---------- - - kernel_spec_manager: KernelSpecManager [optional] - A KernelSpecManager to use for installation. - If none provided, a default instance will be created. - user: bool [default: False] - Whether to do a user-only install, or system-wide. - """ - if kernel_spec_manager is None: - kernel_spec_manager = KernelSpecManager() - path = write_kernel_spec() - kernel_spec_manager.install_kernel_spec(path, - kernel_name=KERNEL_NAME, user=user, replace=True) - # cleanup afterward - shutil.rmtree(path) diff --git a/ipython_kernel/log.py b/ipython_kernel/log.py deleted file mode 100644 index b84dde2..0000000 --- a/ipython_kernel/log.py +++ /dev/null @@ -1,20 +0,0 @@ -from logging import INFO, DEBUG, WARN, ERROR, FATAL - -from zmq.log.handlers import PUBHandler - -class EnginePUBHandler(PUBHandler): - """A simple PUBHandler subclass that sets root_topic""" - engine=None - - def __init__(self, engine, *args, **kwargs): - PUBHandler.__init__(self,*args, **kwargs) - self.engine = engine - - @property - def root_topic(self): - """this is a property, in case the handler is created - before the engine gets registered with an id""" - if isinstance(getattr(self.engine, 'id', None), int): - return "engine.%i"%self.engine.id - else: - return "engine" diff --git a/ipython_kernel/parentpoller.py b/ipython_kernel/parentpoller.py deleted file mode 100644 index 227614d..0000000 --- a/ipython_kernel/parentpoller.py +++ /dev/null @@ -1,114 +0,0 @@ -# Copyright (c) IPython Development Team. -# Distributed under the terms of the Modified BSD License. - -try: - import ctypes -except: - ctypes = None -import os -import platform -import signal -import time -try: - from _thread import interrupt_main # Py 3 -except ImportError: - from thread import interrupt_main # Py 2 -from threading import Thread - -from IPython.utils.warn import warn - - -class ParentPollerUnix(Thread): - """ A Unix-specific daemon thread that terminates the program immediately - when the parent process no longer exists. - """ - - def __init__(self): - super(ParentPollerUnix, self).__init__() - self.daemon = True - - def run(self): - # We cannot use os.waitpid because it works only for child processes. - from errno import EINTR - while True: - try: - if os.getppid() == 1: - os._exit(1) - time.sleep(1.0) - except OSError as e: - if e.errno == EINTR: - continue - raise - - -class ParentPollerWindows(Thread): - """ A Windows-specific daemon thread that listens for a special event that - signals an interrupt and, optionally, terminates the program immediately - when the parent process no longer exists. - """ - - def __init__(self, interrupt_handle=None, parent_handle=None): - """ Create the poller. At least one of the optional parameters must be - provided. - - Parameters - ---------- - interrupt_handle : HANDLE (int), optional - If provided, the program will generate a Ctrl+C event when this - handle is signaled. - - parent_handle : HANDLE (int), optional - If provided, the program will terminate immediately when this - handle is signaled. - """ - assert(interrupt_handle or parent_handle) - super(ParentPollerWindows, self).__init__() - if ctypes is None: - raise ImportError("ParentPollerWindows requires ctypes") - self.daemon = True - self.interrupt_handle = interrupt_handle - self.parent_handle = parent_handle - - def run(self): - """ Run the poll loop. This method never returns. - """ - try: - from _winapi import WAIT_OBJECT_0, INFINITE - except ImportError: - from _subprocess import WAIT_OBJECT_0, INFINITE - - # Build the list of handle to listen on. - handles = [] - if self.interrupt_handle: - handles.append(self.interrupt_handle) - if self.parent_handle: - handles.append(self.parent_handle) - arch = platform.architecture()[0] - c_int = ctypes.c_int64 if arch.startswith('64') else ctypes.c_int - - # Listen forever. - while True: - result = ctypes.windll.kernel32.WaitForMultipleObjects( - len(handles), # nCount - (c_int * len(handles))(*handles), # lpHandles - False, # bWaitAll - INFINITE) # dwMilliseconds - - if WAIT_OBJECT_0 <= result < len(handles): - handle = handles[result - WAIT_OBJECT_0] - - if handle == self.interrupt_handle: - # check if signal handler is callable - # to avoid 'int not callable' error (Python issue #23395) - if callable(signal.getsignal(signal.SIGINT)): - interrupt_main() - - elif handle == self.parent_handle: - os._exit(1) - elif result < 0: - # wait failed, just give up and stop polling. - warn("""Parent poll failed. If the frontend dies, - the kernel may be left running. Please let us know - about your system (bitness, Python, etc.) at - ipython-dev@scipy.org""") - return diff --git a/ipython_kernel/pickleutil.py b/ipython_kernel/pickleutil.py deleted file mode 100644 index 4004e75..0000000 --- a/ipython_kernel/pickleutil.py +++ /dev/null @@ -1,426 +0,0 @@ -# encoding: utf-8 -"""Pickle related utilities. Perhaps this should be called 'can'.""" - -# Copyright (c) IPython Development Team. -# Distributed under the terms of the Modified BSD License. - -import copy -import logging -import sys -from types import FunctionType - -try: - import cPickle as pickle -except ImportError: - import pickle - -from IPython.utils import py3compat -from IPython.utils.importstring import import_item -from IPython.utils.py3compat import string_types, iteritems, buffer_to_bytes_py2 - -from . import codeutil # This registers a hook when it's imported - -from IPython.config import Application -from IPython.utils.log import get_logger - -if py3compat.PY3: - buffer = memoryview - class_type = type -else: - from types import ClassType - class_type = (type, ClassType) - -try: - PICKLE_PROTOCOL = pickle.DEFAULT_PROTOCOL -except AttributeError: - PICKLE_PROTOCOL = pickle.HIGHEST_PROTOCOL - -def _get_cell_type(a=None): - """the type of a closure cell doesn't seem to be importable, - so just create one - """ - def inner(): - return a - return type(py3compat.get_closure(inner)[0]) - -cell_type = _get_cell_type() - -#------------------------------------------------------------------------------- -# Functions -#------------------------------------------------------------------------------- - - -def use_dill(): - """use dill to expand serialization support - - adds support for object methods and closures to serialization. - """ - # import dill causes most of the magic - import dill - - # dill doesn't work with cPickle, - # tell the two relevant modules to use plain pickle - - global pickle - pickle = dill - - try: - from IPython.kernel.zmq import serialize - except ImportError: - pass - else: - serialize.pickle = dill - - # disable special function handling, let dill take care of it - can_map.pop(FunctionType, None) - -def use_cloudpickle(): - """use cloudpickle to expand serialization support - - adds support for object methods and closures to serialization. - """ - from cloud.serialization import cloudpickle - - global pickle - pickle = cloudpickle - - try: - from IPython.kernel.zmq import serialize - except ImportError: - pass - else: - serialize.pickle = cloudpickle - - # disable special function handling, let cloudpickle take care of it - can_map.pop(FunctionType, None) - - -#------------------------------------------------------------------------------- -# Classes -#------------------------------------------------------------------------------- - - -class CannedObject(object): - def __init__(self, obj, keys=[], hook=None): - """can an object for safe pickling - - Parameters - ========== - - obj: - The object to be canned - keys: list (optional) - list of attribute names that will be explicitly canned / uncanned - hook: callable (optional) - An optional extra callable, - which can do additional processing of the uncanned object. - - large data may be offloaded into the buffers list, - used for zero-copy transfers. - """ - self.keys = keys - self.obj = copy.copy(obj) - self.hook = can(hook) - for key in keys: - setattr(self.obj, key, can(getattr(obj, key))) - - self.buffers = [] - - def get_object(self, g=None): - if g is None: - g = {} - obj = self.obj - for key in self.keys: - setattr(obj, key, uncan(getattr(obj, key), g)) - - if self.hook: - self.hook = uncan(self.hook, g) - self.hook(obj, g) - return self.obj - - -class Reference(CannedObject): - """object for wrapping a remote reference by name.""" - def __init__(self, name): - if not isinstance(name, string_types): - raise TypeError("illegal name: %r"%name) - self.name = name - self.buffers = [] - - def __repr__(self): - return ""%self.name - - def get_object(self, g=None): - if g is None: - g = {} - - return eval(self.name, g) - - -class CannedCell(CannedObject): - """Can a closure cell""" - def __init__(self, cell): - self.cell_contents = can(cell.cell_contents) - - def get_object(self, g=None): - cell_contents = uncan(self.cell_contents, g) - def inner(): - return cell_contents - return py3compat.get_closure(inner)[0] - - -class CannedFunction(CannedObject): - - def __init__(self, f): - self._check_type(f) - self.code = f.__code__ - if f.__defaults__: - self.defaults = [ can(fd) for fd in f.__defaults__ ] - else: - self.defaults = None - - closure = py3compat.get_closure(f) - if closure: - self.closure = tuple( can(cell) for cell in closure ) - else: - self.closure = None - - self.module = f.__module__ or '__main__' - self.__name__ = f.__name__ - self.buffers = [] - - def _check_type(self, obj): - assert isinstance(obj, FunctionType), "Not a function type" - - def get_object(self, g=None): - # try to load function back into its module: - if not self.module.startswith('__'): - __import__(self.module) - g = sys.modules[self.module].__dict__ - - if g is None: - g = {} - if self.defaults: - defaults = tuple(uncan(cfd, g) for cfd in self.defaults) - else: - defaults = None - if self.closure: - closure = tuple(uncan(cell, g) for cell in self.closure) - else: - closure = None - newFunc = FunctionType(self.code, g, self.__name__, defaults, closure) - return newFunc - -class CannedClass(CannedObject): - - def __init__(self, cls): - self._check_type(cls) - self.name = cls.__name__ - self.old_style = not isinstance(cls, type) - self._canned_dict = {} - for k,v in cls.__dict__.items(): - if k not in ('__weakref__', '__dict__'): - self._canned_dict[k] = can(v) - if self.old_style: - mro = [] - else: - mro = cls.mro() - - self.parents = [ can(c) for c in mro[1:] ] - self.buffers = [] - - def _check_type(self, obj): - assert isinstance(obj, class_type), "Not a class type" - - def get_object(self, g=None): - parents = tuple(uncan(p, g) for p in self.parents) - return type(self.name, parents, uncan_dict(self._canned_dict, g=g)) - -class CannedArray(CannedObject): - def __init__(self, obj): - from numpy import ascontiguousarray - self.shape = obj.shape - self.dtype = obj.dtype.descr if obj.dtype.fields else obj.dtype.str - self.pickled = False - if sum(obj.shape) == 0: - self.pickled = True - elif obj.dtype == 'O': - # can't handle object dtype with buffer approach - self.pickled = True - elif obj.dtype.fields and any(dt == 'O' for dt,sz in obj.dtype.fields.values()): - self.pickled = True - if self.pickled: - # just pickle it - self.buffers = [pickle.dumps(obj, PICKLE_PROTOCOL)] - else: - # ensure contiguous - obj = ascontiguousarray(obj, dtype=None) - self.buffers = [buffer(obj)] - - def get_object(self, g=None): - from numpy import frombuffer - data = self.buffers[0] - if self.pickled: - # we just pickled it - return pickle.loads(buffer_to_bytes_py2(data)) - else: - return frombuffer(data, dtype=self.dtype).reshape(self.shape) - - -class CannedBytes(CannedObject): - wrap = bytes - def __init__(self, obj): - self.buffers = [obj] - - def get_object(self, g=None): - data = self.buffers[0] - return self.wrap(data) - -def CannedBuffer(CannedBytes): - wrap = buffer - -#------------------------------------------------------------------------------- -# Functions -#------------------------------------------------------------------------------- - -def _import_mapping(mapping, original=None): - """import any string-keys in a type mapping - - """ - log = get_logger() - log.debug("Importing canning map") - for key,value in list(mapping.items()): - if isinstance(key, string_types): - try: - cls = import_item(key) - except Exception: - if original and key not in original: - # only message on user-added classes - log.error("canning class not importable: %r", key, exc_info=True) - mapping.pop(key) - else: - mapping[cls] = mapping.pop(key) - -def istype(obj, check): - """like isinstance(obj, check), but strict - - This won't catch subclasses. - """ - if isinstance(check, tuple): - for cls in check: - if type(obj) is cls: - return True - return False - else: - return type(obj) is check - -def can(obj): - """prepare an object for pickling""" - - import_needed = False - - for cls,canner in iteritems(can_map): - if isinstance(cls, string_types): - import_needed = True - break - elif istype(obj, cls): - return canner(obj) - - if import_needed: - # perform can_map imports, then try again - # this will usually only happen once - _import_mapping(can_map, _original_can_map) - return can(obj) - - return obj - -def can_class(obj): - if isinstance(obj, class_type) and obj.__module__ == '__main__': - return CannedClass(obj) - else: - return obj - -def can_dict(obj): - """can the *values* of a dict""" - if istype(obj, dict): - newobj = {} - for k, v in iteritems(obj): - newobj[k] = can(v) - return newobj - else: - return obj - -sequence_types = (list, tuple, set) - -def can_sequence(obj): - """can the elements of a sequence""" - if istype(obj, sequence_types): - t = type(obj) - return t([can(i) for i in obj]) - else: - return obj - -def uncan(obj, g=None): - """invert canning""" - - import_needed = False - for cls,uncanner in iteritems(uncan_map): - if isinstance(cls, string_types): - import_needed = True - break - elif isinstance(obj, cls): - return uncanner(obj, g) - - if import_needed: - # perform uncan_map imports, then try again - # this will usually only happen once - _import_mapping(uncan_map, _original_uncan_map) - return uncan(obj, g) - - return obj - -def uncan_dict(obj, g=None): - if istype(obj, dict): - newobj = {} - for k, v in iteritems(obj): - newobj[k] = uncan(v,g) - return newobj - else: - return obj - -def uncan_sequence(obj, g=None): - if istype(obj, sequence_types): - t = type(obj) - return t([uncan(i,g) for i in obj]) - else: - return obj - -def _uncan_dependent_hook(dep, g=None): - dep.check_dependency() - -def can_dependent(obj): - return CannedObject(obj, keys=('f', 'df'), hook=_uncan_dependent_hook) - -#------------------------------------------------------------------------------- -# API dictionaries -#------------------------------------------------------------------------------- - -# These dicts can be extended for custom serialization of new objects - -can_map = { - 'IPython.parallel.dependent' : can_dependent, - 'numpy.ndarray' : CannedArray, - FunctionType : CannedFunction, - bytes : CannedBytes, - buffer : CannedBuffer, - cell_type : CannedCell, - class_type : can_class, -} - -uncan_map = { - CannedObject : lambda obj, g: obj.get_object(g), -} - -# for use in _import_mapping: -_original_can_map = can_map.copy() -_original_uncan_map = uncan_map.copy() diff --git a/ipython_kernel/pylab/__init__.py b/ipython_kernel/pylab/__init__.py deleted file mode 100644 index e69de29..0000000 --- a/ipython_kernel/pylab/__init__.py +++ /dev/null diff --git a/ipython_kernel/pylab/backend_inline.py b/ipython_kernel/pylab/backend_inline.py deleted file mode 100644 index fc31daf..0000000 --- a/ipython_kernel/pylab/backend_inline.py +++ /dev/null @@ -1,141 +0,0 @@ -"""A matplotlib backend for publishing figures via display_data""" - -# Copyright (c) IPython Development Team. -# Distributed under the terms of the Modified BSD License. - -from __future__ import print_function - -import matplotlib -from matplotlib.backends.backend_agg import new_figure_manager, FigureCanvasAgg # analysis: ignore -from matplotlib._pylab_helpers import Gcf - -from IPython.core.getipython import get_ipython -from IPython.core.display import display - -from .config import InlineBackend - - -def show(close=None): - """Show all figures as SVG/PNG payloads sent to the IPython clients. - - Parameters - ---------- - close : bool, optional - If true, a ``plt.close('all')`` call is automatically issued after - sending all the figures. If this is set, the figures will entirely - removed from the internal list of figures. - """ - if close is None: - close = InlineBackend.instance().close_figures - try: - for figure_manager in Gcf.get_all_fig_managers(): - display(figure_manager.canvas.figure) - finally: - show._to_draw = [] - # only call close('all') if any to close - # close triggers gc.collect, which can be slow - if close and Gcf.get_all_fig_managers(): - matplotlib.pyplot.close('all') - - -# This flag will be reset by draw_if_interactive when called -show._draw_called = False -# list of figures to draw when flush_figures is called -show._to_draw = [] - - -def draw_if_interactive(): - """ - Is called after every pylab drawing command - """ - # signal that the current active figure should be sent at the end of - # execution. Also sets the _draw_called flag, signaling that there will be - # something to send. At the end of the code execution, a separate call to - # flush_figures() will act upon these values - manager = Gcf.get_active() - if manager is None: - return - fig = manager.canvas.figure - - # Hack: matplotlib FigureManager objects in interacive backends (at least - # in some of them) monkeypatch the figure object and add a .show() method - # to it. This applies the same monkeypatch in order to support user code - # that might expect `.show()` to be part of the official API of figure - # objects. - # For further reference: - # https://github.com/ipython/ipython/issues/1612 - # https://github.com/matplotlib/matplotlib/issues/835 - - if not hasattr(fig, 'show'): - # Queue up `fig` for display - fig.show = lambda *a: display(fig) - - # If matplotlib was manually set to non-interactive mode, this function - # should be a no-op (otherwise we'll generate duplicate plots, since a user - # who set ioff() manually expects to make separate draw/show calls). - if not matplotlib.is_interactive(): - return - - # ensure current figure will be drawn, and each subsequent call - # of draw_if_interactive() moves the active figure to ensure it is - # drawn last - try: - show._to_draw.remove(fig) - except ValueError: - # ensure it only appears in the draw list once - pass - # Queue up the figure for drawing in next show() call - show._to_draw.append(fig) - show._draw_called = True - - -def flush_figures(): - """Send all figures that changed - - This is meant to be called automatically and will call show() if, during - prior code execution, there had been any calls to draw_if_interactive. - - This function is meant to be used as a post_execute callback in IPython, - so user-caused errors are handled with showtraceback() instead of being - allowed to raise. If this function is not called from within IPython, - then these exceptions will raise. - """ - if not show._draw_called: - return - - if InlineBackend.instance().close_figures: - # ignore the tracking, just draw and close all figures - try: - return show(True) - except Exception as e: - # safely show traceback if in IPython, else raise - ip = get_ipython() - if ip is None: - raise e - else: - ip.showtraceback() - return - try: - # exclude any figures that were closed: - active = set([fm.canvas.figure for fm in Gcf.get_all_fig_managers()]) - for fig in [ fig for fig in show._to_draw if fig in active ]: - try: - display(fig) - except Exception as e: - # safely show traceback if in IPython, else raise - ip = get_ipython() - if ip is None: - raise e - else: - ip.showtraceback() - return - finally: - # clear flags for next round - show._to_draw = [] - show._draw_called = False - - -# Changes to matplotlib in version 1.2 requires a mpl backend to supply a default -# figurecanvas. This is set here to a Agg canvas -# See https://github.com/matplotlib/matplotlib/pull/1125 -FigureCanvas = FigureCanvasAgg diff --git a/ipython_kernel/pylab/config.py b/ipython_kernel/pylab/config.py deleted file mode 100644 index 1eaa08d..0000000 --- a/ipython_kernel/pylab/config.py +++ /dev/null @@ -1,119 +0,0 @@ -"""Configurable for configuring the IPython inline backend - -This module does not import anything from matplotlib. -""" -#----------------------------------------------------------------------------- -# 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 IPython.config import Config -from IPython.config.configurable import SingletonConfigurable -from IPython.utils.traitlets import ( - Dict, Instance, CaselessStrEnum, Set, Bool, Int, TraitError, Unicode -) -from IPython.utils.warn import warn - -#----------------------------------------------------------------------------- -# Configurable for inline backend options -#----------------------------------------------------------------------------- - -def pil_available(): - """Test if PIL/Pillow is available""" - out = False - try: - from PIL import Image - out = True - except: - pass - return out - -# inherit from InlineBackendConfig for deprecation purposes -class InlineBackendConfig(SingletonConfigurable): - pass - -class InlineBackend(InlineBackendConfig): - """An object to store configuration of the inline backend.""" - - def _config_changed(self, name, old, new): - # warn on change of renamed config section - if new.InlineBackendConfig != getattr(old, 'InlineBackendConfig', Config()): - warn("InlineBackendConfig has been renamed to InlineBackend") - super(InlineBackend, self)._config_changed(name, old, new) - - # The typical default figure size is too large for inline use, - # so we shrink the figure size to 6x4, and tweak fonts to - # make that fit. - rc = Dict({'figure.figsize': (6.0,4.0), - # play nicely with white background in the Qt and notebook frontend - 'figure.facecolor': (1,1,1,0), - 'figure.edgecolor': (1,1,1,0), - 'axes.facecolor': (1,1,1,0), - # 12pt labels get cutoff on 6x4 logplots, so use 10pt. - 'font.size': 10, - # 72 dpi matches SVG/qtconsole - # this only affects PNG export, as SVG has no dpi setting - 'savefig.dpi': 72, - # 10pt still needs a little more room on the xlabel: - 'figure.subplot.bottom' : .125 - }, config=True, - help="""Subset of matplotlib rcParams that should be different for the - inline backend.""" - ) - - figure_formats = Set({'png'}, config=True, - help="""A set of figure formats to enable: 'png', - 'retina', 'jpeg', 'svg', 'pdf'.""") - - def _update_figure_formatters(self): - if self.shell is not None: - from IPython.core.pylabtools import select_figure_formats - select_figure_formats(self.shell, self.figure_formats, **self.print_figure_kwargs) - - def _figure_formats_changed(self, name, old, new): - if 'jpg' in new or 'jpeg' in new: - if not pil_available(): - raise TraitError("Requires PIL/Pillow for JPG figures") - self._update_figure_formatters() - - figure_format = Unicode(config=True, help="""The figure format to enable (deprecated - use `figure_formats` instead)""") - - def _figure_format_changed(self, name, old, new): - if new: - self.figure_formats = {new} - - print_figure_kwargs = Dict({'bbox_inches' : 'tight'}, config=True, - help="""Extra kwargs to be passed to fig.canvas.print_figure. - - Logical examples include: bbox_inches, quality (for jpeg figures), etc. - """ - ) - _print_figure_kwargs_changed = _update_figure_formatters - - close_figures = Bool(True, config=True, - help="""Close all figures at the end of each cell. - - When True, ensures that each cell starts with no active figures, but it - also means that one must keep track of references in order to edit or - redraw figures in subsequent cells. This mode is ideal for the notebook, - where residual plots from other cells might be surprising. - - When False, one must call figure() to create new figures. This means - that gcf() and getfigs() can reference figures created in other cells, - and the active figure can continue to be edited with pylab/pyplot - methods that reference the current active figure. This mode facilitates - iterative editing of figures, and behaves most consistently with - other matplotlib backends, but figure barriers between cells must - be explicit. - """) - - shell = Instance('IPython.core.interactiveshell.InteractiveShellABC', - allow_none=True) - diff --git a/ipython_kernel/resources/logo-32x32.png b/ipython_kernel/resources/logo-32x32.png deleted file mode 100644 index be81330765764699553aa4fbaf0e9fc27c20c6d2..0000000000000000000000000000000000000000 GIT binary patch literal 0 Hc$@enw2jbMszQuf3kC$K7$S;4l;TgSRfzha5>pgWAEY9PR!IdB zTSZXtp`b02h)|SJ3#AW|AKF?KgNSQ|Sg=ZCgHaT%F`4#g>iG8;N__GBLh26(2qOGO9};SPeUDLyV^m!K($s69;fB|`Ui z{nqhFk+};I5Vb+1*IC+gaNEtF()dX{`(!1eUb?=>+~p#JOj-qUi2^^^uzi1p(thMz&#&LJq>Cf)~tBhxq*;Npy$=mheX>2t4(OR zWk&s74VR$m@6rlD?Nud*cEGO2$>|mV&tzP1%j+W-N_;a>$_%)&Yn?|hX(50fV5s); zkLsKLb20?nJo-eIQ&vLU?~T?v{=JUtFa!EFC;;*i2@lY(#8Ur2b{` z!nc_6C42;g?mDnyRp9)U84ZxUv=Ja10XDYX;KZ|EPJ`h_&;S{#m9Q!a*xC#MiI?P; zx4sNs;+Uif!Da~pAQU}S)ww^M;qb(^FD`~`s1D2+foklsECF&ZZKas%kF~bU-M9bY zuhs+V2CzISGy`A&Lkq;MkgWkjD)R)1WqC_*Tx45LdH=lV+}XPaAFS+wus(ZG#IuZp zEE@YdBSMkKnX~3J?j7u_^kl&mQ+7t_i^t4YG6X0cS+J89bl~_Igc~wh(?=P_08}Iv z0NHqkz|x<~Z;3paR=+czhC^#TYlWDdd@Rc|#cCUooxt4edl>=;-neznjL)SlXtdOh z=2NAO%Gxj%BLM->i|(q=eePLs=%wD>*F6312}yTRxn%!IzZtmkN`YjQBMNkckc4h;pSXO%%?N2y_ccz zS`INlItXC6DR;umS}Mn43NzsR7MS0Sf|rrv1n7UvdO9UC3&XB+{A~zNMyyXY@lF_q zps;z-9S*u(m1{=;T?YYxd%vmwj5N7<3lv^}?EK6DlWbFPZoBI|w5zEE06;(VF2nD? z_QUyZi0eRG2jDb-NyvSR5{_bd`5o6W`WOCh1>4`s79R;zVm_k)0000kjcw83I)rwURf9H)0d)l3>^8*`$3&wplXaSnv^ouL zxig617>J8x{$<2zvZ44vm&sPJz*Z;|)^sj29S|e(QD`@&rR&E%&(A;Zx#ym9?>Xnb z=k|6x#=dRS_rB-ex99mi&+qvXHKxY@^N`8h{N|r@TsA(& zsCpk!BK%oN(i-QUbD69cd?H!sn{mG-Lrs4l70Gd-TRSnnlw<)m#)CQ1364@U( zb1huc+%2C?f zYjwl_PTT;XJ$4oVU=Be51c+U`UEX_ls%aSHu0jnXMCH=*+Sd}C2irp2UqB=Z0E)N85&+GM z>q^`|nwHj#MQ}!_hFxHI0P?d05b<<^{$@L)xRXP$*7NMe_Al`SAe_UPXbALJOH3_5 zcM?1d0-}ThP+N;&R(k{$P!RUyBLuGx7u*NjI0EqWx*LBO^)ny+&f^)CC}~0x8ViOeXmOp`hB@Wk%DqXy3C1Q0?$fKnaUFPm1OP-ZjVK`deF} zSeAF2mylo&RQ`&~-?2v|r4t6AY0JJPRN1JijUXW&kBk6^2Cvr^I{u5UuqP$>16T2K z9R$k@xromL3Y>lI8J_*t?K0<)3neE)OPIZA`y$|W32O|S;>(;-_BoaG7O_=2G z6D)9yzzx@Wf#9y!>3jH(JLX0Lz*6}#sWZF@h^aPF)_fq;^c^8JPiTh*0JRcGe<2b8 zN_@jF0rBt^lR=9@fPBV9TT3%D0)}bdo{O3TaO38^?3k0H{bUT-qpE!%+$xpS2LPf1an-UJ2DJ9KqouI6R;TMiW;X0gzCw zHO|Y+R^XVXy4>IM=$idVj4jUz?GhXz)&RZ6C=nuAOFRF5GYcGpaQ8++^bVf8D~Ysh zasY5*fBszU=;2(eHKTx{cJgCCqK3OyNG?6L{qEzi@F-xtJB056lt^D=Mgd{1M;|3o zptQ9-Tf6}9DG0x>)iWA;*7d!}f34XL)z1YaJw+(tZvmBs7Qne4&B4c^71J}j0Cl!mHAtQyc|{3a zzhEhE=-#}lmuK6SVomEdD6U096Gc<`?9IYNt09igBXq$&uNwIPk|#@Za%kz^ysDSy z+SWt37r+OM+U|uhJI|3tadcq`kq(&o0OEv1c4+!|*N<=iE&E$ngIs6G>;UsEYRUoH z*N{CGAkP{BAQ=ioDsa;2iU)Z9+n0m7&G0!|IACWkdlBI1w@S4<6a_#XeAP z1@TTJt)oc(Zd&9NrG)FXraO%+ph_!V8AqA`#S;PpD4=AwE!!e+(HZRH`J4Q`%$PKn zL#RLx{&wZdvT~>OrXG{ynQ!)hTxeLDW{is=avgT_Q@X{_ryQSRf-z;cCzzZ%57>p+XNOwhgQWFSDdeo<;8g((CJEj(Z4)c6IEc3%k9{YIG zk+*m8hahOo-7ycwG7kU%o^1X(sCP!|<+23tKd4KhH8=|#dkr8hdCPys`Kq?qW`a42rV{8owiaTo2X%UpUcJedmjJmB_0Mh> zDfdCyN&K%dp1k=ojE<}Z_*K9@aFMV5@X-t5FOkM$vasuX>}!EgFkb%DENHq8U>%?f zGQUv=A_?Fk1g}BS5Ab;i4xv&G$^7TeU}{W_sWCMsdHfgT%>1XE)oy threshold: - # buffer larger than threshold, prevent pickling - obj.buffers[i] = None - buffers.append(buf) - elif isinstance(buf, buffer): - # buffer too small for separate send, coerce to bytes - # because pickling buffer objects just results in broken pointers - obj.buffers[i] = bytes(buf) - return buffers - -def _restore_buffers(obj, buffers): - """restore buffers extracted by """ - if isinstance(obj, CannedObject) and obj.buffers: - for i,buf in enumerate(obj.buffers): - if buf is None: - obj.buffers[i] = buffers.pop(0) - -def serialize_object(obj, buffer_threshold=MAX_BYTES, item_threshold=MAX_ITEMS): - """Serialize an object into a list of sendable buffers. - - Parameters - ---------- - - obj : object - The object to be serialized - buffer_threshold : int - The threshold (in bytes) for pulling out data buffers - to avoid pickling them. - item_threshold : int - The maximum number of items over which canning will iterate. - Containers (lists, dicts) larger than this will be pickled without - introspection. - - Returns - ------- - [bufs] : list of buffers representing the serialized object. - """ - buffers = [] - if istype(obj, sequence_types) and len(obj) < item_threshold: - cobj = can_sequence(obj) - for c in cobj: - buffers.extend(_extract_buffers(c, buffer_threshold)) - elif istype(obj, dict) and len(obj) < item_threshold: - cobj = {} - for k in sorted(obj): - c = can(obj[k]) - buffers.extend(_extract_buffers(c, buffer_threshold)) - cobj[k] = c - else: - cobj = can(obj) - buffers.extend(_extract_buffers(cobj, buffer_threshold)) - - buffers.insert(0, pickle.dumps(cobj, PICKLE_PROTOCOL)) - return buffers - -def deserialize_object(buffers, g=None): - """reconstruct an object serialized by serialize_object from data buffers. - - Parameters - ---------- - - bufs : list of buffers/bytes - - g : globals to be used when uncanning - - Returns - ------- - - (newobj, bufs) : unpacked object, and the list of remaining unused buffers. - """ - bufs = list(buffers) - pobj = buffer_to_bytes_py2(bufs.pop(0)) - canned = pickle.loads(pobj) - if istype(canned, sequence_types) and len(canned) < MAX_ITEMS: - for c in canned: - _restore_buffers(c, bufs) - newobj = uncan_sequence(canned, g) - elif istype(canned, dict) and len(canned) < MAX_ITEMS: - newobj = {} - for k in sorted(canned): - c = canned[k] - _restore_buffers(c, bufs) - newobj[k] = uncan(c, g) - else: - _restore_buffers(canned, bufs) - newobj = uncan(canned, g) - - return newobj, bufs - -def pack_apply_message(f, args, kwargs, buffer_threshold=MAX_BYTES, item_threshold=MAX_ITEMS): - """pack up a function, args, and kwargs to be sent over the wire - - Each element of args/kwargs will be canned for special treatment, - but inspection will not go any deeper than that. - - Any object whose data is larger than `threshold` will not have their data copied - (only numpy arrays and bytes/buffers support zero-copy) - - Message will be a list of bytes/buffers of the format: - - [ cf, pinfo, , ] - - With length at least two + len(args) + len(kwargs) - """ - - arg_bufs = list(chain.from_iterable( - serialize_object(arg, buffer_threshold, item_threshold) for arg in args)) - - kw_keys = sorted(kwargs.keys()) - kwarg_bufs = list(chain.from_iterable( - serialize_object(kwargs[key], buffer_threshold, item_threshold) for key in kw_keys)) - - info = dict(nargs=len(args), narg_bufs=len(arg_bufs), kw_keys=kw_keys) - - msg = [pickle.dumps(can(f), PICKLE_PROTOCOL)] - msg.append(pickle.dumps(info, PICKLE_PROTOCOL)) - msg.extend(arg_bufs) - msg.extend(kwarg_bufs) - - return msg - -def unpack_apply_message(bufs, g=None, copy=True): - """unpack f,args,kwargs from buffers packed by pack_apply_message() - Returns: original f,args,kwargs""" - bufs = list(bufs) # allow us to pop - assert len(bufs) >= 2, "not enough buffers!" - pf = buffer_to_bytes_py2(bufs.pop(0)) - f = uncan(pickle.loads(pf), g) - pinfo = buffer_to_bytes_py2(bufs.pop(0)) - info = pickle.loads(pinfo) - arg_bufs, kwarg_bufs = bufs[:info['narg_bufs']], bufs[info['narg_bufs']:] - - args = [] - for i in range(info['nargs']): - arg, arg_bufs = deserialize_object(arg_bufs, g) - args.append(arg) - args = tuple(args) - assert not arg_bufs, "Shouldn't be any arg bufs left over" - - kwargs = {} - for key in info['kw_keys']: - kwarg, kwarg_bufs = deserialize_object(kwarg_bufs, g) - kwargs[key] = kwarg - assert not kwarg_bufs, "Shouldn't be any kwarg bufs left over" - - return f,args,kwargs diff --git a/ipython_kernel/session.py b/ipython_kernel/session.py deleted file mode 100644 index 6c9936d..0000000 --- a/ipython_kernel/session.py +++ /dev/null @@ -1 +0,0 @@ -from jupyter_client.session import * diff --git a/ipython_kernel/tests/__init__.py b/ipython_kernel/tests/__init__.py deleted file mode 100644 index e69de29..0000000 --- a/ipython_kernel/tests/__init__.py +++ /dev/null diff --git a/ipython_kernel/tests/test_connect.py b/ipython_kernel/tests/test_connect.py deleted file mode 100644 index 63e1610..0000000 --- a/ipython_kernel/tests/test_connect.py +++ /dev/null @@ -1,61 +0,0 @@ -"""Tests for kernel connection utilities""" - -# Copyright (c) IPython Development Team. -# Distributed under the terms of the Modified BSD License. - -import json -import os - -import nose.tools as nt - -from IPython.config import Config -from IPython.utils.tempdir import TemporaryDirectory, TemporaryWorkingDirectory -from IPython.utils.py3compat import str_to_bytes -from ipython_kernel import connect -from ipython_kernel.kernelapp import IPKernelApp - - -sample_info = dict(ip='1.2.3.4', transport='ipc', - shell_port=1, hb_port=2, iopub_port=3, stdin_port=4, control_port=5, - key=b'abc123', signature_scheme='hmac-md5', - ) - - -class DummyKernelApp(IPKernelApp): - def initialize(self, argv=[]): - self.init_profile_dir() - self.init_connection_file() - - -def test_get_connection_file(): - cfg = Config() - with TemporaryWorkingDirectory() as d: - cfg.ProfileDir.location = d - cf = 'kernel.json' - app = DummyKernelApp(config=cfg, connection_file=cf) - app.initialize() - - profile_cf = os.path.join(app.profile_dir.location, 'security', cf) - nt.assert_equal(profile_cf, app.abs_connection_file) - with open(profile_cf, 'w') as f: - f.write("{}") - nt.assert_true(os.path.exists(profile_cf)) - nt.assert_equal(connect.get_connection_file(app), profile_cf) - - app.connection_file = cf - nt.assert_equal(connect.get_connection_file(app), profile_cf) - - -def test_get_connection_info(): - with TemporaryDirectory() as d: - cf = os.path.join(d, 'kernel.json') - connect.write_connection_file(cf, **sample_info) - json_info = connect.get_connection_info(cf) - info = connect.get_connection_info(cf, unpack=True) - - nt.assert_equal(type(json_info), type("")) - nt.assert_equal(info, sample_info) - - info2 = json.loads(json_info) - info2['key'] = str_to_bytes(info2['key']) - nt.assert_equal(info2, sample_info) diff --git a/ipython_kernel/tests/test_embed_kernel.py b/ipython_kernel/tests/test_embed_kernel.py deleted file mode 100644 index ead1bc8..0000000 --- a/ipython_kernel/tests/test_embed_kernel.py +++ /dev/null @@ -1,197 +0,0 @@ -"""test IPython.embed_kernel()""" - -#------------------------------------------------------------------------------- -# Copyright (C) 2012 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 -#------------------------------------------------------------------------------- - -import os -import shutil -import sys -import tempfile -import time - -from contextlib import contextmanager -from subprocess import Popen, PIPE - -import nose.tools as nt - -from jupyter_client import BlockingKernelClient -from IPython.utils import path, py3compat -from IPython.utils.py3compat import unicode_type - -#------------------------------------------------------------------------------- -# Tests -#------------------------------------------------------------------------------- - -SETUP_TIMEOUT = 60 -TIMEOUT = 15 - -def setup(): - """setup temporary IPYTHONDIR for tests""" - global IPYTHONDIR - global env - global save_get_ipython_dir - - IPYTHONDIR = tempfile.mkdtemp() - - env = os.environ.copy() - env["IPYTHONDIR"] = IPYTHONDIR - - save_get_ipython_dir = path.get_ipython_dir - path.get_ipython_dir = lambda : IPYTHONDIR - - -def teardown(): - path.get_ipython_dir = save_get_ipython_dir - - try: - shutil.rmtree(IPYTHONDIR) - except (OSError, IOError): - # no such file - pass - - -@contextmanager -def setup_kernel(cmd): - """start an embedded kernel in a subprocess, and wait for it to be ready - - Returns - ------- - kernel_manager: connected KernelManager instance - """ - kernel = Popen([sys.executable, '-c', cmd], stdout=PIPE, stderr=PIPE, env=env) - connection_file = os.path.join(IPYTHONDIR, - 'profile_default', - 'security', - 'kernel-%i.json' % kernel.pid - ) - # wait for connection file to exist, timeout after 5s - tic = time.time() - while not os.path.exists(connection_file) \ - and kernel.poll() is None \ - and time.time() < tic + SETUP_TIMEOUT: - time.sleep(0.1) - - if kernel.poll() is not None: - o,e = kernel.communicate() - e = py3compat.cast_unicode(e) - raise IOError("Kernel failed to start:\n%s" % e) - - if not os.path.exists(connection_file): - if kernel.poll() is None: - kernel.terminate() - raise IOError("Connection file %r never arrived" % connection_file) - - client = BlockingKernelClient(connection_file=connection_file) - client.load_connection_file() - client.start_channels() - client.wait_for_ready() - - try: - yield client - finally: - client.stop_channels() - kernel.terminate() - -def test_embed_kernel_basic(): - """IPython.embed_kernel() is basically functional""" - cmd = '\n'.join([ - 'from IPython import embed_kernel', - 'def go():', - ' a=5', - ' b="hi there"', - ' embed_kernel()', - 'go()', - '', - ]) - - with setup_kernel(cmd) as client: - # oinfo a (int) - msg_id = client.inspect('a') - msg = client.get_shell_msg(block=True, timeout=TIMEOUT) - content = msg['content'] - nt.assert_true(content['found']) - - msg_id = client.execute("c=a*2") - msg = client.get_shell_msg(block=True, timeout=TIMEOUT) - content = msg['content'] - nt.assert_equal(content['status'], u'ok') - - # oinfo c (should be 10) - msg_id = client.inspect('c') - msg = client.get_shell_msg(block=True, timeout=TIMEOUT) - content = msg['content'] - nt.assert_true(content['found']) - text = content['data']['text/plain'] - nt.assert_in('10', text) - -def test_embed_kernel_namespace(): - """IPython.embed_kernel() inherits calling namespace""" - cmd = '\n'.join([ - 'from IPython import embed_kernel', - 'def go():', - ' a=5', - ' b="hi there"', - ' embed_kernel()', - 'go()', - '', - ]) - - with setup_kernel(cmd) as client: - # oinfo a (int) - msg_id = client.inspect('a') - msg = client.get_shell_msg(block=True, timeout=TIMEOUT) - content = msg['content'] - nt.assert_true(content['found']) - text = content['data']['text/plain'] - nt.assert_in(u'5', text) - - # oinfo b (str) - msg_id = client.inspect('b') - msg = client.get_shell_msg(block=True, timeout=TIMEOUT) - content = msg['content'] - nt.assert_true(content['found']) - text = content['data']['text/plain'] - nt.assert_in(u'hi there', text) - - # oinfo c (undefined) - msg_id = client.inspect('c') - msg = client.get_shell_msg(block=True, timeout=TIMEOUT) - content = msg['content'] - nt.assert_false(content['found']) - -def test_embed_kernel_reentrant(): - """IPython.embed_kernel() can be called multiple times""" - cmd = '\n'.join([ - 'from IPython import embed_kernel', - 'count = 0', - 'def go():', - ' global count', - ' embed_kernel()', - ' count = count + 1', - '', - 'while True:' - ' go()', - '', - ]) - - with setup_kernel(cmd) as client: - for i in range(5): - msg_id = client.inspect('count') - msg = client.get_shell_msg(block=True, timeout=TIMEOUT) - content = msg['content'] - nt.assert_true(content['found']) - text = content['data']['text/plain'] - nt.assert_in(unicode_type(i), text) - - # exit from embed_kernel - client.execute("get_ipython().exit_now = True") - msg = client.get_shell_msg(block=True, timeout=TIMEOUT) - time.sleep(0.2) diff --git a/ipython_kernel/tests/test_kernel.py b/ipython_kernel/tests/test_kernel.py deleted file mode 100644 index 131dc70..0000000 --- a/ipython_kernel/tests/test_kernel.py +++ /dev/null @@ -1,228 +0,0 @@ -# coding: utf-8 -"""test the IPython Kernel""" - -# Copyright (c) IPython Development Team. -# Distributed under the terms of the Modified BSD License. - -import io -import os.path -import sys - -import nose.tools as nt - -from IPython.testing import decorators as dec, tools as tt -from IPython.utils import py3compat -from IPython.utils.path import locate_profile -from IPython.utils.tempdir import TemporaryDirectory - -from .utils import (new_kernel, kernel, TIMEOUT, assemble_output, execute, - flush_channels, wait_for_idle) - - -def _check_mp_mode(kc, expected=False, stream="stdout"): - execute(kc=kc, code="import sys") - flush_channels(kc) - msg_id, content = execute(kc=kc, code="print (sys.%s._check_mp_mode())" % stream) - stdout, stderr = assemble_output(kc.iopub_channel) - nt.assert_equal(eval(stdout.strip()), expected) - - -# printing tests - -def test_simple_print(): - """simple print statement in kernel""" - with kernel() as kc: - iopub = kc.iopub_channel - msg_id, content = execute(kc=kc, code="print ('hi')") - stdout, stderr = assemble_output(iopub) - nt.assert_equal(stdout, 'hi\n') - nt.assert_equal(stderr, '') - _check_mp_mode(kc, expected=False) - - -def test_sys_path(): - """test that sys.path doesn't get messed up by default""" - with kernel() as kc: - msg_id, content = execute(kc=kc, code="import sys; print (repr(sys.path[0]))") - stdout, stderr = assemble_output(kc.iopub_channel) - nt.assert_equal(stdout, "''\n") - -def test_sys_path_profile_dir(): - """test that sys.path doesn't get messed up when `--profile-dir` is specified""" - - with new_kernel(['--profile-dir', locate_profile('default')]) as kc: - msg_id, content = execute(kc=kc, code="import sys; print (repr(sys.path[0]))") - stdout, stderr = assemble_output(kc.iopub_channel) - nt.assert_equal(stdout, "''\n") - -@dec.knownfailureif(sys.platform == 'win32', "subprocess prints fail on Windows") -def test_subprocess_print(): - """printing from forked mp.Process""" - with new_kernel() as kc: - iopub = kc.iopub_channel - - _check_mp_mode(kc, expected=False) - flush_channels(kc) - np = 5 - code = '\n'.join([ - "from __future__ import print_function", - "import multiprocessing as mp", - "pool = [mp.Process(target=print, args=('hello', i,)) for i in range(%i)]" % np, - "for p in pool: p.start()", - "for p in pool: p.join()" - ]) - - expected = '\n'.join([ - "hello %s" % i for i in range(np) - ]) + '\n' - - msg_id, content = execute(kc=kc, code=code) - stdout, stderr = assemble_output(iopub) - nt.assert_equal(stdout.count("hello"), np, stdout) - for n in range(np): - nt.assert_equal(stdout.count(str(n)), 1, stdout) - nt.assert_equal(stderr, '') - _check_mp_mode(kc, expected=False) - _check_mp_mode(kc, expected=False, stream="stderr") - - -def test_subprocess_noprint(): - """mp.Process without print doesn't trigger iostream mp_mode""" - with kernel() as kc: - iopub = kc.iopub_channel - - np = 5 - code = '\n'.join([ - "import multiprocessing as mp", - "pool = [mp.Process(target=range, args=(i,)) for i in range(%i)]" % np, - "for p in pool: p.start()", - "for p in pool: p.join()" - ]) - - msg_id, content = execute(kc=kc, code=code) - stdout, stderr = assemble_output(iopub) - nt.assert_equal(stdout, '') - nt.assert_equal(stderr, '') - - _check_mp_mode(kc, expected=False) - _check_mp_mode(kc, expected=False, stream="stderr") - - -@dec.knownfailureif(sys.platform == 'win32', "subprocess prints fail on Windows") -def test_subprocess_error(): - """error in mp.Process doesn't crash""" - with new_kernel() as kc: - iopub = kc.iopub_channel - - code = '\n'.join([ - "import multiprocessing as mp", - "p = mp.Process(target=int, args=('hi',))", - "p.start()", - "p.join()", - ]) - - msg_id, content = execute(kc=kc, code=code) - stdout, stderr = assemble_output(iopub) - nt.assert_equal(stdout, '') - nt.assert_true("ValueError" in stderr, stderr) - - _check_mp_mode(kc, expected=False) - _check_mp_mode(kc, expected=False, stream="stderr") - -# raw_input tests - -def test_raw_input(): - """test [raw_]input""" - with kernel() as kc: - iopub = kc.iopub_channel - - input_f = "input" if py3compat.PY3 else "raw_input" - theprompt = "prompt> " - code = 'print({input_f}("{theprompt}"))'.format(**locals()) - msg_id = kc.execute(code, allow_stdin=True) - msg = kc.get_stdin_msg(block=True, timeout=TIMEOUT) - nt.assert_equal(msg['header']['msg_type'], u'input_request') - content = msg['content'] - nt.assert_equal(content['prompt'], theprompt) - text = "some text" - kc.input(text) - reply = kc.get_shell_msg(block=True, timeout=TIMEOUT) - nt.assert_equal(reply['content']['status'], 'ok') - stdout, stderr = assemble_output(iopub) - nt.assert_equal(stdout, text + "\n") - - -@dec.skipif(py3compat.PY3) -def test_eval_input(): - """test input() on Python 2""" - with kernel() as kc: - iopub = kc.iopub_channel - - input_f = "input" if py3compat.PY3 else "raw_input" - theprompt = "prompt> " - code = 'print(input("{theprompt}"))'.format(**locals()) - msg_id = kc.execute(code, allow_stdin=True) - msg = kc.get_stdin_msg(block=True, timeout=TIMEOUT) - nt.assert_equal(msg['header']['msg_type'], u'input_request') - content = msg['content'] - nt.assert_equal(content['prompt'], theprompt) - kc.input("1+1") - reply = kc.get_shell_msg(block=True, timeout=TIMEOUT) - nt.assert_equal(reply['content']['status'], 'ok') - stdout, stderr = assemble_output(iopub) - nt.assert_equal(stdout, "2\n") - - -def test_save_history(): - # Saving history from the kernel with %hist -f was failing because of - # unicode problems on Python 2. - with kernel() as kc, TemporaryDirectory() as td: - file = os.path.join(td, 'hist.out') - execute(u'a=1', kc=kc) - wait_for_idle(kc) - execute(u'b=u"abcþ"', kc=kc) - wait_for_idle(kc) - _, reply = execute("%hist -f " + file, kc=kc) - nt.assert_equal(reply['status'], 'ok') - with io.open(file, encoding='utf-8') as f: - content = f.read() - nt.assert_in(u'a=1', content) - nt.assert_in(u'b=u"abcþ"', content) - -def test_help_output(): - """ipython kernel --help-all works""" - tt.help_all_output_test('kernel') - -def test_is_complete(): - with kernel() as kc: - # There are more test cases for this in core - here we just check - # that the kernel exposes the interface correctly. - kc.is_complete('2+2') - reply = kc.get_shell_msg(block=True, timeout=TIMEOUT) - assert reply['content']['status'] == 'complete' - - # SyntaxError should mean it's complete - kc.is_complete('raise = 2') - reply = kc.get_shell_msg(block=True, timeout=TIMEOUT) - assert reply['content']['status'] == 'invalid' - - kc.is_complete('a = [1,\n2,') - reply = kc.get_shell_msg(block=True, timeout=TIMEOUT) - assert reply['content']['status'] == 'incomplete' - assert reply['content']['indent'] == '' - -def test_complete(): - with kernel() as kc: - execute(u'a = 1', kc=kc) - wait_for_idle(kc) - cell = 'import IPython\nb = a.' - kc.complete(cell) - reply = kc.get_shell_msg(block=True, timeout=TIMEOUT) - c = reply['content'] - nt.assert_equal(c['status'], 'ok') - nt.assert_equal(c['cursor_start'], cell.find('a.')) - nt.assert_equal(c['cursor_end'], cell.find('a.') + 2) - matches = c['matches'] - nt.assert_greater(len(matches), 0) - for match in matches: - nt.assert_equal(match[:2], 'a.') diff --git a/ipython_kernel/tests/test_kernelspec.py b/ipython_kernel/tests/test_kernelspec.py deleted file mode 100644 index 8232a14..0000000 --- a/ipython_kernel/tests/test_kernelspec.py +++ /dev/null @@ -1,94 +0,0 @@ -# Copyright (c) IPython Development Team. -# Distributed under the terms of the Modified BSD License. - -import json -import io -import os -import shutil -import sys -import tempfile - -try: - from unittest import mock -except ImportError: - import mock # py2 - -from ipython_kernel.kernelspec import ( - make_ipkernel_cmd, - get_kernel_dict, - write_kernel_spec, - install, - KERNEL_NAME, - RESOURCES, -) - -import nose.tools as nt - -pjoin = os.path.join - - -def test_make_ipkernel_cmd(): - cmd = make_ipkernel_cmd() - nt.assert_equal(cmd, [ - sys.executable, - '-m', - 'ipython_kernel', - '-f', - '{connection_file}' - ]) - - -def assert_kernel_dict(d): - nt.assert_equal(d['argv'], make_ipkernel_cmd()) - nt.assert_equal(d['display_name'], 'Python %i' % sys.version_info[0]) - nt.assert_equal(d['language'], 'python') - - -def test_get_kernel_dict(): - d = get_kernel_dict() - assert_kernel_dict(d) - - -def assert_is_spec(path): - for fname in os.listdir(RESOURCES): - dst = pjoin(path, fname) - assert os.path.exists(dst) - kernel_json = pjoin(path, 'kernel.json') - assert os.path.exists(kernel_json) - with io.open(kernel_json, encoding='utf8') as f: - d = json.load(f) - - -def test_write_kernel_spec(): - path = write_kernel_spec() - assert_is_spec(path) - shutil.rmtree(path) - - -def test_write_kernel_spec_path(): - path = os.path.join(tempfile.mkdtemp(), KERNEL_NAME) - path2 = write_kernel_spec(path) - nt.assert_equal(path, path2) - assert_is_spec(path) - shutil.rmtree(path) - - -def test_install_user(): - ipydir = tempfile.mkdtemp() - - with mock.patch.dict(os.environ, {'IPYTHONDIR': ipydir}): - install(user=True) - - assert_is_spec(os.path.join(ipydir, 'kernels', KERNEL_NAME)) - - -def test_install(): - system_kernel_dir = tempfile.mkdtemp(suffix='kernels') - - with mock.patch('jupyter_client.kernelspec.SYSTEM_KERNEL_DIRS', - [system_kernel_dir]): - install() - - assert_is_spec(os.path.join(system_kernel_dir, KERNEL_NAME)) - - diff --git a/ipython_kernel/tests/test_message_spec.py b/ipython_kernel/tests/test_message_spec.py deleted file mode 100644 index f474def..0000000 --- a/ipython_kernel/tests/test_message_spec.py +++ /dev/null @@ -1,495 +0,0 @@ -"""Test suite for our zeromq-based message specification.""" - -# Copyright (c) IPython Development Team. -# Distributed under the terms of the Modified BSD License. - -import re -import sys -from distutils.version import LooseVersion as V -try: - from queue import Empty # Py 3 -except ImportError: - from Queue import Empty # Py 2 - -import nose.tools as nt - -from IPython.utils.traitlets import ( - HasTraits, TraitError, Bool, Unicode, Dict, Integer, List, Enum, -) -from IPython.utils.py3compat import string_types, iteritems - -from .utils import TIMEOUT, start_global_kernel, flush_channels, execute - -#----------------------------------------------------------------------------- -# Globals -#----------------------------------------------------------------------------- -KC = None - -def setup(): - global KC - KC = start_global_kernel() - -#----------------------------------------------------------------------------- -# Message Spec References -#----------------------------------------------------------------------------- - -class Reference(HasTraits): - - """ - Base class for message spec specification testing. - - This class is the core of the message specification test. The - idea is that child classes implement trait attributes for each - message keys, so that message keys can be tested against these - traits using :meth:`check` method. - - """ - - def check(self, d): - """validate a dict against our traits""" - for key in self.trait_names(): - nt.assert_in(key, d) - # FIXME: always allow None, probably not a good idea - if d[key] is None: - continue - try: - setattr(self, key, d[key]) - except TraitError as e: - assert False, str(e) - - -class Version(Unicode): - def __init__(self, *args, **kwargs): - self.min = kwargs.pop('min', None) - self.max = kwargs.pop('max', None) - kwargs['default_value'] = self.min - super(Version, self).__init__(*args, **kwargs) - - def validate(self, obj, value): - if self.min and V(value) < V(self.min): - raise TraitError("bad version: %s < %s" % (value, self.min)) - if self.max and (V(value) > V(self.max)): - raise TraitError("bad version: %s > %s" % (value, self.max)) - - -class RMessage(Reference): - msg_id = Unicode() - msg_type = Unicode() - header = Dict() - parent_header = Dict() - content = Dict() - - def check(self, d): - super(RMessage, self).check(d) - RHeader().check(self.header) - if self.parent_header: - RHeader().check(self.parent_header) - -class RHeader(Reference): - msg_id = Unicode() - msg_type = Unicode() - session = Unicode() - username = Unicode() - version = Version(min='5.0') - -mime_pat = re.compile(r'^[\w\-\+\.]+/[\w\-\+\.]+$') - -class MimeBundle(Reference): - metadata = Dict() - data = Dict() - def _data_changed(self, name, old, new): - for k,v in iteritems(new): - assert mime_pat.match(k) - nt.assert_is_instance(v, string_types) - -# shell replies - -class ExecuteReply(Reference): - execution_count = Integer() - status = Enum((u'ok', u'error'), default_value=u'ok') - - def check(self, d): - Reference.check(self, d) - if d['status'] == 'ok': - ExecuteReplyOkay().check(d) - elif d['status'] == 'error': - ExecuteReplyError().check(d) - - -class ExecuteReplyOkay(Reference): - payload = List(Dict) - user_expressions = Dict() - - -class ExecuteReplyError(Reference): - ename = Unicode() - evalue = Unicode() - traceback = List(Unicode) - - -class InspectReply(MimeBundle): - found = Bool() - - -class ArgSpec(Reference): - args = List(Unicode) - varargs = Unicode() - varkw = Unicode() - defaults = List() - - -class Status(Reference): - execution_state = Enum((u'busy', u'idle', u'starting'), default_value=u'busy') - - -class CompleteReply(Reference): - matches = List(Unicode) - cursor_start = Integer() - cursor_end = Integer() - status = Unicode() - -class LanguageInfo(Reference): - name = Unicode('python') - version = Unicode(sys.version.split()[0]) - -class KernelInfoReply(Reference): - protocol_version = Version(min='5.0') - implementation = Unicode('ipython') - implementation_version = Version(min='2.1') - language_info = Dict() - banner = Unicode() - - def check(self, d): - Reference.check(self, d) - LanguageInfo().check(d['language_info']) - - -class IsCompleteReply(Reference): - status = Enum((u'complete', u'incomplete', u'invalid', u'unknown'), default_value=u'complete') - - def check(self, d): - Reference.check(self, d) - if d['status'] == 'incomplete': - IsCompleteReplyIncomplete().check(d) - -class IsCompleteReplyIncomplete(Reference): - indent = Unicode() - - -# IOPub messages - -class ExecuteInput(Reference): - code = Unicode() - execution_count = Integer() - - -Error = ExecuteReplyError - - -class Stream(Reference): - name = Enum((u'stdout', u'stderr'), default_value=u'stdout') - text = Unicode() - - -class DisplayData(MimeBundle): - pass - - -class ExecuteResult(MimeBundle): - execution_count = Integer() - -class HistoryReply(Reference): - history = List(List()) - - -references = { - 'execute_reply' : ExecuteReply(), - 'inspect_reply' : InspectReply(), - 'status' : Status(), - 'complete_reply' : CompleteReply(), - 'kernel_info_reply': KernelInfoReply(), - 'is_complete_reply': IsCompleteReply(), - 'execute_input' : ExecuteInput(), - 'execute_result' : ExecuteResult(), - 'history_reply' : HistoryReply(), - 'error' : Error(), - 'stream' : Stream(), - 'display_data' : DisplayData(), - 'header' : RHeader(), -} -""" -Specifications of `content` part of the reply messages. -""" - - -def validate_message(msg, msg_type=None, parent=None): - """validate a message - - This is a generator, and must be iterated through to actually - trigger each test. - - If msg_type and/or parent are given, the msg_type and/or parent msg_id - are compared with the given values. - """ - RMessage().check(msg) - if msg_type: - nt.assert_equal(msg['msg_type'], msg_type) - if parent: - nt.assert_equal(msg['parent_header']['msg_id'], parent) - content = msg['content'] - ref = references[msg['msg_type']] - ref.check(content) - - -#----------------------------------------------------------------------------- -# Tests -#----------------------------------------------------------------------------- - -# Shell channel - -def test_execute(): - flush_channels() - - msg_id = KC.execute(code='x=1') - reply = KC.get_shell_msg(timeout=TIMEOUT) - validate_message(reply, 'execute_reply', msg_id) - - -def test_execute_silent(): - flush_channels() - msg_id, reply = execute(code='x=1', silent=True) - - # flush status=idle - status = KC.iopub_channel.get_msg(timeout=TIMEOUT) - validate_message(status, 'status', msg_id) - nt.assert_equal(status['content']['execution_state'], 'idle') - - nt.assert_raises(Empty, KC.iopub_channel.get_msg, timeout=0.1) - count = reply['execution_count'] - - msg_id, reply = execute(code='x=2', silent=True) - - # flush status=idle - status = KC.iopub_channel.get_msg(timeout=TIMEOUT) - validate_message(status, 'status', msg_id) - nt.assert_equal(status['content']['execution_state'], 'idle') - - nt.assert_raises(Empty, KC.iopub_channel.get_msg, timeout=0.1) - count_2 = reply['execution_count'] - nt.assert_equal(count_2, count) - - -def test_execute_error(): - flush_channels() - - msg_id, reply = execute(code='1/0') - nt.assert_equal(reply['status'], 'error') - nt.assert_equal(reply['ename'], 'ZeroDivisionError') - - error = KC.iopub_channel.get_msg(timeout=TIMEOUT) - validate_message(error, 'error', msg_id) - - -def test_execute_inc(): - """execute request should increment execution_count""" - flush_channels() - - msg_id, reply = execute(code='x=1') - count = reply['execution_count'] - - flush_channels() - - msg_id, reply = execute(code='x=2') - count_2 = reply['execution_count'] - nt.assert_equal(count_2, count+1) - -def test_execute_stop_on_error(): - """execute request should not abort execution queue with stop_on_error False""" - flush_channels() - - fail = '\n'.join([ - # sleep to ensure subsequent message is waiting in the queue to be aborted - 'import time', - 'time.sleep(0.5)', - 'raise ValueError', - ]) - KC.execute(code=fail) - msg_id = KC.execute(code='print("Hello")') - KC.get_shell_msg(timeout=TIMEOUT) - reply = KC.get_shell_msg(timeout=TIMEOUT) - nt.assert_equal(reply['content']['status'], 'aborted') - - flush_channels() - - KC.execute(code=fail, stop_on_error=False) - msg_id = KC.execute(code='print("Hello")') - KC.get_shell_msg(timeout=TIMEOUT) - reply = KC.get_shell_msg(timeout=TIMEOUT) - nt.assert_equal(reply['content']['status'], 'ok') - - -def test_user_expressions(): - flush_channels() - - msg_id, reply = execute(code='x=1', user_expressions=dict(foo='x+1')) - user_expressions = reply['user_expressions'] - nt.assert_equal(user_expressions, {u'foo': { - u'status': u'ok', - u'data': {u'text/plain': u'2'}, - u'metadata': {}, - }}) - - -def test_user_expressions_fail(): - flush_channels() - - msg_id, reply = execute(code='x=0', user_expressions=dict(foo='nosuchname')) - user_expressions = reply['user_expressions'] - foo = user_expressions['foo'] - nt.assert_equal(foo['status'], 'error') - nt.assert_equal(foo['ename'], 'NameError') - - -def test_oinfo(): - flush_channels() - - msg_id = KC.inspect('a') - reply = KC.get_shell_msg(timeout=TIMEOUT) - validate_message(reply, 'inspect_reply', msg_id) - - -def test_oinfo_found(): - flush_channels() - - msg_id, reply = execute(code='a=5') - - msg_id = KC.inspect('a') - reply = KC.get_shell_msg(timeout=TIMEOUT) - validate_message(reply, 'inspect_reply', msg_id) - content = reply['content'] - assert content['found'] - text = content['data']['text/plain'] - nt.assert_in('Type:', text) - nt.assert_in('Docstring:', text) - - -def test_oinfo_detail(): - flush_channels() - - msg_id, reply = execute(code='ip=get_ipython()') - - msg_id = KC.inspect('ip.object_inspect', cursor_pos=10, detail_level=1) - reply = KC.get_shell_msg(timeout=TIMEOUT) - validate_message(reply, 'inspect_reply', msg_id) - content = reply['content'] - assert content['found'] - text = content['data']['text/plain'] - nt.assert_in('Signature:', text) - nt.assert_in('Source:', text) - - -def test_oinfo_not_found(): - flush_channels() - - msg_id = KC.inspect('dne') - reply = KC.get_shell_msg(timeout=TIMEOUT) - validate_message(reply, 'inspect_reply', msg_id) - content = reply['content'] - nt.assert_false(content['found']) - - -def test_complete(): - flush_channels() - - msg_id, reply = execute(code="alpha = albert = 5") - - msg_id = KC.complete('al', 2) - reply = KC.get_shell_msg(timeout=TIMEOUT) - validate_message(reply, 'complete_reply', msg_id) - matches = reply['content']['matches'] - for name in ('alpha', 'albert'): - nt.assert_in(name, matches) - - -def test_kernel_info_request(): - flush_channels() - - msg_id = KC.kernel_info() - reply = KC.get_shell_msg(timeout=TIMEOUT) - validate_message(reply, 'kernel_info_reply', msg_id) - - -def test_single_payload(): - flush_channels() - msg_id, reply = execute(code="for i in range(3):\n"+ - " x=range?\n") - payload = reply['payload'] - next_input_pls = [pl for pl in payload if pl["source"] == "set_next_input"] - nt.assert_equal(len(next_input_pls), 1) - -def test_is_complete(): - flush_channels() - - msg_id = KC.is_complete("a = 1") - reply = KC.get_shell_msg(timeout=TIMEOUT) - validate_message(reply, 'is_complete_reply', msg_id) - -def test_history_range(): - flush_channels() - - msg_id_exec = KC.execute(code='x=1', store_history = True) - reply_exec = KC.get_shell_msg(timeout=TIMEOUT) - - msg_id = KC.history(hist_access_type = 'range', raw = True, output = True, start = 1, stop = 2, session = 0) - reply = KC.get_shell_msg(timeout=TIMEOUT) - validate_message(reply, 'history_reply', msg_id) - content = reply['content'] - nt.assert_equal(len(content['history']), 1) - -def test_history_tail(): - flush_channels() - - msg_id_exec = KC.execute(code='x=1', store_history = True) - reply_exec = KC.get_shell_msg(timeout=TIMEOUT) - - msg_id = KC.history(hist_access_type = 'tail', raw = True, output = True, n = 1, session = 0) - reply = KC.get_shell_msg(timeout=TIMEOUT) - validate_message(reply, 'history_reply', msg_id) - content = reply['content'] - nt.assert_equal(len(content['history']), 1) - -def test_history_search(): - flush_channels() - - msg_id_exec = KC.execute(code='x=1', store_history = True) - reply_exec = KC.get_shell_msg(timeout=TIMEOUT) - - msg_id = KC.history(hist_access_type = 'search', raw = True, output = True, n = 1, pattern = '*', session = 0) - reply = KC.get_shell_msg(timeout=TIMEOUT) - validate_message(reply, 'history_reply', msg_id) - content = reply['content'] - nt.assert_equal(len(content['history']), 1) - -# IOPub channel - - -def test_stream(): - flush_channels() - - msg_id, reply = execute("print('hi')") - - stdout = KC.iopub_channel.get_msg(timeout=TIMEOUT) - validate_message(stdout, 'stream', msg_id) - content = stdout['content'] - nt.assert_equal(content['text'], u'hi\n') - - -def test_display_data(): - flush_channels() - - msg_id, reply = execute("from IPython.core.display import display; display(1)") - - display = KC.iopub_channel.get_msg(timeout=TIMEOUT) - validate_message(display, 'display_data', parent=msg_id) - data = display['content']['data'] - nt.assert_equal(data['text/plain'], u'1') diff --git a/ipython_kernel/tests/test_pickleutil.py b/ipython_kernel/tests/test_pickleutil.py deleted file mode 100644 index 46ef56d..0000000 --- a/ipython_kernel/tests/test_pickleutil.py +++ /dev/null @@ -1,62 +0,0 @@ - -import pickle - -import nose.tools as nt -from ipython_kernel import codeutil -from ipython_kernel.pickleutil import can, uncan - -def interactive(f): - f.__module__ = '__main__' - return f - -def dumps(obj): - return pickle.dumps(can(obj)) - -def loads(obj): - return uncan(pickle.loads(obj)) - -def test_no_closure(): - @interactive - def foo(): - a = 5 - return a - - pfoo = dumps(foo) - bar = loads(pfoo) - nt.assert_equal(foo(), bar()) - -def test_generator_closure(): - # this only creates a closure on Python 3 - @interactive - def foo(): - i = 'i' - r = [ i for j in (1,2) ] - return r - - pfoo = dumps(foo) - bar = loads(pfoo) - nt.assert_equal(foo(), bar()) - -def test_nested_closure(): - @interactive - def foo(): - i = 'i' - def g(): - return i - return g() - - pfoo = dumps(foo) - bar = loads(pfoo) - nt.assert_equal(foo(), bar()) - -def test_closure(): - i = 'i' - @interactive - def foo(): - return i - - pfoo = dumps(foo) - bar = loads(pfoo) - nt.assert_equal(foo(), bar()) - - \ No newline at end of file diff --git a/ipython_kernel/tests/test_serialize.py b/ipython_kernel/tests/test_serialize.py deleted file mode 100644 index 1d9eaf8..0000000 --- a/ipython_kernel/tests/test_serialize.py +++ /dev/null @@ -1,208 +0,0 @@ -"""test serialization tools""" - -# Copyright (c) IPython Development Team. -# Distributed under the terms of the Modified BSD License. - -import pickle -from collections import namedtuple - -import nose.tools as nt - -# from unittest import TestCaes -from ipython_kernel.serialize import serialize_object, deserialize_object -from IPython.testing import decorators as dec -from ipython_kernel.pickleutil import CannedArray, CannedClass -from IPython.utils.py3compat import iteritems -from IPython.parallel import interactive - -#------------------------------------------------------------------------------- -# Globals and Utilities -#------------------------------------------------------------------------------- - -def roundtrip(obj): - """roundtrip an object through serialization""" - bufs = serialize_object(obj) - obj2, remainder = deserialize_object(bufs) - nt.assert_equals(remainder, []) - return obj2 - -class C(object): - """dummy class for """ - - def __init__(self, **kwargs): - for key,value in iteritems(kwargs): - setattr(self, key, value) - -SHAPES = ((100,), (1024,10), (10,8,6,5), (), (0,)) -DTYPES = ('uint8', 'float64', 'int32', [('g', 'float32')], '|S10') - -#------------------------------------------------------------------------------- -# Tests -#------------------------------------------------------------------------------- - -def new_array(shape, dtype): - import numpy - return numpy.random.random(shape).astype(dtype) - -def test_roundtrip_simple(): - for obj in [ - 'hello', - dict(a='b', b=10), - [1,2,'hi'], - (b'123', 'hello'), - ]: - obj2 = roundtrip(obj) - nt.assert_equal(obj, obj2) - -def test_roundtrip_nested(): - for obj in [ - dict(a=range(5), b={1:b'hello'}), - [range(5),[range(3),(1,[b'whoda'])]], - ]: - obj2 = roundtrip(obj) - nt.assert_equal(obj, obj2) - -def test_roundtrip_buffered(): - for obj in [ - dict(a=b"x"*1025), - b"hello"*500, - [b"hello"*501, 1,2,3] - ]: - bufs = serialize_object(obj) - nt.assert_equal(len(bufs), 2) - obj2, remainder = deserialize_object(bufs) - nt.assert_equal(remainder, []) - nt.assert_equal(obj, obj2) - -@dec.skip_without('numpy') -def test_numpy(): - import numpy - from numpy.testing.utils import assert_array_equal - for shape in SHAPES: - for dtype in DTYPES: - A = new_array(shape, dtype=dtype) - bufs = serialize_object(A) - B, r = deserialize_object(bufs) - nt.assert_equal(r, []) - nt.assert_equal(A.shape, B.shape) - nt.assert_equal(A.dtype, B.dtype) - assert_array_equal(A,B) - -@dec.skip_without('numpy') -def test_recarray(): - import numpy - from numpy.testing.utils import assert_array_equal - for shape in SHAPES: - for dtype in [ - [('f', float), ('s', '|S10')], - [('n', int), ('s', '|S1'), ('u', 'uint32')], - ]: - A = new_array(shape, dtype=dtype) - - bufs = serialize_object(A) - B, r = deserialize_object(bufs) - nt.assert_equal(r, []) - nt.assert_equal(A.shape, B.shape) - nt.assert_equal(A.dtype, B.dtype) - assert_array_equal(A,B) - -@dec.skip_without('numpy') -def test_numpy_in_seq(): - import numpy - from numpy.testing.utils import assert_array_equal - for shape in SHAPES: - for dtype in DTYPES: - A = new_array(shape, dtype=dtype) - bufs = serialize_object((A,1,2,b'hello')) - canned = pickle.loads(bufs[0]) - nt.assert_is_instance(canned[0], CannedArray) - tup, r = deserialize_object(bufs) - B = tup[0] - nt.assert_equal(r, []) - nt.assert_equal(A.shape, B.shape) - nt.assert_equal(A.dtype, B.dtype) - assert_array_equal(A,B) - -@dec.skip_without('numpy') -def test_numpy_in_dict(): - import numpy - from numpy.testing.utils import assert_array_equal - for shape in SHAPES: - for dtype in DTYPES: - A = new_array(shape, dtype=dtype) - bufs = serialize_object(dict(a=A,b=1,c=range(20))) - canned = pickle.loads(bufs[0]) - nt.assert_is_instance(canned['a'], CannedArray) - d, r = deserialize_object(bufs) - B = d['a'] - nt.assert_equal(r, []) - nt.assert_equal(A.shape, B.shape) - nt.assert_equal(A.dtype, B.dtype) - assert_array_equal(A,B) - -def test_class(): - @interactive - class C(object): - a=5 - bufs = serialize_object(dict(C=C)) - canned = pickle.loads(bufs[0]) - nt.assert_is_instance(canned['C'], CannedClass) - d, r = deserialize_object(bufs) - C2 = d['C'] - nt.assert_equal(C2.a, C.a) - -def test_class_oldstyle(): - @interactive - class C: - a=5 - - bufs = serialize_object(dict(C=C)) - canned = pickle.loads(bufs[0]) - nt.assert_is_instance(canned['C'], CannedClass) - d, r = deserialize_object(bufs) - C2 = d['C'] - nt.assert_equal(C2.a, C.a) - -def test_tuple(): - tup = (lambda x:x, 1) - bufs = serialize_object(tup) - canned = pickle.loads(bufs[0]) - nt.assert_is_instance(canned, tuple) - t2, r = deserialize_object(bufs) - nt.assert_equal(t2[0](t2[1]), tup[0](tup[1])) - -point = namedtuple('point', 'x y') - -def test_namedtuple(): - p = point(1,2) - bufs = serialize_object(p) - canned = pickle.loads(bufs[0]) - nt.assert_is_instance(canned, point) - p2, r = deserialize_object(bufs, globals()) - nt.assert_equal(p2.x, p.x) - nt.assert_equal(p2.y, p.y) - -def test_list(): - lis = [lambda x:x, 1] - bufs = serialize_object(lis) - canned = pickle.loads(bufs[0]) - nt.assert_is_instance(canned, list) - l2, r = deserialize_object(bufs) - nt.assert_equal(l2[0](l2[1]), lis[0](lis[1])) - -def test_class_inheritance(): - @interactive - class C(object): - a=5 - - @interactive - class D(C): - b=10 - - bufs = serialize_object(dict(D=D)) - canned = pickle.loads(bufs[0]) - nt.assert_is_instance(canned['D'], CannedClass) - d, r = deserialize_object(bufs) - D2 = d['D'] - nt.assert_equal(D2.a, D.a) - nt.assert_equal(D2.b, D.b) diff --git a/ipython_kernel/tests/test_start_kernel.py b/ipython_kernel/tests/test_start_kernel.py deleted file mode 100644 index 3ac53dd..0000000 --- a/ipython_kernel/tests/test_start_kernel.py +++ /dev/null @@ -1,48 +0,0 @@ -import nose.tools as nt - -from .test_embed_kernel import setup, teardown, setup_kernel - -TIMEOUT = 15 - -def test_ipython_start_kernel_userns(): - cmd = ('from IPython import start_kernel\n' - 'ns = {"tre": 123}\n' - 'start_kernel(user_ns=ns)') - - with setup_kernel(cmd) as client: - msg_id = client.inspect('tre') - msg = client.get_shell_msg(block=True, timeout=TIMEOUT) - content = msg['content'] - assert content['found'] - text = content['data']['text/plain'] - nt.assert_in(u'123', text) - - # user_module should be an instance of DummyMod - msg_id = client.execute("usermod = get_ipython().user_module") - msg = client.get_shell_msg(block=True, timeout=TIMEOUT) - content = msg['content'] - nt.assert_equal(content['status'], u'ok') - msg_id = client.inspect('usermod') - msg = client.get_shell_msg(block=True, timeout=TIMEOUT) - content = msg['content'] - assert content['found'] - text = content['data']['text/plain'] - nt.assert_in(u'DummyMod', text) - -def test_ipython_start_kernel_no_userns(): - # Issue #4188 - user_ns should be passed to shell as None, not {} - cmd = ('from IPython import start_kernel\n' - 'start_kernel()') - - with setup_kernel(cmd) as client: - # user_module should not be an instance of DummyMod - msg_id = client.execute("usermod = get_ipython().user_module") - msg = client.get_shell_msg(block=True, timeout=TIMEOUT) - content = msg['content'] - nt.assert_equal(content['status'], u'ok') - msg_id = client.inspect('usermod') - msg = client.get_shell_msg(block=True, timeout=TIMEOUT) - content = msg['content'] - assert content['found'] - text = content['data']['text/plain'] - nt.assert_not_in(u'DummyMod', text) diff --git a/ipython_kernel/tests/utils.py b/ipython_kernel/tests/utils.py deleted file mode 100644 index cfa443c..0000000 --- a/ipython_kernel/tests/utils.py +++ /dev/null @@ -1,163 +0,0 @@ -"""utilities for testing IPython kernels""" - -# Copyright (c) IPython Development Team. -# Distributed under the terms of the Modified BSD License. - -import atexit -import os - -from contextlib import contextmanager -from subprocess import PIPE, STDOUT -try: - from queue import Empty # Py 3 -except ImportError: - from Queue import Empty # Py 2 - -import nose -import nose.tools as nt - -from jupyter_client import manager - -#------------------------------------------------------------------------------- -# Globals -#------------------------------------------------------------------------------- - -STARTUP_TIMEOUT = 60 -TIMEOUT = 15 - -KM = None -KC = None - -#------------------------------------------------------------------------------- -# code -#------------------------------------------------------------------------------- -def start_new_kernel(**kwargs): - """start a new kernel, and return its Manager and Client - - Integrates with our output capturing for tests. - """ - kwargs.update(dict(stdout=nose.iptest_stdstreams_fileno(), stderr=STDOUT)) - return manager.start_new_kernel(startup_timeout=STARTUP_TIMEOUT, **kwargs) - -def flush_channels(kc=None): - """flush any messages waiting on the queue""" - from .test_message_spec import validate_message - - if kc is None: - kc = KC - for channel in (kc.shell_channel, kc.iopub_channel): - while True: - try: - msg = channel.get_msg(block=True, timeout=0.1) - except Empty: - break - else: - validate_message(msg) - - -def execute(code='', kc=None, **kwargs): - """wrapper for doing common steps for validating an execution request""" - from .test_message_spec import validate_message - if kc is None: - kc = KC - msg_id = kc.execute(code=code, **kwargs) - reply = kc.get_shell_msg(timeout=TIMEOUT) - validate_message(reply, 'execute_reply', msg_id) - busy = kc.get_iopub_msg(timeout=TIMEOUT) - validate_message(busy, 'status', msg_id) - nt.assert_equal(busy['content']['execution_state'], 'busy') - - if not kwargs.get('silent'): - execute_input = kc.get_iopub_msg(timeout=TIMEOUT) - validate_message(execute_input, 'execute_input', msg_id) - nt.assert_equal(execute_input['content']['code'], code) - - return msg_id, reply['content'] - -def start_global_kernel(): - """start the global kernel (if it isn't running) and return its client""" - global KM, KC - if KM is None: - KM, KC = start_new_kernel() - atexit.register(stop_global_kernel) - else: - flush_channels(KC) - return KC - -@contextmanager -def kernel(): - """Context manager for the global kernel instance - - Should be used for most kernel tests - - Returns - ------- - kernel_client: connected KernelClient instance - """ - yield start_global_kernel() - -def uses_kernel(test_f): - """Decorator for tests that use the global kernel""" - def wrapped_test(): - with kernel() as kc: - test_f(kc) - wrapped_test.__doc__ = test_f.__doc__ - wrapped_test.__name__ = test_f.__name__ - return wrapped_test - -def stop_global_kernel(): - """Stop the global shared kernel instance, if it exists""" - global KM, KC - KC.stop_channels() - KC = None - if KM is None: - return - KM.shutdown_kernel(now=True) - KM = None - -def new_kernel(argv=None): - """Context manager for a new kernel in a subprocess - - Should only be used for tests where the kernel must not be re-used. - - Returns - ------- - kernel_client: connected KernelClient instance - """ - kwargs = dict( - stdout=nose.iptest_stdstreams_fileno(), stderr=STDOUT, - startup_timeout=STARTUP_TIMEOUT) - if argv is not None: - kwargs['extra_arguments'] = argv - return manager.run_kernel(**kwargs) - -def assemble_output(iopub): - """assemble stdout/err from an execution""" - stdout = '' - stderr = '' - while True: - msg = iopub.get_msg(block=True, timeout=1) - msg_type = msg['msg_type'] - content = msg['content'] - if msg_type == 'status' and content['execution_state'] == 'idle': - # idle message signals end of output - break - elif msg['msg_type'] == 'stream': - if content['name'] == 'stdout': - stdout += content['text'] - elif content['name'] == 'stderr': - stderr += content['text'] - else: - raise KeyError("bad stream: %r" % content['name']) - else: - # other output, ignored - pass - return stdout, stderr - -def wait_for_idle(kc): - while True: - msg = kc.iopub_channel.get_msg(block=True, timeout=1) - msg_type = msg['msg_type'] - content = msg['content'] - if msg_type == 'status' and content['execution_state'] == 'idle': - break diff --git a/ipython_kernel/zmqshell.py b/ipython_kernel/zmqshell.py deleted file mode 100644 index 34b1735..0000000 --- a/ipython_kernel/zmqshell.py +++ /dev/null @@ -1,484 +0,0 @@ -"""A ZMQ-based subclass of InteractiveShell. - -This code is meant to ease the refactoring of the base InteractiveShell into -something with a cleaner architecture for 2-process use, without actually -breaking InteractiveShell itself. So we're doing something a bit ugly, where -we subclass and override what we want to fix. Once this is working well, we -can go back to the base class and refactor the code for a cleaner inheritance -implementation that doesn't rely on so much monkeypatching. - -But this lets us maintain a fully working IPython as we develop the new -machinery. This should thus be thought of as scaffolding. -""" - -# Copyright (c) IPython Development Team. -# Distributed under the terms of the Modified BSD License. - -from __future__ import print_function - -import os -import sys -import time - -from zmq.eventloop import ioloop - -from IPython.core.interactiveshell import ( - InteractiveShell, InteractiveShellABC -) -from IPython.core import page -from IPython.core.autocall import ZMQExitAutocall -from IPython.core.displaypub import DisplayPublisher -from IPython.core.error import UsageError -from IPython.core.magics import MacroToEdit, CodeMagics -from IPython.core.magic import magics_class, line_magic, Magics -from IPython.core import payloadpage -from IPython.core.usage import default_gui_banner -from IPython.display import display, Javascript -from ipython_kernel.inprocess.socket import SocketABC -from ipython_kernel import ( - get_connection_file, get_connection_info, connect_qtconsole -) -from IPython.utils import openpy -from jupyter_client.jsonutil import json_clean, encode_images -from IPython.utils.process import arg_split -from IPython.utils import py3compat -from IPython.utils.py3compat import unicode_type -from IPython.utils.traitlets import Instance, Type, Dict, CBool, CBytes, Any -from IPython.utils.warn import error -from ipython_kernel.displayhook import ZMQShellDisplayHook -from ipython_kernel.datapub import ZMQDataPublisher -from ipython_kernel.session import extract_header -from .session import Session - -#----------------------------------------------------------------------------- -# Functions and classes -#----------------------------------------------------------------------------- - -class ZMQDisplayPublisher(DisplayPublisher): - """A display publisher that publishes data using a ZeroMQ PUB socket.""" - - session = Instance(Session, allow_none=True) - pub_socket = Instance(SocketABC, allow_none=True) - parent_header = Dict({}) - topic = CBytes(b'display_data') - - def set_parent(self, parent): - """Set the parent for outbound messages.""" - self.parent_header = extract_header(parent) - - def _flush_streams(self): - """flush IO Streams prior to display""" - sys.stdout.flush() - sys.stderr.flush() - - def publish(self, data, metadata=None, source=None): - self._flush_streams() - if metadata is None: - metadata = {} - self._validate_data(data, metadata) - content = {} - content['data'] = encode_images(data) - content['metadata'] = metadata - self.session.send( - self.pub_socket, u'display_data', json_clean(content), - parent=self.parent_header, ident=self.topic, - ) - - def clear_output(self, wait=False): - content = dict(wait=wait) - self._flush_streams() - self.session.send( - self.pub_socket, u'clear_output', content, - parent=self.parent_header, ident=self.topic, - ) - -@magics_class -class KernelMagics(Magics): - #------------------------------------------------------------------------ - # Magic overrides - #------------------------------------------------------------------------ - # Once the base class stops inheriting from magic, this code needs to be - # moved into a separate machinery as well. For now, at least isolate here - # the magics which this class needs to implement differently from the base - # class, or that are unique to it. - - _find_edit_target = CodeMagics._find_edit_target - - @line_magic - def edit(self, parameter_s='', last_call=['','']): - """Bring up an editor and execute the resulting code. - - Usage: - %edit [options] [args] - - %edit runs an external text editor. You will need to set the command for - this editor via the ``TerminalInteractiveShell.editor`` option in your - configuration file before it will work. - - This command allows you to conveniently edit multi-line code right in - your IPython session. - - If called without arguments, %edit opens up an empty editor with a - temporary file and will execute the contents of this file when you - close it (don't forget to save it!). - - Options: - - -n - Open the editor at a specified line number. By default, the IPython - editor hook uses the unix syntax 'editor +N filename', but you can - configure this by providing your own modified hook if your favorite - editor supports line-number specifications with a different syntax. - - -p - Call the editor with the same data as the previous time it was used, - regardless of how long ago (in your current session) it was. - - -r - Use 'raw' input. This option only applies to input taken from the - user's history. By default, the 'processed' history is used, so that - magics are loaded in their transformed version to valid Python. If - this option is given, the raw input as typed as the command line is - used instead. When you exit the editor, it will be executed by - IPython's own processor. - - Arguments: - - If arguments are given, the following possibilites exist: - - - The arguments are numbers or pairs of colon-separated numbers (like - 1 4:8 9). These are interpreted as lines of previous input to be - loaded into the editor. The syntax is the same of the %macro command. - - - If the argument doesn't start with a number, it is evaluated as a - variable and its contents loaded into the editor. You can thus edit - any string which contains python code (including the result of - previous edits). - - - If the argument is the name of an object (other than a string), - IPython will try to locate the file where it was defined and open the - editor at the point where it is defined. You can use ``%edit function`` - to load an editor exactly at the point where 'function' is defined, - edit it and have the file be executed automatically. - - If the object is a macro (see %macro for details), this opens up your - specified editor with a temporary file containing the macro's data. - Upon exit, the macro is reloaded with the contents of the file. - - Note: opening at an exact line is only supported under Unix, and some - editors (like kedit and gedit up to Gnome 2.8) do not understand the - '+NUMBER' parameter necessary for this feature. Good editors like - (X)Emacs, vi, jed, pico and joe all do. - - - If the argument is not found as a variable, IPython will look for a - file with that name (adding .py if necessary) and load it into the - editor. It will execute its contents with execfile() when you exit, - loading any code in the file into your interactive namespace. - - Unlike in the terminal, this is designed to use a GUI editor, and we do - not know when it has closed. So the file you edit will not be - automatically executed or printed. - - Note that %edit is also available through the alias %ed. - """ - - opts,args = self.parse_options(parameter_s,'prn:') - - try: - filename, lineno, _ = CodeMagics._find_edit_target(self.shell, args, opts, last_call) - except MacroToEdit as e: - # TODO: Implement macro editing over 2 processes. - print("Macro editing not yet implemented in 2-process model.") - return - - # Make sure we send to the client an absolute path, in case the working - # directory of client and kernel don't match - filename = os.path.abspath(filename) - - payload = { - 'source' : 'edit_magic', - 'filename' : filename, - 'line_number' : lineno - } - self.shell.payload_manager.write_payload(payload) - - # A few magics that are adapted to the specifics of using pexpect and a - # remote terminal - - @line_magic - def clear(self, arg_s): - """Clear the terminal.""" - if os.name == 'posix': - self.shell.system("clear") - else: - self.shell.system("cls") - - if os.name == 'nt': - # This is the usual name in windows - cls = line_magic('cls')(clear) - - # Terminal pagers won't work over pexpect, but we do have our own pager - - @line_magic - def less(self, arg_s): - """Show a file through the pager. - - Files ending in .py are syntax-highlighted.""" - if not arg_s: - raise UsageError('Missing filename.') - - if arg_s.endswith('.py'): - cont = self.shell.pycolorize(openpy.read_py_file(arg_s, skip_encoding_cookie=False)) - else: - cont = open(arg_s).read() - page.page(cont) - - more = line_magic('more')(less) - - # Man calls a pager, so we also need to redefine it - if os.name == 'posix': - @line_magic - def man(self, arg_s): - """Find the man page for the given command and display in pager.""" - page.page(self.shell.getoutput('man %s | col -b' % arg_s, - split=False)) - - @line_magic - def connect_info(self, arg_s): - """Print information for connecting other clients to this kernel - - It will print the contents of this session's connection file, as well as - shortcuts for local clients. - - In the simplest case, when called from the most recently launched kernel, - secondary clients can be connected, simply with: - - $> ipython --existing - - """ - - from IPython.core.application import BaseIPythonApplication as BaseIPApp - - if BaseIPApp.initialized(): - app = BaseIPApp.instance() - security_dir = app.profile_dir.security_dir - profile = app.profile - else: - profile = 'default' - security_dir = '' - - try: - connection_file = get_connection_file() - info = get_connection_info(unpack=False) - except Exception as e: - error("Could not get connection info: %r" % e) - return - - # add profile flag for non-default profile - profile_flag = "--profile %s" % profile if profile != 'default' else "" - - # if it's in the security dir, truncate to basename - if security_dir == os.path.dirname(connection_file): - connection_file = os.path.basename(connection_file) - - - print (info + '\n') - print ("Paste the above JSON into a file, and connect with:\n" - " $> ipython --existing \n" - "or, if you are local, you can connect with just:\n" - " $> ipython --existing {0} {1}\n" - "or even just:\n" - " $> ipython --existing {1}\n" - "if this is the most recent IPython session you have started.".format( - connection_file, profile_flag - ) - ) - - @line_magic - def qtconsole(self, arg_s): - """Open a qtconsole connected to this kernel. - - Useful for connecting a qtconsole to running notebooks, for better - debugging. - """ - - # %qtconsole should imply bind_kernel for engines: - try: - from IPython.parallel import bind_kernel - except ImportError: - # technically possible, because parallel has higher pyzmq min-version - pass - else: - bind_kernel() - - try: - p = connect_qtconsole(argv=arg_split(arg_s, os.name=='posix')) - except Exception as e: - error("Could not start qtconsole: %r" % e) - return - - @line_magic - def autosave(self, arg_s): - """Set the autosave interval in the notebook (in seconds). - - The default value is 120, or two minutes. - ``%autosave 0`` will disable autosave. - - This magic only has an effect when called from the notebook interface. - It has no effect when called in a startup file. - """ - - try: - interval = int(arg_s) - except ValueError: - raise UsageError("%%autosave requires an integer, got %r" % arg_s) - - # javascript wants milliseconds - milliseconds = 1000 * interval - display(Javascript("IPython.notebook.set_autosave_interval(%i)" % milliseconds), - include=['application/javascript'] - ) - if interval: - print("Autosaving every %i seconds" % interval) - else: - print("Autosave disabled") - - -class ZMQInteractiveShell(InteractiveShell): - """A subclass of InteractiveShell for ZMQ.""" - - displayhook_class = Type(ZMQShellDisplayHook) - display_pub_class = Type(ZMQDisplayPublisher) - data_pub_class = Type(ZMQDataPublisher) - kernel = Any() - parent_header = Any() - - def _banner1_default(self): - return default_gui_banner - - # Override the traitlet in the parent class, because there's no point using - # readline for the kernel. Can be removed when the readline code is moved - # to the terminal frontend. - colors_force = CBool(True) - readline_use = CBool(False) - # autoindent has no meaning in a zmqshell, and attempting to enable it - # will print a warning in the absence of readline. - autoindent = CBool(False) - - exiter = Instance(ZMQExitAutocall) - def _exiter_default(self): - return ZMQExitAutocall(self) - - def _exit_now_changed(self, name, old, new): - """stop eventloop when exit_now fires""" - if new: - loop = ioloop.IOLoop.instance() - loop.add_timeout(time.time()+0.1, loop.stop) - - keepkernel_on_exit = None - - # Over ZeroMQ, GUI control isn't done with PyOS_InputHook as there is no - # interactive input being read; we provide event loop support in ipkernel - @staticmethod - def enable_gui(gui): - from .eventloops import enable_gui as real_enable_gui - try: - real_enable_gui(gui) - except ValueError as e: - raise UsageError("%s" % e) - - def init_environment(self): - """Configure the user's environment.""" - env = os.environ - # These two ensure 'ls' produces nice coloring on BSD-derived systems - env['TERM'] = 'xterm-color' - env['CLICOLOR'] = '1' - # Since normal pagers don't work at all (over pexpect we don't have - # single-key control of the subprocess), try to disable paging in - # subprocesses as much as possible. - env['PAGER'] = 'cat' - env['GIT_PAGER'] = 'cat' - - def init_hooks(self): - super(ZMQInteractiveShell, self).init_hooks() - self.set_hook('show_in_pager', page.as_hook(payloadpage.page), 99) - - def ask_exit(self): - """Engage the exit actions.""" - self.exit_now = (not self.keepkernel_on_exit) - payload = dict( - source='ask_exit', - keepkernel=self.keepkernel_on_exit, - ) - self.payload_manager.write_payload(payload) - - def _showtraceback(self, etype, evalue, stb): - # try to preserve ordering of tracebacks and print statements - sys.stdout.flush() - sys.stderr.flush() - - exc_content = { - u'traceback' : stb, - u'ename' : unicode_type(etype.__name__), - u'evalue' : py3compat.safe_unicode(evalue), - } - - dh = self.displayhook - # Send exception info over pub socket for other clients than the caller - # to pick up - topic = None - if dh.topic: - topic = dh.topic.replace(b'execute_result', b'error') - - exc_msg = dh.session.send(dh.pub_socket, u'error', json_clean(exc_content), dh.parent_header, ident=topic) - - # FIXME - Hack: store exception info in shell object. Right now, the - # caller is reading this info after the fact, we need to fix this logic - # to remove this hack. Even uglier, we need to store the error status - # here, because in the main loop, the logic that sets it is being - # skipped because runlines swallows the exceptions. - exc_content[u'status'] = u'error' - self._reply_content = exc_content - # /FIXME - - return exc_content - - def set_next_input(self, text, replace=False): - """Send the specified text to the frontend to be presented at the next - input cell.""" - payload = dict( - source='set_next_input', - text=text, - replace=replace, - ) - self.payload_manager.write_payload(payload) - - def set_parent(self, parent): - """Set the parent header for associating output with its triggering input""" - self.parent_header = parent - self.displayhook.set_parent(parent) - self.display_pub.set_parent(parent) - self.data_pub.set_parent(parent) - try: - sys.stdout.set_parent(parent) - except AttributeError: - pass - try: - sys.stderr.set_parent(parent) - except AttributeError: - pass - - def get_parent(self): - return self.parent_header - - #------------------------------------------------------------------------- - # Things related to magics - #------------------------------------------------------------------------- - - def init_magics(self): - super(ZMQInteractiveShell, self).init_magics() - self.register_magics(KernelMagics) - self.magics_manager.register_alias('ed', 'edit') - - -InteractiveShellABC.register(ZMQInteractiveShell)