Show More
@@ -164,7 +164,10 b' def loop_cocoa(kernel):' | |||||
164 | # but still need a Poller for when there are no active windows, |
|
164 | # but still need a Poller for when there are no active windows, | |
165 | # during which time mainloop() returns immediately |
|
165 | # during which time mainloop() returns immediately | |
166 | poller = zmq.Poller() |
|
166 | poller = zmq.Poller() | |
167 | poller.register(kernel.shell_socket, zmq.POLLIN) |
|
167 | if kernel.control_stream: | |
|
168 | poller.register(kernel.control_stream.socket, zmq.POLLIN) | |||
|
169 | for stream in kernel.shell_streams: | |||
|
170 | poller.register(stream.socket, zmq.POLLIN) | |||
168 |
|
171 | |||
169 | while True: |
|
172 | while True: | |
170 | try: |
|
173 | try: |
@@ -26,11 +26,13 b' import uuid' | |||||
26 |
|
26 | |||
27 | from datetime import datetime |
|
27 | from datetime import datetime | |
28 | from signal import ( |
|
28 | from signal import ( | |
29 | signal, default_int_handler, SIGINT, SIG_IGN |
|
29 | signal, getsignal, default_int_handler, SIGINT, SIG_IGN | |
30 | ) |
|
30 | ) | |
31 |
|
31 | |||
32 | # System library imports |
|
32 | # System library imports | |
33 | import zmq |
|
33 | import zmq | |
|
34 | from zmq.eventloop import ioloop | |||
|
35 | from zmq.eventloop.zmqstream import ZMQStream | |||
34 |
|
36 | |||
35 | # Local imports |
|
37 | # Local imports | |
36 | from IPython.core import pylabtools |
|
38 | from IPython.core import pylabtools | |
@@ -68,13 +70,18 b' class Kernel(Configurable):' | |||||
68 |
|
70 | |||
69 | # attribute to override with a GUI |
|
71 | # attribute to override with a GUI | |
70 | eventloop = Any(None) |
|
72 | eventloop = Any(None) | |
|
73 | def _eventloop_changed(self, name, old, new): | |||
|
74 | """schedule call to eventloop from IOLoop""" | |||
|
75 | loop = ioloop.IOLoop.instance() | |||
|
76 | loop.add_timeout(time.time()+0.1, self.enter_eventloop) | |||
71 |
|
77 | |||
72 | shell = Instance('IPython.core.interactiveshell.InteractiveShellABC') |
|
78 | shell = Instance('IPython.core.interactiveshell.InteractiveShellABC') | |
73 | session = Instance(Session) |
|
79 | session = Instance(Session) | |
74 | profile_dir = Instance('IPython.core.profiledir.ProfileDir') |
|
80 | profile_dir = Instance('IPython.core.profiledir.ProfileDir') | |
75 |
shell_s |
|
81 | shell_streams = List() | |
76 |
control_s |
|
82 | control_stream = Instance(ZMQStream) | |
77 |
iopub_s |
|
83 | iopub_stream = Instance(ZMQStream) | |
|
84 | stdin_socket = Instance(zmq.Socket) | |||
78 | log = Instance(logging.Logger) |
|
85 | log = Instance(logging.Logger) | |
79 |
|
86 | |||
80 | user_module = Instance('types.ModuleType') |
|
87 | user_module = Instance('types.ModuleType') | |
@@ -127,14 +134,9 b' class Kernel(Configurable):' | |||||
127 | aborted = Set() |
|
134 | aborted = Set() | |
128 |
|
135 | |||
129 |
|
136 | |||
130 |
|
||||
131 | def __init__(self, **kwargs): |
|
137 | def __init__(self, **kwargs): | |
132 | super(Kernel, self).__init__(**kwargs) |
|
138 | super(Kernel, self).__init__(**kwargs) | |
133 |
|
139 | |||
134 | # Before we even start up the shell, register *first* our exit handlers |
|
|||
135 | # so they come before the shell's |
|
|||
136 | atexit.register(self._at_shutdown) |
|
|||
137 |
|
||||
138 | # Initialize the InteractiveShell subclass |
|
140 | # Initialize the InteractiveShell subclass | |
139 | self.shell = ZMQInteractiveShell.instance(config=self.config, |
|
141 | self.shell = ZMQInteractiveShell.instance(config=self.config, | |
140 | profile_dir = self.profile_dir, |
|
142 | profile_dir = self.profile_dir, | |
@@ -142,9 +144,9 b' class Kernel(Configurable):' | |||||
142 | user_ns = self.user_ns, |
|
144 | user_ns = self.user_ns, | |
143 | ) |
|
145 | ) | |
144 | self.shell.displayhook.session = self.session |
|
146 | self.shell.displayhook.session = self.session | |
145 | self.shell.displayhook.pub_socket = self.iopub_socket |
|
147 | self.shell.displayhook.pub_socket = self.iopub_stream.socket | |
146 | self.shell.display_pub.session = self.session |
|
148 | self.shell.display_pub.session = self.session | |
147 | self.shell.display_pub.pub_socket = self.iopub_socket |
|
149 | self.shell.display_pub.pub_socket = self.iopub_stream.socket | |
148 |
|
150 | |||
149 | # TMP - hack while developing |
|
151 | # TMP - hack while developing | |
150 | self.shell._reply_content = None |
|
152 | self.shell._reply_content = None | |
@@ -155,135 +157,111 b' class Kernel(Configurable):' | |||||
155 | 'connect_request', 'shutdown_request', |
|
157 | 'connect_request', 'shutdown_request', | |
156 | 'apply_request', |
|
158 | 'apply_request', | |
157 | ] |
|
159 | ] | |
158 | self.handlers = {} |
|
160 | self.shell_handlers = {} | |
159 | for msg_type in msg_types: |
|
161 | for msg_type in msg_types: | |
160 | self.handlers[msg_type] = getattr(self, msg_type) |
|
162 | self.shell_handlers[msg_type] = getattr(self, msg_type) | |
161 |
|
163 | |||
162 | control_msg_types = [ 'clear_request', 'abort_request' ] |
|
164 | control_msg_types = [ 'clear_request', 'abort_request' ] | |
163 | self.control_handlers = {} |
|
165 | self.control_handlers = {} | |
164 | for msg_type in control_msg_types: |
|
166 | for msg_type in control_msg_types: | |
165 | self.control_handlers[msg_type] = getattr(self, msg_type) |
|
167 | self.control_handlers[msg_type] = getattr(self, msg_type) | |
166 |
|
168 | |||
167 |
def d |
|
169 | def dispatch_control(self, msg): | |
168 | """Do one iteration of the kernel's evaluation loop. |
|
170 | """dispatch control requests""" | |
169 | """ |
|
171 | idents,msg = self.session.feed_identities(msg, copy=False) | |
170 |
|
172 | try: | ||
171 | # always flush control socket first |
|
173 | msg = self.session.unserialize(msg, content=True, copy=False) | |
172 |
|
|
174 | except: | |
173 | if self.control_socket is None: |
|
175 | self.log.error("Invalid Control Message", exc_info=True) | |
174 |
|
|
176 | return | |
175 | try: |
|
|||
176 | idents,msg = self.session.recv(self.control_socket, zmq.NOBLOCK) |
|
|||
177 | except Exception: |
|
|||
178 | self.log.warn("Invalid Control Message:", exc_info=True) |
|
|||
179 | continue |
|
|||
180 | if msg is None: |
|
|||
181 | break |
|
|||
182 | self.dispatch_message(self.control_socket, idents, msg, self.control_handlers) |
|
|||
183 |
|
||||
184 | for socket in self.shell_sockets: |
|
|||
185 | try: |
|
|||
186 | idents,msg = self.session.recv(socket, zmq.NOBLOCK, copy=False) |
|
|||
187 | except Exception: |
|
|||
188 | self.log.warn("Invalid Message:", exc_info=True) |
|
|||
189 | continue |
|
|||
190 |
|
||||
191 | if msg is None: |
|
|||
192 | continue |
|
|||
193 |
|
||||
194 | self.dispatch_message(socket, idents, msg, self.handlers) |
|
|||
195 |
|
177 | |||
196 | def dispatch_message(self, socket, idents, msg, handlers): |
|
178 | self.log.debug("Control received: %s", msg) | |
197 | msg_type = msg['header']['msg_type'] |
|
|||
198 | msg_id = msg['header']['msg_id'] |
|
|||
199 |
|
179 | |||
200 | # This assert will raise in versions of zeromq 2.0.7 and lesser. |
|
180 | header = msg['header'] | |
201 | # We now require 2.0.8 or above, so we can uncomment for safety. |
|
181 | msg_id = header['msg_id'] | |
202 | # print(ident,msg, file=sys.__stdout__) |
|
182 | msg_type = header['msg_type'] | |
203 | assert idents is not None, "Missing message part." |
|
|||
204 |
|
183 | |||
|
184 | handler = self.control_handlers.get(msg_type, None) | |||
|
185 | if handler is None: | |||
|
186 | self.log.error("UNKNOWN CONTROL MESSAGE TYPE: %r", msg_type) | |||
|
187 | else: | |||
|
188 | handler(self.control_stream, idents, msg) | |||
|
189 | ||||
|
190 | def dispatch_shell(self, stream, msg): | |||
|
191 | """dispatch shell requests""" | |||
|
192 | # flush control requests first | |||
|
193 | if self.control_stream: | |||
|
194 | self.control_stream.flush() | |||
|
195 | ||||
|
196 | idents,msg = self.session.feed_identities(msg, copy=False) | |||
|
197 | try: | |||
|
198 | msg = self.session.unserialize(msg, content=True, copy=False) | |||
|
199 | except: | |||
|
200 | self.log.error("Invalid Message", exc_info=True) | |||
|
201 | return | |||
|
202 | ||||
|
203 | header = msg['header'] | |||
|
204 | msg_id = header['msg_id'] | |||
|
205 | msg_type = msg['header']['msg_type'] | |||
|
206 | ||||
205 | # Print some info about this message and leave a '--->' marker, so it's |
|
207 | # Print some info about this message and leave a '--->' marker, so it's | |
206 | # easier to trace visually the message chain when debugging. Each |
|
208 | # easier to trace visually the message chain when debugging. Each | |
207 | # handler prints its message at the end. |
|
209 | # handler prints its message at the end. | |
208 | self.log.debug('\n*** MESSAGE TYPE:%s***', msg_type) |
|
210 | self.log.debug('\n*** MESSAGE TYPE:%s***', msg_type) | |
209 | self.log.debug(' Content: %s\n --->\n ', msg['content']) |
|
211 | self.log.debug(' Content: %s\n --->\n ', msg['content']) | |
210 |
|
212 | |||
211 | # check if request has been aborted |
|
|||
212 | if msg_id in self.aborted: |
|
213 | if msg_id in self.aborted: | |
213 | self.aborted.remove(msg_id) |
|
214 | self.aborted.remove(msg_id) | |
214 | # is it safe to assume a msg_id will not be resubmitted? |
|
215 | # is it safe to assume a msg_id will not be resubmitted? | |
215 | reply_type = msg_type.split('_')[0] + '_reply' |
|
216 | reply_type = msg_type.split('_')[0] + '_reply' | |
216 | status = {'status' : 'aborted'} |
|
217 | status = {'status' : 'aborted'} | |
217 |
reply_msg = self.session.send(s |
|
218 | reply_msg = self.session.send(stream, reply_type, subheader=status, | |
218 | content=status, parent=msg, ident=idents) |
|
219 | content=status, parent=msg, ident=idents) | |
219 | return |
|
220 | return | |
220 |
|
221 | |||
221 |
|
222 | handler = self.shell_handlers.get(msg_type, None) | ||
222 | # Find and call actual handler for message |
|
|||
223 | handler = handlers.get(msg_type, None) |
|
|||
224 | if handler is None: |
|
223 | if handler is None: | |
225 |
self.log.error("UNKNOWN MESSAGE TYPE: % |
|
224 | self.log.error("UNKNOWN MESSAGE TYPE: %r", msg_type) | |
226 | else: |
|
225 | else: | |
227 | handler(socket, idents, msg) |
|
226 | # ensure default_int_handler during handler call | |
|
227 | sig = signal(SIGINT, default_int_handler) | |||
|
228 | try: | |||
|
229 | handler(stream, idents, msg) | |||
|
230 | finally: | |||
|
231 | signal(SIGINT, sig) | |||
|
232 | ||||
|
233 | def enter_eventloop(self): | |||
|
234 | """enter eventloop""" | |||
|
235 | self.log.critical("entering eventloop") | |||
|
236 | # restore default_int_handler | |||
|
237 | signal(SIGINT, default_int_handler) | |||
|
238 | self.eventloop(self) | |||
|
239 | self.log.critical("exiting eventloop") | |||
|
240 | # if eventloop exits, IOLoop should stop | |||
|
241 | ioloop.IOLoop.instance().stop() | |||
228 |
|
242 | |||
229 | # Check whether we should exit, in case the incoming message set the |
|
243 | def start(self): | |
230 | # exit flag on |
|
244 | """register dispatchers for streams""" | |
231 |
if self. |
|
245 | if self.control_stream: | |
232 | self.log.debug('\nExiting IPython kernel...') |
|
246 | self.control_stream.on_recv(self.dispatch_control, copy=False) | |
233 | # We do a normal, clean exit, which allows any actions registered |
|
|||
234 | # via atexit (such as history saving) to take place. |
|
|||
235 | sys.exit(0) |
|
|||
236 |
|
247 | |||
|
248 | def make_dispatcher(stream): | |||
|
249 | def dispatcher(msg): | |||
|
250 | return self.dispatch_shell(stream, msg) | |||
|
251 | return dispatcher | |||
237 |
|
252 | |||
238 | def start(self): |
|
253 | for s in self.shell_streams: | |
239 | """ Start the kernel main loop. |
|
254 | s.on_recv(make_dispatcher(s), copy=False) | |
240 | """ |
|
255 | ||
241 | # a KeyboardInterrupt (SIGINT) can occur on any python statement, so |
|
256 | def do_one_iteration(self): | |
242 | # let's ignore (SIG_IGN) them until we're in a place to handle them properly |
|
257 | """step eventloop just once""" | |
243 | signal(SIGINT,SIG_IGN) |
|
258 | if self.control_stream: | |
244 | poller = zmq.Poller() |
|
259 | self.control_stream.flush() | |
245 |
for s |
|
260 | for stream in self.shell_streams: | |
246 | poller.register(socket, zmq.POLLIN) |
|
261 | # handle at most one request per iteration | |
247 | if self.control_socket: |
|
262 | stream.flush(zmq.POLLIN, 1) | |
248 | poller.register(self.control_socket, zmq.POLLIN) |
|
263 | stream.flush(zmq.POLLOUT) | |
249 |
|
264 | self.iopub_stream.flush() | ||
250 | # loop while self.eventloop has not been overridden |
|
|||
251 | while self.eventloop is None: |
|
|||
252 | try: |
|
|||
253 | # scale by extra factor of 10, because there is no |
|
|||
254 | # reason for this to be anything less than ~ 0.1s |
|
|||
255 | # since it is a real poller and will respond |
|
|||
256 | # to events immediately |
|
|||
257 |
|
||||
258 | # double nested try/except, to properly catch KeyboardInterrupt |
|
|||
259 | # due to pyzmq Issue #130 |
|
|||
260 | try: |
|
|||
261 | poller.poll(10*1000*self._poll_interval) |
|
|||
262 | # restore raising of KeyboardInterrupt |
|
|||
263 | signal(SIGINT, default_int_handler) |
|
|||
264 | self.do_one_iteration() |
|
|||
265 | except: |
|
|||
266 | raise |
|
|||
267 | finally: |
|
|||
268 | # prevent raising of KeyboardInterrupt |
|
|||
269 | signal(SIGINT,SIG_IGN) |
|
|||
270 | except KeyboardInterrupt: |
|
|||
271 | # Ctrl-C shouldn't crash the kernel |
|
|||
272 | io.raw_print("KeyboardInterrupt caught in kernel") |
|
|||
273 | # stop ignoring sigint, now that we are out of our own loop, |
|
|||
274 | # we don't want to prevent future code from handling it |
|
|||
275 | signal(SIGINT, default_int_handler) |
|
|||
276 | while self.eventloop is not None: |
|
|||
277 | try: |
|
|||
278 | self.eventloop(self) |
|
|||
279 | except KeyboardInterrupt: |
|
|||
280 | # Ctrl-C shouldn't crash the kernel |
|
|||
281 | io.raw_print("KeyboardInterrupt caught in kernel") |
|
|||
282 | continue |
|
|||
283 | else: |
|
|||
284 | # eventloop exited cleanly, this means we should stop (right?) |
|
|||
285 | self.eventloop = None |
|
|||
286 | break |
|
|||
287 |
|
265 | |||
288 |
|
266 | |||
289 | def record_ports(self, ports): |
|
267 | def record_ports(self, ports): | |
@@ -301,12 +279,12 b' class Kernel(Configurable):' | |||||
301 | def _publish_pyin(self, code, parent, execution_count): |
|
279 | def _publish_pyin(self, code, parent, execution_count): | |
302 | """Publish the code request on the pyin stream.""" |
|
280 | """Publish the code request on the pyin stream.""" | |
303 |
|
281 | |||
304 |
self.session.send(self.iopub_s |
|
282 | self.session.send(self.iopub_stream, u'pyin', {u'code':code, | |
305 | u'execution_count': execution_count}, parent=parent) |
|
283 | u'execution_count': execution_count}, parent=parent) | |
306 |
|
284 | |||
307 |
def execute_request(self, s |
|
285 | def execute_request(self, stream, ident, parent): | |
308 |
|
286 | |||
309 |
self.session.send(self.iopub_s |
|
287 | self.session.send(self.iopub_stream, | |
310 | u'status', |
|
288 | u'status', | |
311 | {u'execution_state':u'busy'}, |
|
289 | {u'execution_state':u'busy'}, | |
312 | parent=parent ) |
|
290 | parent=parent ) | |
@@ -413,40 +391,40 b' class Kernel(Configurable):' | |||||
413 |
|
391 | |||
414 | # Send the reply. |
|
392 | # Send the reply. | |
415 | reply_content = json_clean(reply_content) |
|
393 | reply_content = json_clean(reply_content) | |
416 |
reply_msg = self.session.send(s |
|
394 | reply_msg = self.session.send(stream, u'execute_reply', | |
417 | reply_content, parent, ident=ident) |
|
395 | reply_content, parent, ident=ident) | |
418 | self.log.debug("%s", reply_msg) |
|
396 | self.log.debug("%s", reply_msg) | |
419 |
|
397 | |||
420 | if reply_msg['content']['status'] == u'error': |
|
398 | if reply_msg['content']['status'] == u'error': | |
421 | self._abort_queues() |
|
399 | self._abort_queues() | |
422 |
|
400 | |||
423 |
self.session.send(self.iopub_s |
|
401 | self.session.send(self.iopub_stream, | |
424 | u'status', |
|
402 | u'status', | |
425 | {u'execution_state':u'idle'}, |
|
403 | {u'execution_state':u'idle'}, | |
426 | parent=parent ) |
|
404 | parent=parent ) | |
427 |
|
405 | |||
428 |
def complete_request(self, s |
|
406 | def complete_request(self, stream, ident, parent): | |
429 | txt, matches = self._complete(parent) |
|
407 | txt, matches = self._complete(parent) | |
430 | matches = {'matches' : matches, |
|
408 | matches = {'matches' : matches, | |
431 | 'matched_text' : txt, |
|
409 | 'matched_text' : txt, | |
432 | 'status' : 'ok'} |
|
410 | 'status' : 'ok'} | |
433 | matches = json_clean(matches) |
|
411 | matches = json_clean(matches) | |
434 |
completion_msg = self.session.send(s |
|
412 | completion_msg = self.session.send(stream, 'complete_reply', | |
435 | matches, parent, ident) |
|
413 | matches, parent, ident) | |
436 | self.log.debug("%s", completion_msg) |
|
414 | self.log.debug("%s", completion_msg) | |
437 |
|
415 | |||
438 |
def object_info_request(self, s |
|
416 | def object_info_request(self, stream, ident, parent): | |
439 | content = parent['content'] |
|
417 | content = parent['content'] | |
440 | object_info = self.shell.object_inspect(content['oname'], |
|
418 | object_info = self.shell.object_inspect(content['oname'], | |
441 | detail_level = content.get('detail_level', 0) |
|
419 | detail_level = content.get('detail_level', 0) | |
442 | ) |
|
420 | ) | |
443 | # Before we send this object over, we scrub it for JSON usage |
|
421 | # Before we send this object over, we scrub it for JSON usage | |
444 | oinfo = json_clean(object_info) |
|
422 | oinfo = json_clean(object_info) | |
445 |
msg = self.session.send(s |
|
423 | msg = self.session.send(stream, 'object_info_reply', | |
446 | oinfo, parent, ident) |
|
424 | oinfo, parent, ident) | |
447 | self.log.debug("%s", msg) |
|
425 | self.log.debug("%s", msg) | |
448 |
|
426 | |||
449 |
def history_request(self, s |
|
427 | def history_request(self, stream, ident, parent): | |
450 | # We need to pull these out, as passing **kwargs doesn't work with |
|
428 | # We need to pull these out, as passing **kwargs doesn't work with | |
451 | # unicode keys before Python 2.6.5. |
|
429 | # unicode keys before Python 2.6.5. | |
452 | hist_access_type = parent['content']['hist_access_type'] |
|
430 | hist_access_type = parent['content']['hist_access_type'] | |
@@ -474,30 +452,35 b' class Kernel(Configurable):' | |||||
474 | hist = list(hist) |
|
452 | hist = list(hist) | |
475 | content = {'history' : hist} |
|
453 | content = {'history' : hist} | |
476 | content = json_clean(content) |
|
454 | content = json_clean(content) | |
477 |
msg = self.session.send(s |
|
455 | msg = self.session.send(stream, 'history_reply', | |
478 | content, parent, ident) |
|
456 | content, parent, ident) | |
479 | self.log.debug("Sending history reply with %i entries", len(hist)) |
|
457 | self.log.debug("Sending history reply with %i entries", len(hist)) | |
480 |
|
458 | |||
481 |
def connect_request(self, s |
|
459 | def connect_request(self, stream, ident, parent): | |
482 | if self._recorded_ports is not None: |
|
460 | if self._recorded_ports is not None: | |
483 | content = self._recorded_ports.copy() |
|
461 | content = self._recorded_ports.copy() | |
484 | else: |
|
462 | else: | |
485 | content = {} |
|
463 | content = {} | |
486 |
msg = self.session.send(s |
|
464 | msg = self.session.send(stream, 'connect_reply', | |
487 | content, parent, ident) |
|
465 | content, parent, ident) | |
488 | self.log.debug("%s", msg) |
|
466 | self.log.debug("%s", msg) | |
489 |
|
467 | |||
490 | def shutdown_request(self, ident, parent): |
|
468 | def shutdown_request(self, stream, ident, parent): | |
491 | self.shell.exit_now = True |
|
469 | self.shell.exit_now = True | |
492 | self._shutdown_message = self.session.msg(u'shutdown_reply', |
|
470 | self._shutdown_message = self.session.msg(u'shutdown_reply', | |
493 |
parent['content'], parent |
|
471 | parent['content'], parent | |
494 |
|
|
472 | ) | |
|
473 | # self.session.send(stream, self._shutdown_message, ident=ident) | |||
|
474 | ||||
|
475 | self._at_shutdown() | |||
|
476 | # call sys.exit after a short delay | |||
|
477 | ioloop.IOLoop.instance().add_timeout(time.time()+0.05, lambda : sys.exit(0)) | |||
495 |
|
478 | |||
496 | #--------------------------------------------------------------------------- |
|
479 | #--------------------------------------------------------------------------- | |
497 | # Engine methods |
|
480 | # Engine methods | |
498 | #--------------------------------------------------------------------------- |
|
481 | #--------------------------------------------------------------------------- | |
499 |
|
482 | |||
500 |
def apply_request(self, s |
|
483 | def apply_request(self, stream, ident, parent): | |
501 | try: |
|
484 | try: | |
502 | content = parent[u'content'] |
|
485 | content = parent[u'content'] | |
503 | bufs = parent[u'buffers'] |
|
486 | bufs = parent[u'buffers'] | |
@@ -552,7 +535,7 b' class Kernel(Configurable):' | |||||
552 | except: |
|
535 | except: | |
553 | exc_content = self._wrap_exception('apply') |
|
536 | exc_content = self._wrap_exception('apply') | |
554 | # exc_msg = self.session.msg(u'pyerr', exc_content, parent) |
|
537 | # exc_msg = self.session.msg(u'pyerr', exc_content, parent) | |
555 |
self.session.send(self.iopub_s |
|
538 | self.session.send(self.iopub_stream, u'pyerr', exc_content, parent=parent, | |
556 | ident=py3compat.str_to_bytes('%s.pyerr'%self.prefix)) |
|
539 | ident=py3compat.str_to_bytes('%s.pyerr'%self.prefix)) | |
557 | reply_content = exc_content |
|
540 | reply_content = exc_content | |
558 | result_buf = [] |
|
541 | result_buf = [] | |
@@ -569,14 +552,14 b' class Kernel(Configurable):' | |||||
569 | sys.stdout.flush() |
|
552 | sys.stdout.flush() | |
570 | sys.stderr.flush() |
|
553 | sys.stderr.flush() | |
571 |
|
554 | |||
572 |
reply_msg = self.session.send(s |
|
555 | reply_msg = self.session.send(stream, u'apply_reply', reply_content, | |
573 | parent=parent, ident=ident,buffers=result_buf, subheader=sub) |
|
556 | parent=parent, ident=ident,buffers=result_buf, subheader=sub) | |
574 |
|
557 | |||
575 | #--------------------------------------------------------------------------- |
|
558 | #--------------------------------------------------------------------------- | |
576 | # Control messages |
|
559 | # Control messages | |
577 | #--------------------------------------------------------------------------- |
|
560 | #--------------------------------------------------------------------------- | |
578 |
|
561 | |||
579 |
def abort_request(self, s |
|
562 | def abort_request(self, stream, ident, parent): | |
580 | """abort a specifig msg by id""" |
|
563 | """abort a specifig msg by id""" | |
581 | msg_ids = parent['content'].get('msg_ids', None) |
|
564 | msg_ids = parent['content'].get('msg_ids', None) | |
582 | if isinstance(msg_ids, basestring): |
|
565 | if isinstance(msg_ids, basestring): | |
@@ -587,14 +570,14 b' class Kernel(Configurable):' | |||||
587 | self.aborted.add(str(mid)) |
|
570 | self.aborted.add(str(mid)) | |
588 |
|
571 | |||
589 | content = dict(status='ok') |
|
572 | content = dict(status='ok') | |
590 |
reply_msg = self.session.send(s |
|
573 | reply_msg = self.session.send(stream, 'abort_reply', content=content, | |
591 | parent=parent, ident=ident) |
|
574 | parent=parent, ident=ident) | |
592 | self.log.debug("%s", reply_msg) |
|
575 | self.log.debug("%s", reply_msg) | |
593 |
|
576 | |||
594 |
def clear_request(self, s |
|
577 | def clear_request(self, stream, idents, parent): | |
595 | """Clear our namespace.""" |
|
578 | """Clear our namespace.""" | |
596 | self.user_ns = {} |
|
579 | self.user_ns = {} | |
597 |
msg = self.session.send(s |
|
580 | msg = self.session.send(stream, 'clear_reply', ident=idents, parent=parent, | |
598 | content = dict(status='ok')) |
|
581 | content = dict(status='ok')) | |
599 | self._initial_exec_lines() |
|
582 | self._initial_exec_lines() | |
600 |
|
583 | |||
@@ -604,13 +587,13 b' class Kernel(Configurable):' | |||||
604 | #--------------------------------------------------------------------------- |
|
587 | #--------------------------------------------------------------------------- | |
605 |
|
588 | |||
606 | def _abort_queues(self): |
|
589 | def _abort_queues(self): | |
607 |
for s |
|
590 | for stream in self.shell_streams: | |
608 |
if s |
|
591 | if stream: | |
609 |
self._abort_queue(s |
|
592 | self._abort_queue(stream) | |
610 |
|
593 | |||
611 |
def _abort_queue(self, s |
|
594 | def _abort_queue(self, stream): | |
612 | while True: |
|
595 | while True: | |
613 |
idents,msg = self.session.recv(s |
|
596 | idents,msg = self.session.recv(stream, zmq.NOBLOCK, content=True) | |
614 | if msg is None: |
|
597 | if msg is None: | |
615 | return |
|
598 | return | |
616 |
|
599 | |||
@@ -619,9 +602,9 b' class Kernel(Configurable):' | |||||
619 | msg_type = msg['header']['msg_type'] |
|
602 | msg_type = msg['header']['msg_type'] | |
620 | reply_type = msg_type.split('_')[0] + '_reply' |
|
603 | reply_type = msg_type.split('_')[0] + '_reply' | |
621 | # reply_msg = self.session.msg(reply_type, {'status' : 'aborted'}, msg) |
|
604 | # reply_msg = self.session.msg(reply_type, {'status' : 'aborted'}, msg) | |
622 |
# self.reply_s |
|
605 | # self.reply_stream.send(ident,zmq.SNDMORE) | |
623 |
# self.reply_s |
|
606 | # self.reply_stream.send_json(reply_msg) | |
624 |
reply_msg = self.session.send(s |
|
607 | reply_msg = self.session.send(stream, reply_type, | |
625 | content={'status' : 'aborted'}, parent=msg, ident=idents) |
|
608 | content={'status' : 'aborted'}, parent=msg, ident=idents) | |
626 | self.log.debug("%s", reply_msg) |
|
609 | self.log.debug("%s", reply_msg) | |
627 | # We need to wait a bit for requests to come in. This can probably |
|
610 | # We need to wait a bit for requests to come in. This can probably | |
@@ -712,12 +695,9 b' class Kernel(Configurable):' | |||||
712 | """ |
|
695 | """ | |
713 | # io.rprint("Kernel at_shutdown") # dbg |
|
696 | # io.rprint("Kernel at_shutdown") # dbg | |
714 | if self._shutdown_message is not None: |
|
697 | if self._shutdown_message is not None: | |
715 |
self.session.send(self. |
|
698 | self.session.send(self.iopub_stream, self._shutdown_message) | |
716 | self.session.send(self.iopub_socket, self._shutdown_message) |
|
|||
717 | self.log.debug("%s", self._shutdown_message) |
|
699 | self.log.debug("%s", self._shutdown_message) | |
718 | # A very short sleep to give zmq time to flush its message buffers |
|
700 | [ s.flush(zmq.POLLOUT) for s in self.shell_streams + [self.iopub_stream] ] | |
719 | # before Python truly shuts down. |
|
|||
720 | time.sleep(0.01) |
|
|||
721 |
|
701 | |||
722 | #----------------------------------------------------------------------------- |
|
702 | #----------------------------------------------------------------------------- | |
723 | # Aliases and Flags for the IPKernelApp |
|
703 | # Aliases and Flags for the IPKernelApp | |
@@ -770,10 +750,13 b' class IPKernelApp(KernelApp, InteractiveShellApp):' | |||||
770 | self.init_code() |
|
750 | self.init_code() | |
771 |
|
751 | |||
772 | def init_kernel(self): |
|
752 | def init_kernel(self): | |
|
753 | ||||
|
754 | shell_stream = ZMQStream(self.shell_socket) | |||
|
755 | iopub_stream = ZMQStream(self.iopub_socket) | |||
773 |
|
756 | |||
774 | kernel = Kernel(config=self.config, session=self.session, |
|
757 | kernel = Kernel(config=self.config, session=self.session, | |
775 |
shell_s |
|
758 | shell_streams=[shell_stream], | |
776 |
iopub_s |
|
759 | iopub_stream=iopub_stream, | |
777 | stdin_socket=self.stdin_socket, |
|
760 | stdin_socket=self.stdin_socket, | |
778 | log=self.log, |
|
761 | log=self.log, | |
779 | profile_dir=self.profile_dir, |
|
762 | profile_dir=self.profile_dir, |
@@ -15,15 +15,17 b' Authors' | |||||
15 | # Imports |
|
15 | # Imports | |
16 | #----------------------------------------------------------------------------- |
|
16 | #----------------------------------------------------------------------------- | |
17 |
|
17 | |||
18 |
# Standard library imports |
|
18 | # Standard library imports | |
19 | import json |
|
19 | import json | |
20 | import os |
|
20 | import os | |
21 | import sys |
|
21 | import sys | |
|
22 | import signal | |||
22 |
|
23 | |||
23 |
# System library imports |
|
24 | # System library imports | |
24 | import zmq |
|
25 | import zmq | |
|
26 | from zmq.eventloop import ioloop | |||
25 |
|
27 | |||
26 |
# IPython imports |
|
28 | # IPython imports | |
27 | from IPython.core.ultratb import FormattedTB |
|
29 | from IPython.core.ultratb import FormattedTB | |
28 | from IPython.core.application import ( |
|
30 | from IPython.core.application import ( | |
29 | BaseIPythonApplication, base_flags, base_aliases, catch_config_error |
|
31 | BaseIPythonApplication, base_flags, base_aliases, catch_config_error | |
@@ -267,6 +269,9 b' class KernelApp(BaseIPythonApplication):' | |||||
267 | displayhook_factory = import_item(str(self.displayhook_class)) |
|
269 | displayhook_factory = import_item(str(self.displayhook_class)) | |
268 | sys.displayhook = displayhook_factory(self.session, self.iopub_socket) |
|
270 | sys.displayhook = displayhook_factory(self.session, self.iopub_socket) | |
269 |
|
271 | |||
|
272 | def init_signal(self): | |||
|
273 | signal.signal(signal.SIGINT, signal.SIG_IGN) | |||
|
274 | ||||
270 | def init_kernel(self): |
|
275 | def init_kernel(self): | |
271 | """Create the Kernel object itself""" |
|
276 | """Create the Kernel object itself""" | |
272 | kernel_factory = import_item(str(self.kernel_class)) |
|
277 | kernel_factory = import_item(str(self.kernel_class)) | |
@@ -289,6 +294,7 b' class KernelApp(BaseIPythonApplication):' | |||||
289 | # writing connection file must be *after* init_sockets |
|
294 | # writing connection file must be *after* init_sockets | |
290 | self.write_connection_file() |
|
295 | self.write_connection_file() | |
291 | self.init_io() |
|
296 | self.init_io() | |
|
297 | self.init_signal() | |||
292 | self.init_kernel() |
|
298 | self.init_kernel() | |
293 | # flush stdout/stderr, so that anything written to these streams during |
|
299 | # flush stdout/stderr, so that anything written to these streams during | |
294 | # initialization do not get associated with the first execution request |
|
300 | # initialization do not get associated with the first execution request | |
@@ -299,8 +305,9 b' class KernelApp(BaseIPythonApplication):' | |||||
299 | self.heartbeat.start() |
|
305 | self.heartbeat.start() | |
300 | if self.poller is not None: |
|
306 | if self.poller is not None: | |
301 | self.poller.start() |
|
307 | self.poller.start() | |
|
308 | self.kernel.start() | |||
302 | try: |
|
309 | try: | |
303 | self.kernel.start() |
|
310 | ioloop.IOLoop.instance().start() | |
304 | except KeyboardInterrupt: |
|
311 | except KeyboardInterrupt: | |
305 | pass |
|
312 | pass | |
306 |
|
313 |
General Comments 0
You need to be logged in to leave comments.
Login now