From 51fb84bbedf4f131eb5d94b2b69470d6fc181698 2014-05-05 17:20:56 From: Thomas Kluyver Date: 2014-05-05 17:20:56 Subject: [PATCH] Merge pull request #5759 from minrk/travis-3.4 test with Python 3.4 on Travis --- diff --git a/.travis.yml b/.travis.yml index fc7cca9..097300d 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,11 +1,12 @@ # http://travis-ci.org/#!/ipython/ipython language: python python: + - 3.4 - 2.7 - 3.3 env: - - GROUP= - GROUP=js + - GROUP= before_install: # workaround for https://github.com/travis-ci/travis-cookbooks/issues/155 - sudo rm -rf /dev/shm && sudo ln -s /run/shm /dev/shm @@ -18,3 +19,8 @@ install: - time python setup.py install -q script: - cd /tmp && iptest $GROUP + +matrix: + exclude: + - python: 3.3 + env: GROUP=js diff --git a/IPython/html/services/kernels/handlers.py b/IPython/html/services/kernels/handlers.py index 605e9e6..041ffce 100644 --- a/IPython/html/services/kernels/handlers.py +++ b/IPython/html/services/kernels/handlers.py @@ -127,7 +127,10 @@ class ZMQChannelHandler(AuthenticatedZMQStreamHandler): # closed before the ZMQ streams are setup, they could be None. if self.zmq_stream is not None and not self.zmq_stream.closed(): self.zmq_stream.on_recv(None) + # close the socket directly, don't wait for the stream + socket = self.zmq_stream.socket self.zmq_stream.close() + socket.close() class IOPubHandler(ZMQChannelHandler): diff --git a/IPython/kernel/channels.py b/IPython/kernel/channels.py index 2457462..3b64865 100644 --- a/IPython/kernel/channels.py +++ b/IPython/kernel/channels.py @@ -209,6 +209,7 @@ class ShellChannel(ZMQSocketChannel): def run(self): """The thread's main activity. Call start() instead.""" self.socket = self.context.socket(zmq.DEALER) + self.socket.linger = 1000 self.socket.setsockopt(zmq.IDENTITY, self.session.bsession) self.socket.connect(self.address) self.stream = zmqstream.ZMQStream(self.socket, self.ioloop) @@ -408,6 +409,7 @@ class IOPubChannel(ZMQSocketChannel): def run(self): """The thread's main activity. Call start() instead.""" self.socket = self.context.socket(zmq.SUB) + self.socket.linger = 1000 self.socket.setsockopt(zmq.SUBSCRIBE,b'') self.socket.setsockopt(zmq.IDENTITY, self.session.bsession) self.socket.connect(self.address) @@ -468,6 +470,7 @@ class StdInChannel(ZMQSocketChannel): def run(self): """The thread's main activity. Call start() instead.""" self.socket = self.context.socket(zmq.DEALER) + self.socket.linger = 1000 self.socket.setsockopt(zmq.IDENTITY, self.session.bsession) self.socket.connect(self.address) self.stream = zmqstream.ZMQStream(self.socket, self.ioloop) @@ -518,7 +521,7 @@ class HBChannel(ZMQSocketChannel): self.poller.unregister(self.socket) self.socket.close() self.socket = self.context.socket(zmq.REQ) - self.socket.setsockopt(zmq.LINGER, 0) + self.socket.linger = 1000 self.socket.connect(self.address) self.poller.register(self.socket, zmq.POLLIN) diff --git a/IPython/kernel/connect.py b/IPython/kernel/connect.py index 137d0c8..fc4da36 100644 --- a/IPython/kernel/connect.py +++ b/IPython/kernel/connect.py @@ -531,6 +531,8 @@ class ConnectionFileMixin(Configurable): socket_type = channel_socket_types[channel] self.log.debug("Connecting to: %s" % url) sock = self.context.socket(socket_type) + # set linger to 1s to prevent hangs at exit + sock.linger = 1000 if identity: sock.identity = identity sock.connect(url) diff --git a/IPython/kernel/zmq/heartbeat.py b/IPython/kernel/zmq/heartbeat.py index ccfd5dc..2ee6dc2 100644 --- a/IPython/kernel/zmq/heartbeat.py +++ b/IPython/kernel/zmq/heartbeat.py @@ -53,6 +53,7 @@ class Heartbeat(Thread): 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: diff --git a/IPython/kernel/zmq/kernelapp.py b/IPython/kernel/zmq/kernelapp.py index 07046df..ece61e8 100644 --- a/IPython/kernel/zmq/kernelapp.py +++ b/IPython/kernel/zmq/kernelapp.py @@ -236,18 +236,22 @@ class IPKernelApp(BaseIPythonApplication, InteractiveShellApp, # 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) diff --git a/IPython/testing/iptestcontroller.py b/IPython/testing/iptestcontroller.py index 47118dc..eb554e4 100644 --- a/IPython/testing/iptestcontroller.py +++ b/IPython/testing/iptestcontroller.py @@ -6,16 +6,9 @@ test suite. """ -#----------------------------------------------------------------------------- -# Copyright (C) 2009-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 -#----------------------------------------------------------------------------- +# Copyright (c) IPython Development Team. +# Distributed under the terms of the Modified BSD License. + from __future__ import print_function import argparse @@ -34,6 +27,24 @@ from IPython.utils.py3compat import bytes_to_str from IPython.utils.sysinfo import get_sys_info from IPython.utils.tempdir import TemporaryDirectory +try: + # Python >= 3.3 + from subprocess import TimeoutExpired + def popen_wait(p, timeout): + return p.wait(timeout) +except ImportError: + class TimeoutExpired(Exception): + pass + def popen_wait(p, timeout): + """backport of Popen.wait from Python 3""" + for i in range(int(10 * timeout)): + if p.poll() is not None: + return + time.sleep(0.1) + if p.poll() is None: + raise TimeoutExpired + +NOTEBOOK_SHUTDOWN_TIMEOUT = 10 class TestController(object): """Run tests in a subprocess @@ -287,7 +298,27 @@ class JSController(TestController): except OSError: # already dead pass - self.server.wait() + # wait 10s for the server to shutdown + try: + popen_wait(self.server, NOTEBOOK_SHUTDOWN_TIMEOUT) + except TimeoutExpired: + # server didn't terminate, kill it + try: + print("Failed to terminate notebook server, killing it.", + file=sys.stderr + ) + self.server.kill() + except OSError: + # already dead + pass + # wait another 10s + try: + popen_wait(self.server, NOTEBOOK_SHUTDOWN_TIMEOUT) + except TimeoutExpired: + print("Notebook server still running (%s)" % self.server_info_file, + file=sys.stderr + ) + self.stream_capturer.halt() TestController.cleanup(self)