diff --git a/ipykernel/heartbeat.py b/ipykernel/heartbeat.py index 9d5ed177d..39ac5379a 100644 --- a/ipykernel/heartbeat.py +++ b/ipykernel/heartbeat.py @@ -98,7 +98,20 @@ def run(self): zmq.device(zmq.QUEUE, self.socket, self.socket) except zmq.ZMQError as e: if e.errno == errno.EINTR: + # signal interrupt, resume heartbeat continue + elif e.errno == zmq.ETERM: + # context terminated, close socket and exit + try: + self.socket.close() + except zmq.ZMQError: + # suppress further errors during cleanup + # this shouldn't happen, though + pass + break + elif e.errno == zmq.ENOTSOCK: + # socket closed elsewhere, exit + break else: raise else: diff --git a/ipykernel/kernelapp.py b/ipykernel/kernelapp.py index db3d8a461..159dc51bb 100644 --- a/ipykernel/kernelapp.py +++ b/ipykernel/kernelapp.py @@ -309,10 +309,11 @@ def init_heartbeat(self): def close(self): """Close zmq sockets in an orderly fashion""" + # un-capture IO before we start closing channels + self.reset_io() self.log.info("Cleaning up sockets") if self.heartbeat: self.log.debug("Closing heartbeat channel") - self.heartbeat.socket.close() self.heartbeat.context.term() if self.iopub_thread: self.log.debug("Closing iopub channel") @@ -391,6 +392,15 @@ def init_io(self): self.patch_io() + def reset_io(self): + """restore original io + + restores state after init_io + """ + sys.stdout = sys.__stdout__ + sys.stderr = sys.__stderr__ + sys.displayhook = sys.__displayhook__ + def patch_io(self): """Patch important libraries that can't handle sys.stdout forwarding""" try: