##// END OF EJS Templates
lazy-formatting logs in ipkernel...
MinRK -
Show More
@@ -1,851 +1,851 b''
1 1 #!/usr/bin/env python
2 2 """A simple interactive kernel that talks to a frontend over 0MQ.
3 3
4 4 Things to do:
5 5
6 6 * Implement `set_parent` logic. Right before doing exec, the Kernel should
7 7 call set_parent on all the PUB objects with the message about to be executed.
8 8 * Implement random port and security key logic.
9 9 * Implement control messages.
10 10 * Implement event loop and poll version.
11 11 """
12 12
13 13 #-----------------------------------------------------------------------------
14 14 # Imports
15 15 #-----------------------------------------------------------------------------
16 16 from __future__ import print_function
17 17
18 18 # Standard library imports.
19 19 import __builtin__
20 20 import atexit
21 21 import sys
22 22 import time
23 23 import traceback
24 24 import logging
25 25 from signal import (
26 26 signal, default_int_handler, SIGINT, SIG_IGN
27 27 )
28 28 # System library imports.
29 29 import zmq
30 30
31 31 # Local imports.
32 32 from IPython.core import pylabtools
33 33 from IPython.config.configurable import Configurable
34 34 from IPython.config.application import boolean_flag, catch_config_error
35 35 from IPython.core.application import ProfileDir
36 36 from IPython.core.error import StdinNotImplementedError
37 37 from IPython.core.shellapp import (
38 38 InteractiveShellApp, shell_flags, shell_aliases
39 39 )
40 40 from IPython.utils import io
41 41 from IPython.utils import py3compat
42 42 from IPython.utils.frame import extract_module_locals
43 43 from IPython.utils.jsonutil import json_clean
44 44 from IPython.utils.traitlets import (
45 45 Any, Instance, Float, Dict, CaselessStrEnum
46 46 )
47 47
48 48 from entry_point import base_launch_kernel
49 49 from kernelapp import KernelApp, kernel_flags, kernel_aliases
50 50 from session import Session, Message
51 51 from zmqshell import ZMQInteractiveShell
52 52
53 53
54 54 #-----------------------------------------------------------------------------
55 55 # Main kernel class
56 56 #-----------------------------------------------------------------------------
57 57
58 58 class Kernel(Configurable):
59 59
60 60 #---------------------------------------------------------------------------
61 61 # Kernel interface
62 62 #---------------------------------------------------------------------------
63 63
64 64 # attribute to override with a GUI
65 65 eventloop = Any(None)
66 66
67 67 shell = Instance('IPython.core.interactiveshell.InteractiveShellABC')
68 68 session = Instance(Session)
69 69 profile_dir = Instance('IPython.core.profiledir.ProfileDir')
70 70 shell_socket = Instance('zmq.Socket')
71 71 iopub_socket = Instance('zmq.Socket')
72 72 stdin_socket = Instance('zmq.Socket')
73 73 log = Instance(logging.Logger)
74 74
75 75 user_module = Instance('types.ModuleType')
76 76 def _user_module_changed(self, name, old, new):
77 77 if self.shell is not None:
78 78 self.shell.user_module = new
79 79
80 80 user_ns = Dict(default_value=None)
81 81 def _user_ns_changed(self, name, old, new):
82 82 if self.shell is not None:
83 83 self.shell.user_ns = new
84 84 self.shell.init_user_ns()
85 85
86 86 # Private interface
87 87
88 88 # Time to sleep after flushing the stdout/err buffers in each execute
89 89 # cycle. While this introduces a hard limit on the minimal latency of the
90 90 # execute cycle, it helps prevent output synchronization problems for
91 91 # clients.
92 92 # Units are in seconds. The minimum zmq latency on local host is probably
93 93 # ~150 microseconds, set this to 500us for now. We may need to increase it
94 94 # a little if it's not enough after more interactive testing.
95 95 _execute_sleep = Float(0.0005, config=True)
96 96
97 97 # Frequency of the kernel's event loop.
98 98 # Units are in seconds, kernel subclasses for GUI toolkits may need to
99 99 # adapt to milliseconds.
100 100 _poll_interval = Float(0.05, config=True)
101 101
102 102 # If the shutdown was requested over the network, we leave here the
103 103 # necessary reply message so it can be sent by our registered atexit
104 104 # handler. This ensures that the reply is only sent to clients truly at
105 105 # the end of our shutdown process (which happens after the underlying
106 106 # IPython shell's own shutdown).
107 107 _shutdown_message = None
108 108
109 109 # This is a dict of port number that the kernel is listening on. It is set
110 110 # by record_ports and used by connect_request.
111 111 _recorded_ports = Dict()
112 112
113 113
114 114
115 115 def __init__(self, **kwargs):
116 116 super(Kernel, self).__init__(**kwargs)
117 117
118 118 # Before we even start up the shell, register *first* our exit handlers
119 119 # so they come before the shell's
120 120 atexit.register(self._at_shutdown)
121 121
122 122 # Initialize the InteractiveShell subclass
123 123 self.shell = ZMQInteractiveShell.instance(config=self.config,
124 124 profile_dir = self.profile_dir,
125 125 user_module = self.user_module,
126 126 user_ns = self.user_ns,
127 127 )
128 128 self.shell.displayhook.session = self.session
129 129 self.shell.displayhook.pub_socket = self.iopub_socket
130 130 self.shell.display_pub.session = self.session
131 131 self.shell.display_pub.pub_socket = self.iopub_socket
132 132
133 133 # TMP - hack while developing
134 134 self.shell._reply_content = None
135 135
136 136 # Build dict of handlers for message types
137 137 msg_types = [ 'execute_request', 'complete_request',
138 138 'object_info_request', 'history_request',
139 139 'connect_request', 'shutdown_request',
140 140 'apply_request',
141 141 ]
142 142 self.handlers = {}
143 143 for msg_type in msg_types:
144 144 self.handlers[msg_type] = getattr(self, msg_type)
145
145
146 146 control_msg_types = [ 'clear_request', 'abort_request' ]
147 147 self.control_handlers = {}
148 148 for msg_type in control_msg_types:
149 149 self.control_handlers[msg_type] = getattr(self, msg_type)
150 150
151 151 def do_one_iteration(self):
152 152 """Do one iteration of the kernel's evaluation loop.
153 153 """
154
154
155 155 try:
156 156 ident,msg = self.session.recv(self.shell_socket, zmq.NOBLOCK)
157 157 except Exception:
158 158 self.log.warn("Invalid Message:", exc_info=True)
159 159 return
160 160 if msg is None:
161 161 return
162 162
163 163 msg_type = msg['header']['msg_type']
164 164
165 165 # This assert will raise in versions of zeromq 2.0.7 and lesser.
166 166 # We now require 2.0.8 or above, so we can uncomment for safety.
167 167 # print(ident,msg, file=sys.__stdout__)
168 168 assert ident is not None, "Missing message part."
169 169
170 170 # Print some info about this message and leave a '--->' marker, so it's
171 171 # easier to trace visually the message chain when debugging. Each
172 172 # handler prints its message at the end.
173 self.log.debug('\n*** MESSAGE TYPE:'+str(msg_type)+'***')
174 self.log.debug(' Content: '+str(msg['content'])+'\n --->\n ')
173 self.log.debug('\n*** MESSAGE TYPE:%s***', msg_type)
174 self.log.debug(' Content: %s\n --->\n ', msg['content'])
175 175
176 176 # Find and call actual handler for message
177 177 handler = self.handlers.get(msg_type, None)
178 178 if handler is None:
179 self.log.error("UNKNOWN MESSAGE TYPE:" +str(msg))
179 self.log.error("UNKNOWN MESSAGE TYPE: %s", msg)
180 180 else:
181 181 handler(ident, msg)
182 182
183 183 # Check whether we should exit, in case the incoming message set the
184 184 # exit flag on
185 185 if self.shell.exit_now:
186 186 self.log.debug('\nExiting IPython kernel...')
187 187 # We do a normal, clean exit, which allows any actions registered
188 188 # via atexit (such as history saving) to take place.
189 189 sys.exit(0)
190 190
191 191
192 192 def start(self):
193 193 """ Start the kernel main loop.
194 194 """
195 195 # a KeyboardInterrupt (SIGINT) can occur on any python statement, so
196 196 # let's ignore (SIG_IGN) them until we're in a place to handle them properly
197 197 signal(SIGINT,SIG_IGN)
198 198 poller = zmq.Poller()
199 199 poller.register(self.shell_socket, zmq.POLLIN)
200 200 # loop while self.eventloop has not been overridden
201 201 while self.eventloop is None:
202 202 try:
203 203 # scale by extra factor of 10, because there is no
204 204 # reason for this to be anything less than ~ 0.1s
205 205 # since it is a real poller and will respond
206 206 # to events immediately
207 207
208 208 # double nested try/except, to properly catch KeyboardInterrupt
209 209 # due to pyzmq Issue #130
210 210 try:
211 211 poller.poll(10*1000*self._poll_interval)
212 212 # restore raising of KeyboardInterrupt
213 213 signal(SIGINT, default_int_handler)
214 214 self.do_one_iteration()
215 215 except:
216 216 raise
217 217 finally:
218 218 # prevent raising of KeyboardInterrupt
219 219 signal(SIGINT,SIG_IGN)
220 220 except KeyboardInterrupt:
221 221 # Ctrl-C shouldn't crash the kernel
222 222 io.raw_print("KeyboardInterrupt caught in kernel")
223 223 # stop ignoring sigint, now that we are out of our own loop,
224 224 # we don't want to prevent future code from handling it
225 225 signal(SIGINT, default_int_handler)
226 226 while self.eventloop is not None:
227 227 try:
228 228 self.eventloop(self)
229 229 except KeyboardInterrupt:
230 230 # Ctrl-C shouldn't crash the kernel
231 231 io.raw_print("KeyboardInterrupt caught in kernel")
232 232 continue
233 233 else:
234 234 # eventloop exited cleanly, this means we should stop (right?)
235 235 self.eventloop = None
236 236 break
237 237
238 238
239 239 def record_ports(self, ports):
240 240 """Record the ports that this kernel is using.
241 241
242 242 The creator of the Kernel instance must call this methods if they
243 243 want the :meth:`connect_request` method to return the port numbers.
244 244 """
245 245 self._recorded_ports = ports
246 246
247 247 #---------------------------------------------------------------------------
248 248 # Kernel request handlers
249 249 #---------------------------------------------------------------------------
250 250
251 251 def _publish_pyin(self, code, parent, execution_count):
252 252 """Publish the code request on the pyin stream."""
253 253
254 254 self.session.send(self.iopub_socket, u'pyin', {u'code':code,
255 255 u'execution_count': execution_count}, parent=parent)
256 256
257 257 def execute_request(self, ident, parent):
258 258
259 259 self.session.send(self.iopub_socket,
260 260 u'status',
261 261 {u'execution_state':u'busy'},
262 262 parent=parent )
263 263
264 264 try:
265 265 content = parent[u'content']
266 266 code = content[u'code']
267 267 silent = content[u'silent']
268 268 except:
269 269 self.log.error("Got bad msg: ")
270 self.log.error(str(Message(parent)))
270 self.log.error("%s", parent)
271 271 return
272 272
273 273 shell = self.shell # we'll need this a lot here
274 274
275 275 # Replace raw_input. Note that is not sufficient to replace
276 276 # raw_input in the user namespace.
277 277 if content.get('allow_stdin', False):
278 278 raw_input = lambda prompt='': self._raw_input(prompt, ident, parent)
279 279 else:
280 280 raw_input = lambda prompt='' : self._no_raw_input()
281 281
282 282 if py3compat.PY3:
283 283 __builtin__.input = raw_input
284 284 else:
285 285 __builtin__.raw_input = raw_input
286 286
287 287 # Set the parent message of the display hook and out streams.
288 288 shell.displayhook.set_parent(parent)
289 289 shell.display_pub.set_parent(parent)
290 290 sys.stdout.set_parent(parent)
291 291 sys.stderr.set_parent(parent)
292 292
293 293 # Re-broadcast our input for the benefit of listening clients, and
294 294 # start computing output
295 295 if not silent:
296 296 self._publish_pyin(code, parent, shell.execution_count)
297 297
298 298 reply_content = {}
299 299 try:
300 300 if silent:
301 301 # run_code uses 'exec' mode, so no displayhook will fire, and it
302 302 # doesn't call logging or history manipulations. Print
303 303 # statements in that code will obviously still execute.
304 304 shell.run_code(code)
305 305 else:
306 306 # FIXME: the shell calls the exception handler itself.
307 307 shell.run_cell(code, store_history=True)
308 308 except:
309 309 status = u'error'
310 310 # FIXME: this code right now isn't being used yet by default,
311 311 # because the run_cell() call above directly fires off exception
312 312 # reporting. This code, therefore, is only active in the scenario
313 313 # where runlines itself has an unhandled exception. We need to
314 314 # uniformize this, for all exception construction to come from a
315 315 # single location in the codbase.
316 316 etype, evalue, tb = sys.exc_info()
317 317 tb_list = traceback.format_exception(etype, evalue, tb)
318 318 reply_content.update(shell._showtraceback(etype, evalue, tb_list))
319 319 else:
320 320 status = u'ok'
321 321
322 322 reply_content[u'status'] = status
323 323
324 324 # Return the execution counter so clients can display prompts
325 325 reply_content['execution_count'] = shell.execution_count -1
326 326
327 327 # FIXME - fish exception info out of shell, possibly left there by
328 328 # runlines. We'll need to clean up this logic later.
329 329 if shell._reply_content is not None:
330 330 reply_content.update(shell._reply_content)
331 331 # reset after use
332 332 shell._reply_content = None
333 333
334 334 # At this point, we can tell whether the main code execution succeeded
335 335 # or not. If it did, we proceed to evaluate user_variables/expressions
336 336 if reply_content['status'] == 'ok':
337 337 reply_content[u'user_variables'] = \
338 338 shell.user_variables(content[u'user_variables'])
339 339 reply_content[u'user_expressions'] = \
340 340 shell.user_expressions(content[u'user_expressions'])
341 341 else:
342 342 # If there was an error, don't even try to compute variables or
343 343 # expressions
344 344 reply_content[u'user_variables'] = {}
345 345 reply_content[u'user_expressions'] = {}
346 346
347 347 # Payloads should be retrieved regardless of outcome, so we can both
348 348 # recover partial output (that could have been generated early in a
349 349 # block, before an error) and clear the payload system always.
350 350 reply_content[u'payload'] = shell.payload_manager.read_payload()
351 351 # Be agressive about clearing the payload because we don't want
352 352 # it to sit in memory until the next execute_request comes in.
353 353 shell.payload_manager.clear_payload()
354 354
355 355 # Flush output before sending the reply.
356 356 sys.stdout.flush()
357 357 sys.stderr.flush()
358 358 # FIXME: on rare occasions, the flush doesn't seem to make it to the
359 359 # clients... This seems to mitigate the problem, but we definitely need
360 360 # to better understand what's going on.
361 361 if self._execute_sleep:
362 362 time.sleep(self._execute_sleep)
363 363
364 364 # Send the reply.
365 365 reply_content = json_clean(reply_content)
366 366 reply_msg = self.session.send(self.shell_socket, u'execute_reply',
367 367 reply_content, parent, ident=ident)
368 self.log.debug(str(reply_msg))
368 self.log.debug("%s", reply_msg)
369 369
370 370 if reply_msg['content']['status'] == u'error':
371 371 self._abort_queue()
372 372
373 373 self.session.send(self.iopub_socket,
374 374 u'status',
375 375 {u'execution_state':u'idle'},
376 376 parent=parent )
377 377
378 378 def complete_request(self, ident, parent):
379 379 txt, matches = self._complete(parent)
380 380 matches = {'matches' : matches,
381 381 'matched_text' : txt,
382 382 'status' : 'ok'}
383 383 matches = json_clean(matches)
384 384 completion_msg = self.session.send(self.shell_socket, 'complete_reply',
385 385 matches, parent, ident)
386 self.log.debug(str(completion_msg))
386 self.log.debug("%s", completion_msg)
387 387
388 388 def object_info_request(self, ident, parent):
389 389 content = parent['content']
390 390 object_info = self.shell.object_inspect(content['oname'],
391 391 detail_level = content.get('detail_level', 0)
392 392 )
393 393 # Before we send this object over, we scrub it for JSON usage
394 394 oinfo = json_clean(object_info)
395 395 msg = self.session.send(self.shell_socket, 'object_info_reply',
396 396 oinfo, parent, ident)
397 self.log.debug(msg)
397 self.log.debug("%s", msg)
398 398
399 399 def history_request(self, ident, parent):
400 400 # We need to pull these out, as passing **kwargs doesn't work with
401 401 # unicode keys before Python 2.6.5.
402 402 hist_access_type = parent['content']['hist_access_type']
403 403 raw = parent['content']['raw']
404 404 output = parent['content']['output']
405 405 if hist_access_type == 'tail':
406 406 n = parent['content']['n']
407 407 hist = self.shell.history_manager.get_tail(n, raw=raw, output=output,
408 408 include_latest=True)
409 409
410 410 elif hist_access_type == 'range':
411 411 session = parent['content']['session']
412 412 start = parent['content']['start']
413 413 stop = parent['content']['stop']
414 414 hist = self.shell.history_manager.get_range(session, start, stop,
415 415 raw=raw, output=output)
416 416
417 417 elif hist_access_type == 'search':
418 418 pattern = parent['content']['pattern']
419 419 hist = self.shell.history_manager.search(pattern, raw=raw,
420 420 output=output)
421 421
422 422 else:
423 423 hist = []
424 424 hist = list(hist)
425 425 content = {'history' : hist}
426 426 content = json_clean(content)
427 427 msg = self.session.send(self.shell_socket, 'history_reply',
428 428 content, parent, ident)
429 429 self.log.debug("Sending history reply with %i entries", len(hist))
430 430
431 431 def connect_request(self, ident, parent):
432 432 if self._recorded_ports is not None:
433 433 content = self._recorded_ports.copy()
434 434 else:
435 435 content = {}
436 436 msg = self.session.send(self.shell_socket, 'connect_reply',
437 437 content, parent, ident)
438 self.log.debug(msg)
438 self.log.debug("%s", msg)
439 439
440 440 def shutdown_request(self, ident, parent):
441 441 self.shell.exit_now = True
442 442 self._shutdown_message = self.session.msg(u'shutdown_reply',
443 443 parent['content'], parent)
444 444 sys.exit(0)
445 445
446 446 #---------------------------------------------------------------------------
447 447 # Engine methods
448 448 #---------------------------------------------------------------------------
449 449
450 450 def apply_request(self, stream, ident, parent):
451 451 # flush previous reply, so this request won't block it
452 452 stream.flush(zmq.POLLOUT)
453 453 try:
454 454 content = parent[u'content']
455 455 bufs = parent[u'buffers']
456 456 msg_id = parent['header']['msg_id']
457 457 # bound = parent['header'].get('bound', False)
458 458 except:
459 self.log.error("Got bad msg: %s"%parent, exc_info=True)
459 self.log.error("Got bad msg: %s", parent, exc_info=True)
460 460 return
461 461 # pyin_msg = self.session.msg(u'pyin',{u'code':code}, parent=parent)
462 462 # self.iopub_stream.send(pyin_msg)
463 463 # self.session.send(self.iopub_stream, u'pyin', {u'code':code},parent=parent)
464 464 sub = {'dependencies_met' : True, 'engine' : self.ident,
465 465 'started': datetime.now()}
466 466 try:
467 467 # allow for not overriding displayhook
468 468 if hasattr(sys.displayhook, 'set_parent'):
469 469 sys.displayhook.set_parent(parent)
470 470 sys.stdout.set_parent(parent)
471 471 sys.stderr.set_parent(parent)
472 472 # exec "f(*args,**kwargs)" in self.user_ns, self.user_ns
473 473 working = self.user_ns
474 474 # suffix =
475 475 prefix = "_"+str(msg_id).replace("-","")+"_"
476 476
477 477 f,args,kwargs = unpack_apply_message(bufs, working, copy=False)
478 478 # if bound:
479 479 # bound_ns = Namespace(working)
480 480 # args = [bound_ns]+list(args)
481 481
482 482 fname = getattr(f, '__name__', 'f')
483 483
484 484 fname = prefix+"f"
485 485 argname = prefix+"args"
486 486 kwargname = prefix+"kwargs"
487 487 resultname = prefix+"result"
488 488
489 489 ns = { fname : f, argname : args, kwargname : kwargs , resultname : None }
490 490 # print ns
491 491 working.update(ns)
492 492 code = "%s=%s(*%s,**%s)"%(resultname, fname, argname, kwargname)
493 493 try:
494 494 exec code in working,working
495 495 result = working.get(resultname)
496 496 finally:
497 497 for key in ns.iterkeys():
498 498 working.pop(key)
499 499 # if bound:
500 500 # working.update(bound_ns)
501 501
502 502 packed_result,buf = serialize_object(result)
503 503 result_buf = [packed_result]+buf
504 504 except:
505 505 exc_content = self._wrap_exception('apply')
506 506 # exc_msg = self.session.msg(u'pyerr', exc_content, parent)
507 507 self.session.send(self.iopub_stream, u'pyerr', exc_content, parent=parent,
508 508 ident=asbytes('%s.pyerr'%self.prefix))
509 509 reply_content = exc_content
510 510 result_buf = []
511 511
512 512 if exc_content['ename'] == 'UnmetDependency':
513 513 sub['dependencies_met'] = False
514 514 else:
515 515 reply_content = {'status' : 'ok'}
516 516
517 517 # put 'ok'/'error' status in header, for scheduler introspection:
518 518 sub['status'] = reply_content['status']
519 519
520 520 # flush i/o
521 521 sys.stdout.flush()
522 522 sys.stderr.flush()
523
523
524 524 reply_msg = self.session.send(stream, u'apply_reply', reply_content,
525 525 parent=parent, ident=ident,buffers=result_buf, subheader=sub)
526 526
527 527 #---------------------------------------------------------------------------
528 528 # Control messages
529 529 #---------------------------------------------------------------------------
530 530
531 531 def abort_queues(self):
532 532 for stream in self.shell_streams:
533 533 if stream:
534 534 self.abort_queue(stream)
535 535
536 536 def abort_queue(self, stream):
537 537 while True:
538 538 idents,msg = self.session.recv(stream, zmq.NOBLOCK, content=True)
539 539 if msg is None:
540 540 return
541 541
542 542 self.log.info("Aborting:")
543 self.log.info(str(msg))
543 self.log.info("%s", msg)
544 544 msg_type = msg['header']['msg_type']
545 545 reply_type = msg_type.split('_')[0] + '_reply'
546 546 # reply_msg = self.session.msg(reply_type, {'status' : 'aborted'}, msg)
547 547 # self.reply_socket.send(ident,zmq.SNDMORE)
548 548 # self.reply_socket.send_json(reply_msg)
549 549 reply_msg = self.session.send(stream, reply_type,
550 550 content={'status' : 'aborted'}, parent=msg, ident=idents)
551 self.log.debug(str(reply_msg))
551 self.log.debug("%s", reply_msg)
552 552 # We need to wait a bit for requests to come in. This can probably
553 553 # be set shorter for true asynchronous clients.
554 554 time.sleep(0.05)
555 555
556 556 def abort_request(self, stream, ident, parent):
557 557 """abort a specifig msg by id"""
558 558 msg_ids = parent['content'].get('msg_ids', None)
559 559 if isinstance(msg_ids, basestring):
560 560 msg_ids = [msg_ids]
561 561 if not msg_ids:
562 562 self.abort_queues()
563 563 for mid in msg_ids:
564 564 self.aborted.add(str(mid))
565 565
566 566 content = dict(status='ok')
567 567 reply_msg = self.session.send(stream, 'abort_reply', content=content,
568 568 parent=parent, ident=ident)
569 self.log.debug(str(reply_msg))
569 self.log.debug("%s", reply_msg)
570 570
571 571 def clear_request(self, stream, idents, parent):
572 572 """Clear our namespace."""
573 573 self.user_ns = {}
574 574 msg = self.session.send(stream, 'clear_reply', ident=idents, parent=parent,
575 575 content = dict(status='ok'))
576 576 self._initial_exec_lines()
577 577
578 578
579 579 #---------------------------------------------------------------------------
580 580 # Protected interface
581 581 #---------------------------------------------------------------------------
582 582
583 583 def _abort_queue(self):
584 584 while True:
585 585 try:
586 586 ident,msg = self.session.recv(self.shell_socket, zmq.NOBLOCK)
587 587 except Exception:
588 588 self.log.warn("Invalid Message:", exc_info=True)
589 589 continue
590 590 if msg is None:
591 591 break
592 592 else:
593 593 assert ident is not None, \
594 594 "Unexpected missing message part."
595 595
596 self.log.debug("Aborting:\n"+str(Message(msg)))
596 self.log.debug("Aborting:\n%s", msg)
597 597 msg_type = msg['header']['msg_type']
598 598 reply_type = msg_type.split('_')[0] + '_reply'
599 599 reply_msg = self.session.send(self.shell_socket, reply_type,
600 600 {'status' : 'aborted'}, msg, ident=ident)
601 self.log.debug(reply_msg)
601 self.log.debug("%s", reply_msg)
602 602 # We need to wait a bit for requests to come in. This can probably
603 603 # be set shorter for true asynchronous clients.
604 604 time.sleep(0.1)
605 605
606 606 def _no_raw_input(self):
607 607 """Raise StdinNotImplentedError if active frontend doesn't support
608 608 stdin."""
609 609 raise StdinNotImplementedError("raw_input was called, but this "
610 610 "frontend does not support stdin.")
611 611
612 612 def _raw_input(self, prompt, ident, parent):
613 613 # Flush output before making the request.
614 614 sys.stderr.flush()
615 615 sys.stdout.flush()
616 616
617 617 # Send the input request.
618 618 content = json_clean(dict(prompt=prompt))
619 619 self.session.send(self.stdin_socket, u'input_request', content, parent,
620 620 ident=ident)
621 621
622 622 # Await a response.
623 623 while True:
624 624 try:
625 625 ident, reply = self.session.recv(self.stdin_socket, 0)
626 626 except Exception:
627 627 self.log.warn("Invalid Message:", exc_info=True)
628 628 else:
629 629 break
630 630 try:
631 631 value = reply['content']['value']
632 632 except:
633 633 self.log.error("Got bad raw_input reply: ")
634 self.log.error(str(Message(parent)))
634 self.log.error("%s", parent)
635 635 value = ''
636 636 if value == '\x04':
637 637 # EOF
638 638 raise EOFError
639 639 return value
640 640
641 641 def _complete(self, msg):
642 642 c = msg['content']
643 643 try:
644 644 cpos = int(c['cursor_pos'])
645 645 except:
646 646 # If we don't get something that we can convert to an integer, at
647 647 # least attempt the completion guessing the cursor is at the end of
648 648 # the text, if there's any, and otherwise of the line
649 649 cpos = len(c['text'])
650 650 if cpos==0:
651 651 cpos = len(c['line'])
652 652 return self.shell.complete(c['text'], c['line'], cpos)
653 653
654 654 def _object_info(self, context):
655 655 symbol, leftover = self._symbol_from_context(context)
656 656 if symbol is not None and not leftover:
657 657 doc = getattr(symbol, '__doc__', '')
658 658 else:
659 659 doc = ''
660 660 object_info = dict(docstring = doc)
661 661 return object_info
662 662
663 663 def _symbol_from_context(self, context):
664 664 if not context:
665 665 return None, context
666 666
667 667 base_symbol_string = context[0]
668 668 symbol = self.shell.user_ns.get(base_symbol_string, None)
669 669 if symbol is None:
670 670 symbol = __builtin__.__dict__.get(base_symbol_string, None)
671 671 if symbol is None:
672 672 return None, context
673 673
674 674 context = context[1:]
675 675 for i, name in enumerate(context):
676 676 new_symbol = getattr(symbol, name, None)
677 677 if new_symbol is None:
678 678 return symbol, context[i:]
679 679 else:
680 680 symbol = new_symbol
681 681
682 682 return symbol, []
683 683
684 684 def _at_shutdown(self):
685 685 """Actions taken at shutdown by the kernel, called by python's atexit.
686 686 """
687 687 # io.rprint("Kernel at_shutdown") # dbg
688 688 if self._shutdown_message is not None:
689 689 self.session.send(self.shell_socket, self._shutdown_message)
690 690 self.session.send(self.iopub_socket, self._shutdown_message)
691 self.log.debug(str(self._shutdown_message))
691 self.log.debug("%s", self._shutdown_message)
692 692 # A very short sleep to give zmq time to flush its message buffers
693 693 # before Python truly shuts down.
694 694 time.sleep(0.01)
695 695
696 696 #-----------------------------------------------------------------------------
697 697 # Aliases and Flags for the IPKernelApp
698 698 #-----------------------------------------------------------------------------
699 699
700 700 flags = dict(kernel_flags)
701 701 flags.update(shell_flags)
702 702
703 703 addflag = lambda *args: flags.update(boolean_flag(*args))
704 704
705 705 flags['pylab'] = (
706 706 {'IPKernelApp' : {'pylab' : 'auto'}},
707 707 """Pre-load matplotlib and numpy for interactive use with
708 708 the default matplotlib backend."""
709 709 )
710 710
711 711 aliases = dict(kernel_aliases)
712 712 aliases.update(shell_aliases)
713 713
714 714 # it's possible we don't want short aliases for *all* of these:
715 715 aliases.update(dict(
716 716 pylab='IPKernelApp.pylab',
717 717 ))
718 718
719 719 #-----------------------------------------------------------------------------
720 720 # The IPKernelApp class
721 721 #-----------------------------------------------------------------------------
722 722
723 723 class IPKernelApp(KernelApp, InteractiveShellApp):
724 724 name = 'ipkernel'
725 725
726 726 aliases = Dict(aliases)
727 727 flags = Dict(flags)
728 728 classes = [Kernel, ZMQInteractiveShell, ProfileDir, Session]
729 729
730 730 # configurables
731 731 pylab = CaselessStrEnum(['tk', 'qt', 'wx', 'gtk', 'osx', 'inline', 'auto'],
732 732 config=True,
733 733 help="""Pre-load matplotlib and numpy for interactive use,
734 734 selecting a particular matplotlib backend and loop integration.
735 735 """
736 736 )
737 737
738 738 @catch_config_error
739 739 def initialize(self, argv=None):
740 740 super(IPKernelApp, self).initialize(argv)
741 741 self.init_path()
742 742 self.init_shell()
743 743 self.init_extensions()
744 744 self.init_code()
745 745
746 746 def init_kernel(self):
747 747
748 748 kernel = Kernel(config=self.config, session=self.session,
749 749 shell_socket=self.shell_socket,
750 750 iopub_socket=self.iopub_socket,
751 751 stdin_socket=self.stdin_socket,
752 752 log=self.log,
753 753 profile_dir=self.profile_dir,
754 754 )
755 755 self.kernel = kernel
756 756 kernel.record_ports(self.ports)
757 757 shell = kernel.shell
758 758 if self.pylab:
759 759 try:
760 760 gui, backend = pylabtools.find_gui_and_backend(self.pylab)
761 761 shell.enable_pylab(gui, import_all=self.pylab_import_all)
762 762 except Exception:
763 763 self.log.error("Pylab initialization failed", exc_info=True)
764 764 # print exception straight to stdout, because normally
765 765 # _showtraceback associates the reply with an execution,
766 766 # which means frontends will never draw it, as this exception
767 767 # is not associated with any execute request.
768 768
769 769 # replace pyerr-sending traceback with stdout
770 770 _showtraceback = shell._showtraceback
771 771 def print_tb(etype, evalue, stb):
772 772 print ("Error initializing pylab, pylab mode will not "
773 773 "be active", file=io.stderr)
774 774 print (shell.InteractiveTB.stb2text(stb), file=io.stdout)
775 775 shell._showtraceback = print_tb
776 776
777 777 # send the traceback over stdout
778 778 shell.showtraceback(tb_offset=0)
779 779
780 780 # restore proper _showtraceback method
781 781 shell._showtraceback = _showtraceback
782 782
783 783
784 784 def init_shell(self):
785 785 self.shell = self.kernel.shell
786 786 self.shell.configurables.append(self)
787 787
788 788
789 789 #-----------------------------------------------------------------------------
790 790 # Kernel main and launch functions
791 791 #-----------------------------------------------------------------------------
792 792
793 793 def launch_kernel(*args, **kwargs):
794 794 """Launches a localhost IPython kernel, binding to the specified ports.
795 795
796 796 This function simply calls entry_point.base_launch_kernel with the right
797 797 first command to start an ipkernel. See base_launch_kernel for arguments.
798 798
799 799 Returns
800 800 -------
801 801 A tuple of form:
802 802 (kernel_process, shell_port, iopub_port, stdin_port, hb_port)
803 803 where kernel_process is a Popen object and the ports are integers.
804 804 """
805 805 return base_launch_kernel('from IPython.zmq.ipkernel import main; main()',
806 806 *args, **kwargs)
807 807
808 808
809 809 def embed_kernel(module=None, local_ns=None, **kwargs):
810 810 """Embed and start an IPython kernel in a given scope.
811 811
812 812 Parameters
813 813 ----------
814 814 module : ModuleType, optional
815 815 The module to load into IPython globals (default: caller)
816 816 local_ns : dict, optional
817 817 The namespace to load into IPython user namespace (default: caller)
818 818
819 819 kwargs : various, optional
820 820 Further keyword args are relayed to the KernelApp constructor,
821 821 allowing configuration of the Kernel. Will only have an effect
822 822 on the first embed_kernel call for a given process.
823 823
824 824 """
825 825 # get the app if it exists, or set it up if it doesn't
826 826 if IPKernelApp.initialized():
827 827 app = IPKernelApp.instance()
828 828 else:
829 829 app = IPKernelApp.instance(**kwargs)
830 830 app.initialize([])
831 831
832 832 # load the calling scope if not given
833 833 (caller_module, caller_locals) = extract_module_locals(1)
834 834 if module is None:
835 835 module = caller_module
836 836 if local_ns is None:
837 837 local_ns = caller_locals
838 838
839 839 app.kernel.user_module = module
840 840 app.kernel.user_ns = local_ns
841 841 app.start()
842 842
843 843 def main():
844 844 """Run an IPKernel as an application"""
845 845 app = IPKernelApp.instance()
846 846 app.initialize()
847 847 app.start()
848 848
849 849
850 850 if __name__ == '__main__':
851 851 main()
General Comments 0
You need to be logged in to leave comments. Login now