def object_info_request(self, ident, parent): object_info = self.shell.object_inspect(parent['content']['oname']) # Before we send this object over, we scrub it for JSON usage oinfo = json_clean(object_info) msg = self.session.send(self.reply_socket, 'object_info_reply', oinfo, parent, ident) io.raw_print(msg)
def connect_request(self, ident, parent): if self._recorded_ports is not None: content = self._recorded_ports.copy() else: content = {} msg = self.session.send(self.reply_socket, "connect_reply", content, parent, ident) io.raw_print(msg)
def init_sockets(self): # Create a context, a session, and the kernel sockets. io.raw_print("Starting the kernel at pid:", os.getpid()) context = zmq.Context.instance() # Uncomment this to try closing the context. # atexit.register(context.term) self.shell_socket = context.socket(zmq.XREP) self.shell_port = self._bind_socket(self.shell_socket, self.shell_port) self.log.debug("shell XREP Channel on port: %i"%self.shell_port) self.iopub_socket = context.socket(zmq.PUB) 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.XREQ) self.stdin_port = self._bind_socket(self.stdin_socket, self.stdin_port) self.log.debug("stdin XREQ Channel on port: %i"%self.stdin_port) self.heartbeat = Heartbeat(context, (self.ip, self.hb_port)) self.hb_port = self.heartbeat.port self.log.debug("Heartbeat REP Channel on port: %i"%self.hb_port) # Helper to make it easier to connect to an existing kernel, until we have # single-port connection negotiation fully implemented. self.log.info("To connect another client to this kernel, use:") self.log.info("--external shell={0} iopub={1} stdin={2} hb={3}".format( self.shell_port, self.iopub_port, self.stdin_port, self.hb_port)) self.ports = dict(shell=self.shell_port, iopub=self.iopub_port, stdin=self.stdin_port, hb=self.hb_port)
def start(self): """ Start the kernel main loop. """ poller = zmq.Poller() poller.register(self.shell_socket, zmq.POLLIN) # loop while self.eventloop has not been overridden while self.eventloop is None: try: # scale by extra factor of 10, because there is no # reason for this to be anything less than ~ 0.1s # since it is a real poller and will respond # to events immediately # double nested try/except, to properly catch KeyboardInterrupt # due to pyzmq Issue #130 try: poller.poll(10*1000*self._poll_interval) self.do_one_iteration() except: raise except KeyboardInterrupt: # Ctrl-C shouldn't crash the kernel io.raw_print("KeyboardInterrupt caught in kernel") if self.eventloop is not None: try: self.eventloop(self) except KeyboardInterrupt: # Ctrl-C shouldn't crash the kernel io.raw_print("KeyboardInterrupt caught in kernel")
def loop_cocoa(kernel): """Start the kernel, coordinating with the Cocoa CFRunLoop event loop via the matplotlib MacOSX backend. """ from ._eventloop_macos import mainloop, stop real_excepthook = sys.excepthook def handle_int(etype, value, tb): """don't let KeyboardInterrupts look like crashes""" # wake the eventloop when we get a signal stop() if etype is KeyboardInterrupt: io.raw_print("KeyboardInterrupt caught in CFRunLoop") else: real_excepthook(etype, value, tb) while not kernel.shell.exit_now: 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 mainloop(kernel._poll_interval) sys.excepthook = real_excepthook 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 run(self): """ Run the poll loop. This method never returns. """ 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) # Listen forever. while True: result = ctypes.windll.kernel32.WaitForMultipleObjects( len(handles), # nCount (ctypes.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: raw_print('Interrupted by parent poller!') interrupt_main() elif handle == self.parent_handle: raw_print('Killed by parent poller!') os._exit(1)
def complete_request(self, ident, parent): txt, matches = self._complete(parent) matches = {'matches' : matches, 'matched_text' : txt, 'status' : 'ok'} completion_msg = self.session.send(self.reply_socket, 'complete_reply', matches, parent, ident) io.raw_print(completion_msg)
def history_request(self, ident, parent): output = parent["content"]["output"] index = parent["content"]["index"] raw = parent["content"]["raw"] hist = self.shell.get_history(index=index, raw=raw, output=output) content = {"history": hist} msg = self.session.send(self.reply_socket, "history_reply", content, parent, ident) io.raw_print(msg)
def handle_int(etype, value, tb): """don't let KeyboardInterrupts look like crashes""" # wake the eventloop when we get a signal stop() if etype is KeyboardInterrupt: io.raw_print("KeyboardInterrupt caught in CFRunLoop") else: real_excepthook(etype, value, tb)
def history_request(self, ident, parent): output = parent['content']['output'] index = parent['content']['index'] raw = parent['content']['raw'] hist = self.shell.get_history(index=index, raw=raw, output=output) content = {'history' : hist} msg = self.session.send(self.reply_socket, 'history_reply', content, parent, ident) io.raw_print(msg)
def start(self): """ Start the kernel main loop. """ while True: try: time.sleep(self._poll_interval) self.do_one_iteration() except KeyboardInterrupt: # Ctrl-C shouldn't crash the kernel io.raw_print("KeyboardInterrupt caught in kernel")
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())
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())
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.reply_socket.send_json(self._shutdown_message) self.pub_socket.send_json(self._shutdown_message) io.raw_print(self._shutdown_message) # A very short sleep to give zmq time to flush its message buffers # before Python truly shuts down. time.sleep(0.01)
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: raw_print('Killed by parent poller!') os._exit(1) time.sleep(1.0) except OSError, e: if e.errno == EINTR: continue raise
def flush(self): #io.rprint('>>>flushing output buffer: %s<<<' % self.name) # dbg if self.pub_socket is None: raise ValueError('I/O operation on closed file') else: data = self._buffer.getvalue() if data: content = {'name':self.name, 'data':data} msg = self.session.send(self.pub_socket, 'stream', content=content, parent=self.parent_header) io.raw_print(msg) self._buffer.close() self._new_buffer()
def start(self): """ Start the kernel main loop. """ poller = zmq.Poller() poller.register(self.shell_socket, zmq.POLLIN) while True: try: # scale by extra factor of 10, because there is no # reason for this to be anything less than ~ 0.1s # since it is a real poller and will respond # to events immediately poller.poll(10*1000*self._poll_interval) self.do_one_iteration() except KeyboardInterrupt: # Ctrl-C shouldn't crash the kernel io.raw_print("KeyboardInterrupt caught in kernel")
def _abort_queue(self): while True: ident,msg = self.session.recv(self.reply_socket, zmq.NOBLOCK) if msg is None: break else: assert ident is not None, \ "Unexpected missing message part." io.raw_print("Aborting:\n", Message(msg)) msg_type = msg['msg_type'] reply_type = msg_type.split('_')[0] + '_reply' reply_msg = self.session.send(self.reply_socket, reply_type, {'status' : 'aborted'}, msg, ident=ident) io.raw_print(reply_msg) # We need to wait a bit for requests to come in. This can probably # be set shorter for true asynchronous clients. time.sleep(0.1)
def start(self): """ Start the kernel main loop. """ # a KeyboardInterrupt (SIGINT) can occur on any python statement, so # let's ignore (SIG_IGN) them until we're in a place to handle them properly signal(SIGINT,SIG_IGN) poller = zmq.Poller() poller.register(self.shell_socket, zmq.POLLIN) # loop while self.eventloop has not been overridden while self.eventloop is None: try: # scale by extra factor of 10, because there is no # reason for this to be anything less than ~ 0.1s # since it is a real poller and will respond # to events immediately # double nested try/except, to properly catch KeyboardInterrupt # due to pyzmq Issue #130 try: poller.poll(10*1000*self._poll_interval) # restore raising of KeyboardInterrupt signal(SIGINT, default_int_handler) self.do_one_iteration() except: raise finally: # prevent raising of KeyboardInterrupt signal(SIGINT,SIG_IGN) except KeyboardInterrupt: # Ctrl-C shouldn't crash the kernel io.raw_print("KeyboardInterrupt caught in kernel") # stop ignoring sigint, now that we are out of our own loop, # we don't want to prevent future code from handling it signal(SIGINT, default_int_handler) while self.eventloop is not None: try: self.eventloop(self) except KeyboardInterrupt: # Ctrl-C shouldn't crash the kernel io.raw_print("KeyboardInterrupt caught in kernel") continue else: # eventloop exited cleanly, this means we should stop (right?) self.eventloop = None break
def _abort_queue(self): while True: try: ident = self.reply_socket.recv(zmq.NOBLOCK) except zmq.ZMQError, e: if e.errno == zmq.EAGAIN: break else: assert self.reply_socket.rcvmore(), "Unexpected missing message part." msg = self.reply_socket.recv_json() io.raw_print("Aborting:\n", Message(msg)) msg_type = msg["msg_type"] reply_type = msg_type.split("_")[0] + "_reply" reply_msg = self.session.msg(reply_type, {"status": "aborted"}, msg) io.raw_print(reply_msg) self.reply_socket.send(ident, zmq.SNDMORE) self.reply_socket.send_json(reply_msg) # We need to wait a bit for requests to come in. This can probably # be set shorter for true asynchronous clients. time.sleep(0.1)
def do_one_iteration(self): """Do one iteration of the kernel's evaluation loop. """ ident,msg = self.session.recv(self.reply_socket, zmq.NOBLOCK) if msg is None: return # This assert will raise in versions of zeromq 2.0.7 and lesser. # We now require 2.0.8 or above, so we can uncomment for safety. # print(ident,msg, file=sys.__stdout__) assert ident is not None, "Missing message part." # 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. # Eventually we'll move these from stdout to a logger. io.raw_print('\n*** MESSAGE TYPE:', msg['msg_type'], '***') io.raw_print(' Content: ', msg['content'], '\n --->\n ', sep='', end='') # Find and call actual handler for message handler = self.handlers.get(msg['msg_type'], None) if handler is None: io.raw_print_err("UNKNOWN MESSAGE TYPE:", msg) else: handler(ident, msg) # Check whether we should exit, in case the incoming message set the # exit flag on if self.shell.exit_now: io.raw_print('\nExiting IPython kernel...') # We do a normal, clean exit, which allows any actions registered # via atexit (such as history saving) to take place. sys.exit(0)
def execute_request(self, ident, parent): status_msg = self.session.msg(u"status", {u"execution_state": u"busy"}, parent=parent) self.pub_socket.send_json(status_msg) try: content = parent[u"content"] code = content[u"code"] silent = content[u"silent"] except: io.raw_print_err("Got bad msg: ") io.raw_print_err(Message(parent)) return shell = self.shell # we'll need this a lot here # Replace raw_input. Note that is not sufficient to replace # raw_input in the user namespace. raw_input = lambda prompt="": self._raw_input(prompt, ident, parent) __builtin__.raw_input = raw_input # Set the parent message of the display hook and out streams. shell.displayhook.set_parent(parent) sys.stdout.set_parent(parent) sys.stderr.set_parent(parent) # Re-broadcast our input for the benefit of listening clients, and # start computing output if not silent: self._publish_pyin(code, parent) reply_content = {} try: if silent: # runcode uses 'exec' mode, so no displayhook will fire, and it # doesn't call logging or history manipulations. Print # statements in that code will obviously still execute. shell.runcode(code) else: # FIXME: runlines calls the exception handler itself. shell._reply_content = None # For now leave this here until we're sure we can stop using it # shell.runlines(code) # Experimental: cell mode! Test more before turning into # default and removing the hacks around runlines. shell.run_cell(code) except: status = u"error" # FIXME: this code right now isn't being used yet by default, # because the runlines() 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" reply_content[u"status"] = status # Compute the execution counter so clients can display prompts reply_content["execution_count"] = shell.displayhook.prompt_count # 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) # At this point, we can tell whether the main code execution succeeded # or not. If it did, we proceed to evaluate user_variables/expressions if reply_content["status"] == "ok": reply_content[u"user_variables"] = shell.user_variables(content[u"user_variables"]) reply_content[u"user_expressions"] = shell.user_expressions(content[u"user_expressions"]) else: # If there was an error, don't even try to compute variables or # expressions reply_content[u"user_variables"] = {} 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() # Send the reply. reply_msg = self.session.msg(u"execute_reply", reply_content, parent) io.raw_print(reply_msg) # 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) self.reply_socket.send(ident, zmq.SNDMORE) self.reply_socket.send_json(reply_msg) if reply_msg["content"]["status"] == u"error": self._abort_queue() status_msg = self.session.msg(u"status", {u"execution_state": u"idle"}, parent=parent) self.pub_socket.send_json(status_msg)
def make_kernel(namespace, kernel_factory, out_stream_factory=None, display_hook_factory=None): """ Creates a kernel, redirects stdout/stderr, and installs a display hook and exception handler. """ # Re-direct stdout/stderr, if necessary. if namespace.no_stdout or namespace.no_stderr: blackhole = file(os.devnull, 'w') if namespace.no_stdout: sys.stdout = sys.__stdout__ = blackhole if namespace.no_stderr: sys.stderr = sys.__stderr__ = blackhole # Install minimal exception handling sys.excepthook = FormattedTB(mode='Verbose', color_scheme='NoColor', ostream=sys.__stdout__) # Create a context, a session, and the kernel sockets. io.raw_print("Starting the kernel at pid:", os.getpid()) context = zmq.Context() # Uncomment this to try closing the context. # atexit.register(context.close) session = Session(username=u'kernel') reply_socket = context.socket(zmq.XREP) xrep_port = bind_port(reply_socket, namespace.ip, namespace.xrep) io.raw_print("XREP Channel on port", xrep_port) pub_socket = context.socket(zmq.PUB) pub_port = bind_port(pub_socket, namespace.ip, namespace.pub) io.raw_print("PUB Channel on port", pub_port) req_socket = context.socket(zmq.XREQ) req_port = bind_port(req_socket, namespace.ip, namespace.req) io.raw_print("REQ Channel on port", req_port) hb = Heartbeat(context, (namespace.ip, namespace.hb)) hb.start() hb_port = hb.port io.raw_print("Heartbeat REP Channel on port", hb_port) # Helper to make it easier to connect to an existing kernel, until we have # single-port connection negotiation fully implemented. io.raw_print("To connect another client to this kernel, use:") io.raw_print("-e --xreq {0} --sub {1} --rep {2} --hb {3}".format( xrep_port, pub_port, req_port, hb_port)) # Redirect input streams and set a display hook. if out_stream_factory: sys.stdout = out_stream_factory(session, pub_socket, u'stdout') sys.stderr = out_stream_factory(session, pub_socket, u'stderr') if display_hook_factory: sys.displayhook = display_hook_factory(session, pub_socket) # Create the kernel. kernel = kernel_factory(session=session, reply_socket=reply_socket, pub_socket=pub_socket, req_socket=req_socket) kernel.record_ports(xrep_port=xrep_port, pub_port=pub_port, req_port=req_port, hb_port=hb_port) return kernel
def make_kernel(namespace, kernel_factory, out_stream_factory=None, display_hook_factory=None): """ Creates a kernel, redirects stdout/stderr, and installs a display hook and exception handler. """ # If running under pythonw.exe, the interpreter will crash if more than 4KB # of data is written to stdout or stderr. This is a bug that has been with # Python for a very long time; see http://bugs.python.org/issue706263. if sys.executable.endswith('pythonw.exe'): blackhole = file(os.devnull, 'w') sys.stdout = sys.stderr = blackhole sys.__stdout__ = sys.__stderr__ = blackhole # Install minimal exception handling sys.excepthook = FormattedTB(mode='Verbose', color_scheme='NoColor', ostream=sys.__stdout__) # Create a context, a session, and the kernel sockets. io.raw_print("Starting the kernel at pid:", os.getpid()) context = zmq.Context() # Uncomment this to try closing the context. # atexit.register(context.close) session = Session(username=u'kernel') reply_socket = context.socket(zmq.XREP) xrep_port = bind_port(reply_socket, namespace.ip, namespace.xrep) io.raw_print("XREP Channel on port", xrep_port) pub_socket = context.socket(zmq.PUB) pub_port = bind_port(pub_socket, namespace.ip, namespace.pub) io.raw_print("PUB Channel on port", pub_port) req_socket = context.socket(zmq.XREQ) req_port = bind_port(req_socket, namespace.ip, namespace.req) io.raw_print("REQ Channel on port", req_port) hb = Heartbeat(context, (namespace.ip, namespace.hb)) hb.start() hb_port = hb.port io.raw_print("Heartbeat REP Channel on port", hb_port) # Helper to make it easier to connect to an existing kernel, until we have # single-port connection negotiation fully implemented. io.raw_print("To connect another client to this kernel, use:") io.raw_print("-e --xreq {0} --sub {1} --rep {2} --hb {3}".format( xrep_port, pub_port, req_port, hb_port)) # Redirect input streams and set a display hook. if out_stream_factory: sys.stdout = out_stream_factory(session, pub_socket, u'stdout') sys.stderr = out_stream_factory(session, pub_socket, u'stderr') if display_hook_factory: sys.displayhook = display_hook_factory(session, pub_socket) # Create the kernel. kernel = kernel_factory(session=session, reply_socket=reply_socket, pub_socket=pub_socket, req_socket=req_socket) kernel.record_ports(xrep_port=xrep_port, pub_port=pub_port, req_port=req_port, hb_port=hb_port) return kernel
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
class Kernel(Configurable): #--------------------------------------------------------------------------- # Kernel interface #--------------------------------------------------------------------------- shell = Instance('IPython.core.interactiveshell.InteractiveShellABC') session = Instance(Session) reply_socket = Instance('zmq.Socket') pub_socket = Instance('zmq.Socket') req_socket = Instance('zmq.Socket') # Private interface # 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 = None def __init__(self, **kwargs): super(Kernel, self).__init__(**kwargs) # Before we even start up the shell, register *first* our exit handlers # so they come before the shell's atexit.register(self._at_shutdown) # Initialize the InteractiveShell subclass self.shell = ZMQInteractiveShell.instance() self.shell.displayhook.session = self.session self.shell.displayhook.pub_socket = self.pub_socket # TMP - hack while developing self.shell._reply_content = None # Build dict of handlers for message types msg_types = [ 'execute_request', 'complete_request', 'object_info_request', 'history_request', 'connect_request', 'shutdown_request'] self.handlers = {} for msg_type in msg_types: self.handlers[msg_type] = getattr(self, msg_type) def do_one_iteration(self): """Do one iteration of the kernel's evaluation loop. """ try: ident = self.reply_socket.recv(zmq.NOBLOCK) except zmq.ZMQError, e: if e.errno == zmq.EAGAIN: return else: raise # This assert will raise in versions of zeromq 2.0.7 and lesser. # We now require 2.0.8 or above, so we can uncomment for safety. assert self.reply_socket.rcvmore(), "Missing message part." msg = self.reply_socket.recv_json() # 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. # Eventually we'll move these from stdout to a logger. io.raw_print('\n*** MESSAGE TYPE:', msg['msg_type'], '***') io.raw_print(' Content: ', msg['content'], '\n --->\n ', sep='', end='') # Find and call actual handler for message handler = self.handlers.get(msg['msg_type'], None) if handler is None: io.raw_print_err("UNKNOWN MESSAGE TYPE:", msg) else: handler(ident, msg) # Check whether we should exit, in case the incoming message set the # exit flag on if self.shell.exit_now: io.raw_print('\nExiting IPython kernel...') # We do a normal, clean exit, which allows any actions registered # via atexit (such as history saving) to take place. sys.exit(0)
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 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)
def complete_request(self, ident, parent): txt, matches = self._complete(parent) matches = {"matches": matches, "matched_text": txt, "status": "ok"} completion_msg = self.session.send(self.reply_socket, "complete_reply", matches, parent, ident) io.raw_print(completion_msg)
def execute_request(self, ident, parent): status_msg = self.session.msg( u'status', {u'execution_state':u'busy'}, parent=parent ) self.pub_socket.send_json(status_msg) try: content = parent[u'content'] code = content[u'code'] silent = content[u'silent'] except: io.raw_print_err("Got bad msg: ") io.raw_print_err(Message(parent)) return shell = self.shell # we'll need this a lot here # Replace raw_input. Note that is not sufficient to replace # raw_input in the user namespace. raw_input = lambda prompt='': self._raw_input(prompt, ident, parent) __builtin__.raw_input = raw_input # Set the parent message of the display hook and out streams. shell.displayhook.set_parent(parent) sys.stdout.set_parent(parent) sys.stderr.set_parent(parent) # Re-broadcast our input for the benefit of listening clients, and # start computing output if not silent: self._publish_pyin(code, parent) reply_content = {} try: if silent: # run_code uses 'exec' mode, so no displayhook will fire, and it # doesn't call logging or history manipulations. Print # statements in that code will obviously still execute. shell.run_code(code) else: # FIXME: the shell calls the exception handler itself. shell._reply_content = None shell.run_cell(code) except: status = u'error' # FIXME: this code right now isn't being used yet by default, # because the runlines() 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' 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) # At this point, we can tell whether the main code execution succeeded # or not. If it did, we proceed to evaluate user_variables/expressions if reply_content['status'] == 'ok': reply_content[u'user_variables'] = \ shell.user_variables(content[u'user_variables']) reply_content[u'user_expressions'] = \ shell.user_expressions(content[u'user_expressions']) else: # If there was an error, don't even try to compute variables or # expressions reply_content[u'user_variables'] = {} 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() # Send the reply. reply_msg = self.session.msg(u'execute_reply', reply_content, parent) io.raw_print(reply_msg) # 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) self.reply_socket.send(ident, zmq.SNDMORE) self.reply_socket.send_json(reply_msg) if reply_msg['content']['status'] == u'error': self._abort_queue() status_msg = self.session.msg( u'status', {u'execution_state':u'idle'}, parent=parent ) self.pub_socket.send_json(status_msg)