commit python3-jupyter_ipykernel for openSUSE:Factory
Hello community, here is the log from the commit of package python3-jupyter_ipykernel for openSUSE:Factory checked in at 2016-03-07 13:28:25 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/python3-jupyter_ipykernel (Old) and /work/SRC/openSUSE:Factory/.python3-jupyter_ipykernel.new (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Package is "python3-jupyter_ipykernel" Changes: -------- --- /work/SRC/openSUSE:Factory/python3-jupyter_ipykernel/python3-jupyter_ipykernel.changes 2016-01-09 23:13:52.000000000 +0100 +++ /work/SRC/openSUSE:Factory/.python3-jupyter_ipykernel.new/python3-jupyter_ipykernel.changes 2016-03-07 13:29:55.000000000 +0100 @@ -1,0 +2,20 @@ +Sat Feb 27 16:39:07 UTC 2016 - arun@gmx.de + +- update to version 4.3.1: + * Fix Windows Python 3.5 incompatibility caused by faulthandler + patch in 4.3 + +- changes from version 4.3.0: + * Publish all IO in a thread, via :class:`IOPubThread`. This solves + the problem of requiring :meth:`sys.stdout.flush` to be called in + the notebook to produce output promptly during long-running cells. + * Remove refrences to outdated IPython guiref in kernel banner. + * Patch faulthandler to use sys.__stderr__ instead of forwarded + sys.stderr, which has no fileno when forwarded. + * Deprecate some vestiges of the Big Split: - + :func:`ipykernel.find_connection_file` is deprecated. Use + :func:`jupyter_client.find_connection_file` instead. - Various + pieces of code specific to IPython parallel are deprecated in + ipykernel and moved to ipyparallel. + +------------------------------------------------------------------- Old: ---- ipykernel-4.2.2.tar.gz New: ---- ipykernel-4.3.1.tar.gz ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ python3-jupyter_ipykernel.spec ++++++ --- /var/tmp/diff_new_pack.xRhaZg/_old 2016-03-07 13:29:55.000000000 +0100 +++ /var/tmp/diff_new_pack.xRhaZg/_new 2016-03-07 13:29:55.000000000 +0100 @@ -17,7 +17,7 @@ Name: python3-jupyter_ipykernel -Version: 4.2.2 +Version: 4.3.1 Release: 0 Summary: IPython Kernel for Jupyter License: BSD-3-Clause ++++++ ipykernel-4.2.2.tar.gz -> ipykernel-4.3.1.tar.gz ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ipykernel-4.2.2/PKG-INFO new/ipykernel-4.3.1/PKG-INFO --- old/ipykernel-4.2.2/PKG-INFO 2016-01-02 14:18:13.000000000 +0100 +++ new/ipykernel-4.3.1/PKG-INFO 2016-02-26 14:37:08.000000000 +0100 @@ -1,6 +1,6 @@ Metadata-Version: 1.1 Name: ipykernel -Version: 4.2.2 +Version: 4.3.1 Summary: IPython Kernel for Jupyter Home-page: http://ipython.org Author: IPython Development Team diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ipykernel-4.2.2/docs/changelog.rst new/ipykernel-4.3.1/docs/changelog.rst --- old/ipykernel-4.2.2/docs/changelog.rst 2016-01-02 14:17:21.000000000 +0100 +++ new/ipykernel-4.3.1/docs/changelog.rst 2016-02-26 14:25:25.000000000 +0100 @@ -1,6 +1,30 @@ Changes in IPython kernel ========================= +4.3 +--- + +4.3.1 +***** + +- Fix Windows Python 3.5 incompatibility caused by faulthandler patch in 4.3 + +4.3.0 +***** + +`4.3.0 on GitHub https://github.com/ipython/ipykernel/milestones/4.3`__ + +- Publish all IO in a thread, via :class:`IOPubThread`. + This solves the problem of requiring :meth:`sys.stdout.flush` to be called in the notebook to produce output promptly during long-running cells. +- Remove refrences to outdated IPython guiref in kernel banner. +- Patch faulthandler to use ``sys.__stderr__`` instead of forwarded ``sys.stderr``, + which has no fileno when forwarded. +- Deprecate some vestiges of the Big Split: + - :func:`ipykernel.find_connection_file` is deprecated. Use :func:`jupyter_client.find_connection_file` instead. + - Various pieces of code specific to IPython parallel are deprecated in ipykernel + and moved to ipyparallel. + + 4.2 --- diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ipykernel-4.2.2/ipykernel/_version.py new/ipykernel-4.3.1/ipykernel/_version.py --- old/ipykernel-4.2.2/ipykernel/_version.py 2016-01-02 14:17:50.000000000 +0100 +++ new/ipykernel-4.3.1/ipykernel/_version.py 2016-02-26 14:32:27.000000000 +0100 @@ -1,4 +1,4 @@ -version_info = (4, 2, 2) +version_info = (4, 3, 1) __version__ = '.'.join(map(str, version_info)) kernel_protocol_version_info = (5, 0) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ipykernel-4.2.2/ipykernel/codeutil.py new/ipykernel-4.3.1/ipykernel/codeutil.py --- old/ipykernel-4.2.2/ipykernel/codeutil.py 2015-11-13 10:25:37.000000000 +0100 +++ new/ipykernel-4.3.1/ipykernel/codeutil.py 2016-02-22 13:54:46.000000000 +0100 @@ -13,6 +13,9 @@ # Copyright (c) IPython Development Team. # Distributed under the terms of the Modified BSD License. +import warnings +warnings.warn("ipykernel.codeutil is deprecated. It has moved to ipyparallel.serialize", DeprecationWarning) + import sys import types try: diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ipykernel-4.2.2/ipykernel/connect.py new/ipykernel-4.3.1/ipykernel/connect.py --- old/ipykernel-4.2.2/ipykernel/connect.py 2015-06-15 22:26:41.000000000 +0200 +++ new/ipykernel-4.3.1/ipykernel/connect.py 2016-02-25 15:47:39.000000000 +0100 @@ -8,6 +8,7 @@ import json import sys from subprocess import Popen, PIPE +import warnings from IPython.core.profiledir import ProfileDir from IPython.paths import get_ipython_dir @@ -37,18 +38,9 @@ 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. + """DEPRECATED: find a connection file, and return its absolute path. + + THIS FUNCION IS DEPRECATED. Use juptyer_client.find_connection_file instead. Parameters ---------- @@ -62,6 +54,10 @@ ------- str : The absolute path of the connection file. """ + + import warnings + warnings.warn("""ipykernel.find_connection_file is deprecated, use jupyter_client.find_connection_file""", + DeprecationWarning, stacklevel=2) from IPython.core.application import BaseIPythonApplication as IPApp try: # quick check for absolute path, before going through logic @@ -85,6 +81,28 @@ return jupyter_client.find_connection_file(filename, path=['.', security_dir]) +def _find_connection_file(connection_file, profile=None): + """Return the absolute path for a connection file + + - If nothing specified, return current Kernel's connection file + - If profile specified, show deprecation warning about finding connection files in profiles + - Otherwise, call jupyter_client.find_connection_file + """ + if connection_file is None: + # get connection file from current kernel + return get_connection_file() + else: + # connection file specified, allow shortnames: + if profile is not None: + warnings.warn( + "Finding connection file by profile is deprecated.", + DeprecationWarning, stacklevel=3, + ) + return find_connection_file(connection_file, profile=profile) + else: + return jupyter_client.find_connection_file(connection_file) + + def get_connection_info(connection_file=None, unpack=False, profile=None): """Return the connection information for the current Kernel. @@ -100,22 +118,14 @@ 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'. - + profile : DEPRECATED 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) + cf = _find_connection_file(connection_file, profile) with open(cf) as f: info = f.read() @@ -144,10 +154,7 @@ 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'. - + profile : DEPRECATED Returns ------- @@ -155,11 +162,7 @@ """ 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) + cf = _find_connection_file(connection_file, profile) cmd = ';'.join([ "from IPython.qt.console import qtconsoleapp", diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ipykernel-4.2.2/ipykernel/datapub.py new/ipykernel-4.3.1/ipykernel/datapub.py --- old/ipykernel-4.2.2/ipykernel/datapub.py 2015-11-13 10:25:37.000000000 +0100 +++ new/ipykernel-4.3.1/ipykernel/datapub.py 2016-02-22 13:54:46.000000000 +0100 @@ -1,6 +1,9 @@ """Publishing native (typically pickled) objects. """ +import warnings +warnings.warn("ipykernel.datapub is deprecated. It has moved to ipyparallel.datapub", DeprecationWarning) + # Copyright (c) IPython Development Team. # Distributed under the terms of the Modified BSD License. @@ -53,5 +56,7 @@ data : dict The data to be published. Think of it as a namespace. """ + warnings.warn("ipykernel.datapub is deprecated. It has moved to ipyparallel.datapub", DeprecationWarning) + from ipykernel.zmqshell import ZMQInteractiveShell ZMQInteractiveShell.instance().data_pub.publish_data(data) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ipykernel-4.2.2/ipykernel/inprocess/ipkernel.py new/ipykernel-4.3.1/ipykernel/inprocess/ipkernel.py --- old/ipykernel-4.2.2/ipykernel/inprocess/ipkernel.py 2016-01-02 14:15:12.000000000 +0100 +++ new/ipykernel-4.3.1/ipykernel/inprocess/ipkernel.py 2016-02-22 13:54:46.000000000 +0100 @@ -14,6 +14,7 @@ from ipykernel.zmqshell import ZMQInteractiveShell from .socket import DummySocket +from ..iostream import OutStream, BackgroundSocket, IOPubThread #----------------------------------------------------------------------------- # Main kernel class @@ -49,13 +50,23 @@ shell_class = Type(allow_none=True) shell_streams = List() control_stream = Any() - iopub_socket = Instance(DummySocket, ()) + _underlying_iopub_socket = Instance(DummySocket, ()) + iopub_thread = Instance(IOPubThread) + def _iopub_thread_default(self): + thread = IOPubThread(self._underlying_iopub_socket) + thread.start() + return thread + + iopub_socket = Instance(BackgroundSocket) + def _iopub_socket_default(self): + return self.iopub_thread.background_socket + 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._underlying_iopub_socket.on_trait_change(self._io_dispatch, 'message_sent') self.shell.kernel = self def execute_request(self, stream, ident, parent): @@ -128,12 +139,10 @@ return InProcessInteractiveShell def _stdout_default(self): - from ipykernel.iostream import OutStream - return OutStream(self.session, self.iopub_socket, u'stdout', pipe=False) + return OutStream(self.session, self.iopub_thread, u'stdout') def _stderr_default(self): - from ipykernel.iostream import OutStream - return OutStream(self.session, self.iopub_socket, u'stderr', pipe=False) + return OutStream(self.session, self.iopub_thread, u'stderr') #----------------------------------------------------------------------------- # Interactive shell subclass diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ipykernel-4.2.2/ipykernel/iostream.py new/ipykernel-4.3.1/ipykernel/iostream.py --- old/ipykernel-4.2.2/ipykernel/iostream.py 2016-01-02 14:15:12.000000000 +0100 +++ new/ipykernel-4.3.1/ipykernel/iostream.py 2016-02-22 13:54:46.000000000 +0100 @@ -1,21 +1,27 @@ +# coding: utf-8 """Wrappers for forwarding stdout/stderr over zmq""" # Copyright (c) IPython Development Team. # Distributed under the terms of the Modified BSD License. +from __future__ import print_function +import atexit import os import threading -import time +import sys import uuid +import warnings from io import StringIO, UnsupportedOperation import zmq from zmq.eventloop.ioloop import IOLoop +from zmq.eventloop.zmqstream import ZMQStream from jupyter_client.session import extract_header from ipython_genutils import py3compat from ipython_genutils.py3compat import unicode_type + from IPython.utils.warn import warn #----------------------------------------------------------------------------- @@ -26,166 +32,261 @@ CHILD = 1 #----------------------------------------------------------------------------- -# Stream classes +# IO 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() +class IOPubThread(object): + """An object for sending IOPub messages in a background thread + + prevents a blocking main thread + + IOPubThread(pub_socket).background_socket is a Socket-API-providing object + whose IO is always run in a thread. + """ + + def __init__(self, socket, pipe=False): + self.socket = socket + self.background_socket = BackgroundSocket(self) self._master_pid = os.getpid() - self._master_thread = threading.current_thread().ident - self._pipe_pid = os.getpid() self._pipe_flag = pipe + self.io_loop = IOLoop() if pipe: self._setup_pipe_in() + self.thread = threading.Thread(target=self._thread_main) + self.thread.daemon = True + + def _thread_main(self): + """The inner loop that's actually run in a thread""" + self.io_loop.start() + self.io_loop.close() def _setup_pipe_in(self): """setup listening pipe for subprocesses""" - ctx = self.pub_socket.context + ctx = self.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 + pipe_in = ctx.socket(zmq.PULL) + pipe_in.linger = 0 try: - self._pipe_port = self._pipe_in.bind_to_random_port("tcp://127.0.0.1") + self._pipe_port = 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 + + warn("Couldn't bind IOPub Pipe to 127.0.0.1: %s" % e + "\nsubprocess output will be unavailable." ) self._pipe_flag = False - self._pipe_in.close() - del self._pipe_in + pipe_in.close() 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, - ) + self._pipe_in = ZMQStream(pipe_in, self.io_loop) + self._pipe_in.on_recv(self._handle_pipe_msg) + + def _handle_pipe_msg(self, msg): + """handle a pipe message from a subprocess""" + if not self._pipe_flag or not self._is_master_process(): + return + if msg[0] != self._pipe_uuid: + print("Bad pipe message: %s", msg, file=sys.__stderr__) + return + self.send_multipart(msg[1:]) 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) + pipe_out = ctx.socket(zmq.PUSH) + pipe_out.linger = 3000 # 3s timeout for pipe_out sends before discarding the message + pipe_out.connect("tcp://127.0.0.1:%i" % self._pipe_port) + return ctx, pipe_out 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 start(self): + """Start the IOPub thread""" + self.thread.start() + # make sure we don't prevent process exit + # I'm not sure why setting daemon=True above isn't enough, but it doesn't appear to be. + atexit.register(self.stop) + + def stop(self): + """Stop the IOPub thread""" + if not self.thread.is_alive(): + return + self.io_loop.add_callback(self.io_loop.stop) + self.thread.join() + + def close(self): + self.socket.close() + self.socket = None + + @property + def closed(self): + return self.socket is None + + def send_multipart(self, *args, **kwargs): + """send_multipart schedules actual zmq send in my thread. + + If my thread isn't running (e.g. forked process), send immediately. + """ + + if self.thread.is_alive(): + self.io_loop.add_callback(lambda : self._really_send(*args, **kwargs)) + else: + self._really_send(*args, **kwargs) + + def _really_send(self, msg, *args, **kwargs): + """The callback that actually sends messages""" + mp_mode = self._check_mp_mode() + + if mp_mode != CHILD: + # we are master, do a regular send + self.socket.send_multipart(msg, *args, **kwargs) + else: + # we are a child, pipe to master + # new context/socket for every pipe-out + # since forks don't teardown politely, use ctx.term to ensure send has completed + ctx, pipe_out = self._setup_pipe_out() + pipe_out.send_multipart([self._pipe_uuid] + msg, *args, **kwargs) + pipe_out.close() + ctx.term() + + +class BackgroundSocket(object): + """Wrapper around IOPub thread that provides zmq send[_multipart]""" + io_thread = None + + def __init__(self, io_thread): + self.io_thread = io_thread + + def __getattr__(self, attr): + """Wrap socket attr access for backward-compatibility""" + if attr.startswith('__') and attr.endswith('__'): + # don't wrap magic methods + super(BackgroundSocket, self).__getattr__(attr) + if hasattr(self.io_thread.socket, attr): + warnings.warn("Accessing zmq Socket attribute %s on BackgroundSocket" % attr, + DeprecationWarning, stacklevel=2) + return getattr(self.io_thread.socket, attr) + super(BackgroundSocket, self).__getattr__(attr) + + def __setattr__(self, attr, value): + if attr == 'io_thread' or (attr.startswith('__' and attr.endswith('__'))): + super(BackgroundSocket, self).__setattr__(attr, value) + else: + warnings.warn("Setting zmq Socket attribute %s on BackgroundSocket" % attr, + DeprecationWarning, stacklevel=2) + setattr(self.io_thread.socket, attr, value) + + def send(self, msg, *args, **kwargs): + return self.send_multipart([msg], *args, **kwargs) + + def send_multipart(self, *args, **kwargs): + """Schedule send in IO thread""" + return self.io_thread.send_multipart(*args, **kwargs) + + +class OutStream(object): + """A file like object that publishes the stream to a 0MQ PUB socket. + + Output is handed off to an IO Thread + """ + + # The time interval between automatic flushes, in seconds. + flush_interval = 0.2 + topic=None + + def __init__(self, session, pub_thread, name, pipe=None): + if pipe is not None: + warnings.warn("pipe argument to OutStream is deprecated and ignored", + DeprecationWarning) + self.encoding = 'UTF-8' + self.session = session + if not isinstance(pub_thread, IOPubThread): + # Backward-compat: given socket, not thread. Wrap in a thread. + warnings.warn("OutStream should be created with IOPubThread, not %r" % pub_thread, + DeprecationWarning, stacklevel=2) + pub_thread = IOPubThread(pub_thread) + pub_thread.start() + self.pub_thread = pub_thread + self.name = name + self.topic = b'stream.' + py3compat.cast_bytes(name) + self.parent_header = {} + self._master_pid = os.getpid() + self._flush_lock = threading.Lock() + self._flush_timeout = None + self._io_loop = pub_thread.io_loop + self._new_buffer() + + def _is_master_process(self): + return os.getpid() == self._master_pid def set_parent(self, parent): self.parent_header = extract_header(parent) def close(self): - self.pub_socket = None + self.pub_thread = 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 + return self.pub_thread is None def _schedule_flush(self): - """schedule a flush in the main thread - - only works with a tornado/pyzmq eventloop running + """schedule a flush in the IO thread + + call this on write, to indicate that flush should be called soon. """ - 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() + with self._flush_lock: + if self._flush_timeout is not None: return + # None indicates there's no flush scheduled. + # Use a non-None placeholder to indicate that a flush is scheduled + # to avoid races while we wait for _schedule_in_thread below to fire in the io thread. + self._flush_timeout = 'placeholder' + + # add_timeout has to be handed to the io thread with add_callback + def _schedule_in_thread(): + self._flush_timeout = self._io_loop.call_later(self.flush_interval, self._flush) + self._io_loop.add_callback(_schedule_in_thread) - self._flush_from_subprocesses() + def flush(self): + """trigger actual zmq send + + send will happen in the background thread + """ + if self.pub_thread.thread.is_alive(): + self._io_loop.add_callback(self._flush) + # wait for flush to actually get through: + evt = threading.Event() + self._io_loop.add_callback(evt.set) + evt.wait() + else: + self._flush() + + def _flush(self): + """This is where the actual send happens. + + _flush should generally be called in the IO thread, + unless the thread has been destroyed (e.g. forked subprocess). + """ + with self._flush_lock: + self._flush_timeout = None 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 - + if data: + # FIXME: this disables Session's fork-safe check, + # since pub_thread is itself fork-safe. + # There should be a better way to do this. + self.session.pid = os.getpid() + content = {u'name':self.name, u'text':data} + self.session.send(self.pub_thread, u'stream', content=content, + parent=self.parent_header, ident=self.topic) + def isatty(self): return False @@ -205,14 +306,14 @@ raise UnsupportedOperation("IOStream has no fileno.") def write(self, string): - if self.pub_socket is None: + if self.pub_thread 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) + is_child = (not self._is_master_process()) self._buffer.write(string) if is_child: # newlines imply flush in subprocesses @@ -220,16 +321,11 @@ # 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() + else: + self._schedule_flush() def writelines(self, sequence): - if self.pub_socket is None: + if self.pub_thread is None: raise ValueError('I/O operation on closed file') else: for string in sequence: @@ -239,11 +335,11 @@ """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() + buf = self._buffer + self._new_buffer() + data = buf.getvalue() + buf.close() return data def _new_buffer(self): self._buffer = StringIO() - self._start = -1 diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ipykernel-4.2.2/ipykernel/ipkernel.py new/ipykernel-4.3.1/ipykernel/ipkernel.py --- old/ipykernel-4.2.2/ipykernel/ipkernel.py 2015-11-13 10:25:37.000000000 +0100 +++ new/ipykernel-4.3.1/ipykernel/ipkernel.py 2016-02-22 13:54:46.000000000 +0100 @@ -11,7 +11,6 @@ from .comm import CommManager from .kernelbase import Kernel as KernelBase -from .serialize import serialize_object, unpack_apply_message from .zmqshell import ZMQInteractiveShell @@ -51,8 +50,6 @@ 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 @@ -124,6 +121,33 @@ super(IPythonKernel, self).set_parent(ident, parent) self.shell.set_parent(parent) + def init_metadata(self, parent): + """Initialize metadata. + + Run at the beginning of each execution request. + """ + md = super(IPythonKernel, self).init_metadata(parent) + # FIXME: remove deprecated ipyparallel-specific code + # This is required for ipyparallel < 5.0 + md.update({ + 'dependencies_met' : True, + 'engine' : self.ident, + }) + return md + + def finish_metadata(self, parent, metadata, reply_content): + """Finish populating metadata. + + Run after completing an execution request. + """ + # FIXME: remove deprecated ipyparallel-specific code + # This is required by ipyparallel < 5.0 + metadata['status'] = reply_content['status'] + if reply_content['status'] == 'error' and reply_content['ename'] == 'UnmetDependency': + metadata['dependencies_met'] = False + + return metadata + def _forward_input(self, allow_stdin=False): """Forward raw_input and getpass to the current frontend. @@ -198,10 +222,11 @@ # 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 + # FIXME: deprecate piece for ipyparallel: + e_info = dict(engine_uuid=self.ident, engine_id=self.int_id, method='execute') + reply_content['engine_info'] = e_info if 'traceback' in reply_content: self.log.info("Exception in execute request:\n%s", '\n'.join(reply_content['traceback'])) @@ -289,6 +314,7 @@ return r def do_apply(self, content, bufs, msg_id, reply_metadata): + from .serialize import serialize_object, unpack_apply_message shell = self.shell try: working = shell.user_ns @@ -328,18 +354,17 @@ 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 + + # FIXME: deprecate piece for ipyparallel: + e_info = dict(engine_uuid=self.ident, engine_id=self.int_id, method='apply') + reply_content['engine_info'] = e_info 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'} diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ipykernel-4.2.2/ipykernel/kernelapp.py new/ipykernel-4.3.1/ipykernel/kernelapp.py --- old/ipykernel-4.2.2/ipykernel/kernelapp.py 2016-01-02 14:15:14.000000000 +0100 +++ new/ipykernel-4.3.1/ipykernel/kernelapp.py 2016-02-26 14:25:25.000000000 +0100 @@ -33,6 +33,7 @@ from jupyter_client.connect import ConnectionFileMixin # local imports +from .iostream import IOPubThread from .heartbeat import Heartbeat from .ipkernel import IPythonKernel from .parentpoller import ParentPollerUnix, ParentPollerWindows @@ -231,11 +232,6 @@ 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) @@ -245,6 +241,19 @@ 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) + + self.init_iopub(context) + + def init_iopub(self, context): + 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.iopub_thread = IOPubThread(self.iopub_socket, pipe=True) + self.iopub_thread.start() + # backward-compat: wrap iopub socket API in background thread + self.iopub_socket = self.iopub_thread.background_socket + def init_heartbeat(self): """start the heart beating""" @@ -298,11 +307,38 @@ """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') + sys.stdout = outstream_factory(self.session, self.iopub_thread, u'stdout') + sys.stderr = outstream_factory(self.session, self.iopub_thread, u'stderr') if self.displayhook_class: displayhook_factory = import_item(str(self.displayhook_class)) sys.displayhook = displayhook_factory(self.session, self.iopub_socket) + + self.patch_io() + + def patch_io(self): + """Patch important libraries that can't handle sys.stdout forwarding""" + try: + import faulthandler + except ImportError: + pass + else: + # Warning: this is a monkeypatch of `faulthandler.enable`, watch for possible + # updates to the upstream API and update accordingly (up-to-date as of Python 3.5): + # https://docs.python.org/3/library/faulthandler.html#faulthandler.enable + + # change default file to __stderr__ from forwarded stderr + faulthandler_enable = faulthandler.enable + def enable(file=sys.__stderr__, all_threads=True, **kwargs): + return faulthandler_enable(file=file, all_threads=all_threads, **kwargs) + + faulthandler.enable = enable + + if hasattr(faulthandler, 'register'): + faulthandler_register = faulthandler.register + def register(signum, file=sys.__stderr__, all_threads=True, chain=False, **kwargs): + return faulthandler_register(signum, file=file, all_threads=all_threads, + chain=chain, **kwargs) + faulthandler.register = register def init_signal(self): signal.signal(signal.SIGINT, signal.SIG_IGN) @@ -316,6 +352,7 @@ kernel = kernel_factory(parent=self, session=self.session, shell_streams=[shell_stream, control_stream], + iopub_thread=self.iopub_thread, iopub_socket=self.iopub_socket, stdin_socket=self.stdin_socket, log=self.log, diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ipykernel-4.2.2/ipykernel/kernelbase.py new/ipykernel-4.3.1/ipykernel/kernelbase.py --- old/ipykernel-4.2.2/ipykernel/kernelbase.py 2016-01-02 14:15:12.000000000 +0100 +++ new/ipykernel-4.3.1/ipykernel/kernelbase.py 2016-02-22 13:54:46.000000000 +0100 @@ -49,8 +49,9 @@ 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) + iopub_socket = Any() + iopub_thread = Any() + stdin_socket = Any() log = Instance(logging.Logger, allow_none=True) # identities: @@ -111,25 +112,29 @@ # Track execution count here. For IPython, we override this to use the # execution count we store in the shell. execution_count = 0 - + + msg_types = [ + 'execute_request', 'complete_request', + 'inspect_request', 'history_request', + 'comm_info_request', 'kernel_info_request', + 'connect_request', 'shutdown_request', + 'is_complete_request', + # deprecated: + 'apply_request', + ] + # add deprecated ipyparallel control messages + control_msg_types = msg_types + ['clear_request', 'abort_request'] 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', - 'comm_info_request', 'kernel_info_request', - 'connect_request', 'shutdown_request', - 'apply_request', 'is_complete_request', - ] self.shell_handlers = {} - for msg_type in msg_types: + for msg_type in self.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: + for msg_type in self.control_msg_types: self.control_handlers[msg_type] = getattr(self, msg_type) @@ -164,6 +169,25 @@ sys.stderr.flush() self._publish_status(u'idle') + def should_handle(self, stream, msg, idents): + """Check whether a shell-channel message should be handled + + Allows subclasses to prevent handling of certain messages (e.g. aborted requests). + """ + msg_id = msg['header']['msg_id'] + if msg_id in self.aborted: + msg_type = msg['header']['msg_type'] + # is it safe to assume a msg_id will not be resubmitted? + self.aborted.remove(msg_id) + 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 False + return True + def dispatch_shell(self, stream, msg): """dispatch shell requests""" # flush control requests first @@ -191,15 +215,7 @@ 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) + if not self.should_handle(stream, msg, idents): return handler = self.shell_handlers.get(msg_type, None) @@ -288,17 +304,6 @@ # 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.""" @@ -340,6 +345,22 @@ """ return self.session.send(stream, msg_or_type, content, self._parent_header, ident, buffers, track, header, metadata) + + def init_metadata(self, parent): + """Initialize metadata. + + Run at the beginning of execution requests. + """ + return { + 'started': datetime.now(), + } + + def finish_metadata(self, parent, metadata, reply_content): + """Finish populating metadata. + + Run after completing an execution request. + """ + return metadata def execute_request(self, stream, ident, parent): """handle an execute_request""" @@ -358,7 +379,7 @@ stop_on_error = content.get('stop_on_error', True) - md = self._make_metadata(parent['metadata']) + metadata = self.init_metadata(parent) # Re-broadcast our input for the benefit of listening clients, and # start computing output @@ -380,14 +401,10 @@ # 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 + metadata = self.finish_metadata(parent, metadata, reply_content) reply_msg = self.session.send(stream, u'execute_reply', - reply_content, parent, metadata=md, + reply_content, parent, metadata=metadata, ident=ident) self.log.debug("%s", reply_msg) @@ -532,10 +549,11 @@ } #--------------------------------------------------------------------------- - # Engine methods + # Engine methods (DEPRECATED) #--------------------------------------------------------------------------- def apply_request(self, stream, ident, parent): + self.log.warn("""apply_request is deprecated in kernel_base, moving to ipyparallel.""") try: content = parent[u'content'] bufs = parent[u'buffers'] @@ -544,31 +562,30 @@ self.log.error("Got bad msg: %s", parent, exc_info=True) return - md = self._make_metadata(parent['metadata']) + md = self.init_metadata(parent) 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() + md = self.finish_metadata(parent, md, reply_content) + 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. - """ + """DEPRECATED""" raise NotImplementedError #--------------------------------------------------------------------------- - # Control messages + # Control messages (DEPRECATED) #--------------------------------------------------------------------------- def abort_request(self, stream, ident, parent): """abort a specific msg by id""" + self.log.warn("abort_request is deprecated in kernel_base. It os only part of IPython parallel") msg_ids = parent['content'].get('msg_ids', None) if isinstance(msg_ids, string_types): msg_ids = [msg_ids] @@ -584,15 +601,13 @@ def clear_request(self, stream, idents, parent): """Clear our namespace.""" + self.log.warn("clear_request is deprecated in kernel_base. It os only part of IPython parallel") 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. - """ + """DEPRECATED""" raise NotImplementedError #--------------------------------------------------------------------------- @@ -601,10 +616,7 @@ 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 + base = "kernel.%s" % self.ident return py3compat.cast_bytes("%s.%s" % (base, topic)) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ipykernel-4.2.2/ipykernel/kernelspec.py new/ipykernel-4.3.1/ipykernel/kernelspec.py --- old/ipykernel-4.2.2/ipykernel/kernelspec.py 2015-12-08 22:50:06.000000000 +0100 +++ new/ipykernel-4.3.1/ipykernel/kernelspec.py 2016-02-22 13:54:46.000000000 +0100 @@ -140,7 +140,7 @@ def start(self): import argparse - parser = argparse.ArgumentParser( + parser = argparse.ArgumentParser(prog=self.name, description="Install the IPython kernel spec.") parser.add_argument('--user', action='store_true', help="Install for the current user instead of system-wide") diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ipykernel-4.2.2/ipykernel/log.py new/ipykernel-4.3.1/ipykernel/log.py --- old/ipykernel-4.2.2/ipykernel/log.py 2015-11-13 10:25:37.000000000 +0100 +++ new/ipykernel-4.3.1/ipykernel/log.py 2016-02-22 13:54:46.000000000 +0100 @@ -2,6 +2,9 @@ from zmq.log.handlers import PUBHandler +import warnings +warnings.warn("ipykernel.log is deprecated. It has moved to ipyparallel.engine.log", DeprecationWarning) + class EnginePUBHandler(PUBHandler): """A simple PUBHandler subclass that sets root_topic""" engine=None diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ipykernel-4.2.2/ipykernel/pickleutil.py new/ipykernel-4.3.1/ipykernel/pickleutil.py --- old/ipykernel-4.2.2/ipykernel/pickleutil.py 2015-11-13 10:25:37.000000000 +0100 +++ new/ipykernel-4.3.1/ipykernel/pickleutil.py 2016-02-22 13:54:46.000000000 +0100 @@ -4,6 +4,9 @@ # Copyright (c) IPython Development Team. # Distributed under the terms of the Modified BSD License. +import warnings +warnings.warn("ipykernel.pickleutil is deprecated. It has moved to ipyparallel.", DeprecationWarning) + import copy import logging import sys diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ipykernel-4.2.2/ipykernel/serialize.py new/ipykernel-4.3.1/ipykernel/serialize.py --- old/ipykernel-4.2.2/ipykernel/serialize.py 2015-11-13 10:25:37.000000000 +0100 +++ new/ipykernel-4.3.1/ipykernel/serialize.py 2016-02-22 13:54:46.000000000 +0100 @@ -3,6 +3,9 @@ # Copyright (c) IPython Development Team. # Distributed under the terms of the Modified BSD License. +import warnings +warnings.warn("ipykernel.serialize is deprecated. It has moved to ipyparallel.serialize", DeprecationWarning) + try: import cPickle pickle = cPickle diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ipykernel-4.2.2/ipykernel/tests/test_connect.py new/ipykernel-4.3.1/ipykernel/tests/test_connect.py --- old/ipykernel-4.2.2/ipykernel/tests/test_connect.py 2015-05-08 22:15:04.000000000 +0200 +++ new/ipykernel-4.3.1/ipykernel/tests/test_connect.py 2016-02-22 13:54:46.000000000 +0100 @@ -52,10 +52,12 @@ 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) + sub_info = {k:v for k,v in info.items() if k in sample_info} + nt.assert_equal(sub_info, sample_info) info2 = json.loads(json_info) info2['key'] = str_to_bytes(info2['key']) - nt.assert_equal(info2, sample_info) + sub_info2 = {k:v for k,v in info.items() if k in sample_info} + nt.assert_equal(sub_info2, sample_info) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ipykernel-4.2.2/ipykernel/tests/test_kernel.py new/ipykernel-4.3.1/ipykernel/tests/test_kernel.py --- old/ipykernel-4.2.2/ipykernel/tests/test_kernel.py 2016-01-02 14:15:14.000000000 +0100 +++ new/ipykernel-4.3.1/ipykernel/tests/test_kernel.py 2016-02-22 13:54:46.000000000 +0100 @@ -7,6 +7,7 @@ import io import os.path import sys +import time import nose.tools as nt @@ -19,12 +20,12 @@ flush_channels, wait_for_idle) -def _check_mp_mode(kc, expected=False, stream="stdout"): +def _check_master(kc, expected=True, 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) + msg_id, content = execute(kc=kc, code="print (sys.%s._is_master_process())" % stream) stdout, stderr = assemble_output(kc.iopub_channel) - nt.assert_equal(eval(stdout.strip()), expected) + nt.assert_equal(stdout.strip(), repr(expected)) # printing tests @@ -37,7 +38,7 @@ stdout, stderr = assemble_output(iopub) nt.assert_equal(stdout, 'hi\n') nt.assert_equal(stderr, '') - _check_mp_mode(kc, expected=False) + _check_master(kc, expected=True) def test_sys_path(): @@ -61,29 +62,27 @@ with new_kernel() as kc: iopub = kc.iopub_channel - _check_mp_mode(kc, expected=False) + _check_master(kc, expected=True) flush_channels(kc) np = 5 code = '\n'.join([ "from __future__ import print_function", + "import time", "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()" + "for p in pool: p.join()", + "time.sleep(0.5)," ]) - 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") + _check_master(kc, expected=True) + _check_master(kc, expected=True, stream="stderr") def test_subprocess_noprint(): @@ -104,8 +103,8 @@ nt.assert_equal(stdout, '') nt.assert_equal(stderr, '') - _check_mp_mode(kc, expected=False) - _check_mp_mode(kc, expected=False, stream="stderr") + _check_master(kc, expected=True) + _check_master(kc, expected=True, stream="stderr") @dec.knownfailureif(sys.platform == 'win32', "subprocess prints fail on Windows") @@ -126,8 +125,8 @@ 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") + _check_master(kc, expected=True) + _check_master(kc, expected=True, stream="stderr") # raw_input tests @@ -189,6 +188,20 @@ nt.assert_in(u'a=1', content) nt.assert_in(u'b=u"abcþ"', content) + +@dec.skip_without('faulthandler') +def test_smoke_faulthandler(): + with kernel() as kc: + code = u'\n'.join([ + 'import faulthandler, signal', + 'faulthandler.enable()', + 'faulthandler.register(signal.SIGTERM)', + ]) + _, reply = execute(code, kc=kc) + print(_) + nt.assert_equal(reply['status'], 'ok', reply.get('traceback', '')) + + def test_help_output(): """ipython kernel --help-all works""" tt.help_all_output_test('kernel') @@ -226,3 +239,19 @@ nt.assert_greater(len(matches), 0) for match in matches: nt.assert_equal(match[:2], 'a.') + + +def test_shutdown(): + """Kernel exits after polite shutdown_request""" + with new_kernel() as kc: + km = kc.parent + execute(u'a = 1', kc=kc) + wait_for_idle(kc) + kc.shutdown() + for i in range(100): # 10s timeout + if km.is_alive(): + time.sleep(.1) + else: + break + nt.assert_false(km.is_alive()) + diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ipykernel-4.2.2/ipykernel/zmqshell.py new/ipykernel-4.3.1/ipykernel/zmqshell.py --- old/ipykernel-4.2.2/ipykernel/zmqshell.py 2015-12-08 22:50:06.000000000 +0100 +++ new/ipykernel-4.3.1/ipykernel/zmqshell.py 2016-02-26 14:25:34.000000000 +0100 @@ -19,6 +19,7 @@ import os import sys import time +import warnings from zmq.eventloop import ioloop @@ -32,7 +33,7 @@ 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.core.usage import default_banner from IPython.display import display, Javascript from ipykernel import ( get_connection_file, get_connection_info, connect_qtconsole @@ -45,7 +46,6 @@ from traitlets import Instance, Type, Dict, CBool, CBytes, Any from IPython.utils.warn import error from ipykernel.displayhook import ZMQShellDisplayHook -from ipykernel.datapub import ZMQDataPublisher from jupyter_client.session import extract_header from jupyter_client.session import Session @@ -345,12 +345,12 @@ displayhook_class = Type(ZMQShellDisplayHook) display_pub_class = Type(ZMQDisplayPublisher) - data_pub_class = Type(ZMQDataPublisher) + data_pub_class = Type('ipykernel.datapub.ZMQDataPublisher') kernel = Any() parent_header = Any() def _banner1_default(self): - return default_gui_banner + return default_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 @@ -398,6 +398,25 @@ def init_hooks(self): super(ZMQInteractiveShell, self).init_hooks() self.set_hook('show_in_pager', page.as_hook(payloadpage.page), 99) + + def init_data_pub(self): + """Delay datapub init until request, for deprecation warnings""" + pass + + @property + def data_pub(self): + if not hasattr(self, '_data_pub'): + warnings.warn("InteractiveShell.data_pub is deprecated outside IPython parallel.", + DeprecationWarning, stacklevel=2) + + self._data_pub = self.data_pub_class(parent=self) + self._data_pub.session = self.display_pub.session + self._data_pub.pub_socket = self.display_pub.pub_socket + return self._data_pub + + @data_pub.setter + def data_pub(self, pub): + self._data_pub = pub def ask_exit(self): """Engage the exit actions.""" @@ -454,7 +473,8 @@ self.parent_header = parent self.displayhook.set_parent(parent) self.display_pub.set_parent(parent) - self.data_pub.set_parent(parent) + if hasattr(self, '_data_pub'): + self.data_pub.set_parent(parent) try: sys.stdout.set_parent(parent) except AttributeError: diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ipykernel-4.2.2/setup.py new/ipykernel-4.3.1/setup.py --- old/ipykernel-4.2.2/setup.py 2015-08-12 03:12:09.000000000 +0200 +++ new/ipykernel-4.3.1/setup.py 2016-02-22 13:54:46.000000000 +0100 @@ -83,6 +83,7 @@ 'ipython>=4.0.0', 'traitlets', 'jupyter_client', + 'tornado>=4.0', ] extras_require = setuptools_args['extras_require'] = {
participants (1)
-
root@hilbert.suse.de