##// END OF EJS Templates
include execution_count in pyin msgs, closes #1619
Paul Ivanov -
Show More
@@ -1,657 +1,657 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.jsonutil import json_clean
43 43 from IPython.utils.traitlets import (
44 44 Any, Instance, Float, Dict, CaselessStrEnum
45 45 )
46 46
47 47 from entry_point import base_launch_kernel
48 48 from kernelapp import KernelApp, kernel_flags, kernel_aliases
49 49 from session import Session, Message
50 50 from zmqshell import ZMQInteractiveShell
51 51
52 52
53 53 #-----------------------------------------------------------------------------
54 54 # Main kernel class
55 55 #-----------------------------------------------------------------------------
56 56
57 57 class Kernel(Configurable):
58 58
59 59 #---------------------------------------------------------------------------
60 60 # Kernel interface
61 61 #---------------------------------------------------------------------------
62 62
63 63 # attribute to override with a GUI
64 64 eventloop = Any(None)
65 65
66 66 shell = Instance('IPython.core.interactiveshell.InteractiveShellABC')
67 67 session = Instance(Session)
68 68 profile_dir = Instance('IPython.core.profiledir.ProfileDir')
69 69 shell_socket = Instance('zmq.Socket')
70 70 iopub_socket = Instance('zmq.Socket')
71 71 stdin_socket = Instance('zmq.Socket')
72 72 log = Instance(logging.Logger)
73 73
74 74 # Private interface
75 75
76 76 # Time to sleep after flushing the stdout/err buffers in each execute
77 77 # cycle. While this introduces a hard limit on the minimal latency of the
78 78 # execute cycle, it helps prevent output synchronization problems for
79 79 # clients.
80 80 # Units are in seconds. The minimum zmq latency on local host is probably
81 81 # ~150 microseconds, set this to 500us for now. We may need to increase it
82 82 # a little if it's not enough after more interactive testing.
83 83 _execute_sleep = Float(0.0005, config=True)
84 84
85 85 # Frequency of the kernel's event loop.
86 86 # Units are in seconds, kernel subclasses for GUI toolkits may need to
87 87 # adapt to milliseconds.
88 88 _poll_interval = Float(0.05, config=True)
89 89
90 90 # If the shutdown was requested over the network, we leave here the
91 91 # necessary reply message so it can be sent by our registered atexit
92 92 # handler. This ensures that the reply is only sent to clients truly at
93 93 # the end of our shutdown process (which happens after the underlying
94 94 # IPython shell's own shutdown).
95 95 _shutdown_message = None
96 96
97 97 # This is a dict of port number that the kernel is listening on. It is set
98 98 # by record_ports and used by connect_request.
99 99 _recorded_ports = Dict()
100 100
101 101
102 102
103 103 def __init__(self, **kwargs):
104 104 super(Kernel, self).__init__(**kwargs)
105 105
106 106 # Before we even start up the shell, register *first* our exit handlers
107 107 # so they come before the shell's
108 108 atexit.register(self._at_shutdown)
109 109
110 110 # Initialize the InteractiveShell subclass
111 111 self.shell = ZMQInteractiveShell.instance(config=self.config,
112 112 profile_dir = self.profile_dir,
113 113 )
114 114 self.shell.displayhook.session = self.session
115 115 self.shell.displayhook.pub_socket = self.iopub_socket
116 116 self.shell.display_pub.session = self.session
117 117 self.shell.display_pub.pub_socket = self.iopub_socket
118 118
119 119 # TMP - hack while developing
120 120 self.shell._reply_content = None
121 121
122 122 # Build dict of handlers for message types
123 123 msg_types = [ 'execute_request', 'complete_request',
124 124 'object_info_request', 'history_request',
125 125 'connect_request', 'shutdown_request']
126 126 self.handlers = {}
127 127 for msg_type in msg_types:
128 128 self.handlers[msg_type] = getattr(self, msg_type)
129 129
130 130 def do_one_iteration(self):
131 131 """Do one iteration of the kernel's evaluation loop.
132 132 """
133 133 try:
134 134 ident,msg = self.session.recv(self.shell_socket, zmq.NOBLOCK)
135 135 except Exception:
136 136 self.log.warn("Invalid Message:", exc_info=True)
137 137 return
138 138 if msg is None:
139 139 return
140 140
141 141 msg_type = msg['header']['msg_type']
142 142
143 143 # This assert will raise in versions of zeromq 2.0.7 and lesser.
144 144 # We now require 2.0.8 or above, so we can uncomment for safety.
145 145 # print(ident,msg, file=sys.__stdout__)
146 146 assert ident is not None, "Missing message part."
147 147
148 148 # Print some info about this message and leave a '--->' marker, so it's
149 149 # easier to trace visually the message chain when debugging. Each
150 150 # handler prints its message at the end.
151 151 self.log.debug('\n*** MESSAGE TYPE:'+str(msg_type)+'***')
152 152 self.log.debug(' Content: '+str(msg['content'])+'\n --->\n ')
153 153
154 154 # Find and call actual handler for message
155 155 handler = self.handlers.get(msg_type, None)
156 156 if handler is None:
157 157 self.log.error("UNKNOWN MESSAGE TYPE:" +str(msg))
158 158 else:
159 159 handler(ident, msg)
160 160
161 161 # Check whether we should exit, in case the incoming message set the
162 162 # exit flag on
163 163 if self.shell.exit_now:
164 164 self.log.debug('\nExiting IPython kernel...')
165 165 # We do a normal, clean exit, which allows any actions registered
166 166 # via atexit (such as history saving) to take place.
167 167 sys.exit(0)
168 168
169 169
170 170 def start(self):
171 171 """ Start the kernel main loop.
172 172 """
173 173 # a KeyboardInterrupt (SIGINT) can occur on any python statement, so
174 174 # let's ignore (SIG_IGN) them until we're in a place to handle them properly
175 175 signal(SIGINT,SIG_IGN)
176 176 poller = zmq.Poller()
177 177 poller.register(self.shell_socket, zmq.POLLIN)
178 178 # loop while self.eventloop has not been overridden
179 179 while self.eventloop is None:
180 180 try:
181 181 # scale by extra factor of 10, because there is no
182 182 # reason for this to be anything less than ~ 0.1s
183 183 # since it is a real poller and will respond
184 184 # to events immediately
185 185
186 186 # double nested try/except, to properly catch KeyboardInterrupt
187 187 # due to pyzmq Issue #130
188 188 try:
189 189 poller.poll(10*1000*self._poll_interval)
190 190 # restore raising of KeyboardInterrupt
191 191 signal(SIGINT, default_int_handler)
192 192 self.do_one_iteration()
193 193 except:
194 194 raise
195 195 finally:
196 196 # prevent raising of KeyboardInterrupt
197 197 signal(SIGINT,SIG_IGN)
198 198 except KeyboardInterrupt:
199 199 # Ctrl-C shouldn't crash the kernel
200 200 io.raw_print("KeyboardInterrupt caught in kernel")
201 201 # stop ignoring sigint, now that we are out of our own loop,
202 202 # we don't want to prevent future code from handling it
203 203 signal(SIGINT, default_int_handler)
204 204 while self.eventloop is not None:
205 205 try:
206 206 self.eventloop(self)
207 207 except KeyboardInterrupt:
208 208 # Ctrl-C shouldn't crash the kernel
209 209 io.raw_print("KeyboardInterrupt caught in kernel")
210 210 continue
211 211 else:
212 212 # eventloop exited cleanly, this means we should stop (right?)
213 213 self.eventloop = None
214 214 break
215 215
216 216
217 217 def record_ports(self, ports):
218 218 """Record the ports that this kernel is using.
219 219
220 220 The creator of the Kernel instance must call this methods if they
221 221 want the :meth:`connect_request` method to return the port numbers.
222 222 """
223 223 self._recorded_ports = ports
224 224
225 225 #---------------------------------------------------------------------------
226 226 # Kernel request handlers
227 227 #---------------------------------------------------------------------------
228 228
229 def _publish_pyin(self, code, parent):
229 def _publish_pyin(self, code, parent, execution_count):
230 230 """Publish the code request on the pyin stream."""
231 231
232 self.session.send(self.iopub_socket, u'pyin', {u'code':code},
233 parent=parent)
232 self.session.send(self.iopub_socket, u'pyin', {u'code':code,
233 u'execution_count': execution_count}, parent=parent)
234 234
235 235 def execute_request(self, ident, parent):
236 236
237 237 self.session.send(self.iopub_socket,
238 238 u'status',
239 239 {u'execution_state':u'busy'},
240 240 parent=parent )
241 241
242 242 try:
243 243 content = parent[u'content']
244 244 code = content[u'code']
245 245 silent = content[u'silent']
246 246 except:
247 247 self.log.error("Got bad msg: ")
248 248 self.log.error(str(Message(parent)))
249 249 return
250 250
251 251 shell = self.shell # we'll need this a lot here
252 252
253 253 # Replace raw_input. Note that is not sufficient to replace
254 254 # raw_input in the user namespace.
255 255 if content.get('allow_stdin', False):
256 256 raw_input = lambda prompt='': self._raw_input(prompt, ident, parent)
257 257 else:
258 258 raw_input = lambda prompt='' : self._no_raw_input()
259 259
260 260 if py3compat.PY3:
261 261 __builtin__.input = raw_input
262 262 else:
263 263 __builtin__.raw_input = raw_input
264 264
265 265 # Set the parent message of the display hook and out streams.
266 266 shell.displayhook.set_parent(parent)
267 267 shell.display_pub.set_parent(parent)
268 268 sys.stdout.set_parent(parent)
269 269 sys.stderr.set_parent(parent)
270 270
271 271 # Re-broadcast our input for the benefit of listening clients, and
272 272 # start computing output
273 273 if not silent:
274 self._publish_pyin(code, parent)
274 self._publish_pyin(code, parent, shell.execution_count)
275 275
276 276 reply_content = {}
277 277 try:
278 278 if silent:
279 279 # run_code uses 'exec' mode, so no displayhook will fire, and it
280 280 # doesn't call logging or history manipulations. Print
281 281 # statements in that code will obviously still execute.
282 282 shell.run_code(code)
283 283 else:
284 284 # FIXME: the shell calls the exception handler itself.
285 285 shell.run_cell(code, store_history=True)
286 286 except:
287 287 status = u'error'
288 288 # FIXME: this code right now isn't being used yet by default,
289 289 # because the run_cell() call above directly fires off exception
290 290 # reporting. This code, therefore, is only active in the scenario
291 291 # where runlines itself has an unhandled exception. We need to
292 292 # uniformize this, for all exception construction to come from a
293 293 # single location in the codbase.
294 294 etype, evalue, tb = sys.exc_info()
295 295 tb_list = traceback.format_exception(etype, evalue, tb)
296 296 reply_content.update(shell._showtraceback(etype, evalue, tb_list))
297 297 else:
298 298 status = u'ok'
299 299
300 300 reply_content[u'status'] = status
301 301
302 302 # Return the execution counter so clients can display prompts
303 303 reply_content['execution_count'] = shell.execution_count -1
304 304
305 305 # FIXME - fish exception info out of shell, possibly left there by
306 306 # runlines. We'll need to clean up this logic later.
307 307 if shell._reply_content is not None:
308 308 reply_content.update(shell._reply_content)
309 309 # reset after use
310 310 shell._reply_content = None
311 311
312 312 # At this point, we can tell whether the main code execution succeeded
313 313 # or not. If it did, we proceed to evaluate user_variables/expressions
314 314 if reply_content['status'] == 'ok':
315 315 reply_content[u'user_variables'] = \
316 316 shell.user_variables(content[u'user_variables'])
317 317 reply_content[u'user_expressions'] = \
318 318 shell.user_expressions(content[u'user_expressions'])
319 319 else:
320 320 # If there was an error, don't even try to compute variables or
321 321 # expressions
322 322 reply_content[u'user_variables'] = {}
323 323 reply_content[u'user_expressions'] = {}
324 324
325 325 # Payloads should be retrieved regardless of outcome, so we can both
326 326 # recover partial output (that could have been generated early in a
327 327 # block, before an error) and clear the payload system always.
328 328 reply_content[u'payload'] = shell.payload_manager.read_payload()
329 329 # Be agressive about clearing the payload because we don't want
330 330 # it to sit in memory until the next execute_request comes in.
331 331 shell.payload_manager.clear_payload()
332 332
333 333 # Flush output before sending the reply.
334 334 sys.stdout.flush()
335 335 sys.stderr.flush()
336 336 # FIXME: on rare occasions, the flush doesn't seem to make it to the
337 337 # clients... This seems to mitigate the problem, but we definitely need
338 338 # to better understand what's going on.
339 339 if self._execute_sleep:
340 340 time.sleep(self._execute_sleep)
341 341
342 342 # Send the reply.
343 343 reply_content = json_clean(reply_content)
344 344 reply_msg = self.session.send(self.shell_socket, u'execute_reply',
345 345 reply_content, parent, ident=ident)
346 346 self.log.debug(str(reply_msg))
347 347
348 348 if reply_msg['content']['status'] == u'error':
349 349 self._abort_queue()
350 350
351 351 self.session.send(self.iopub_socket,
352 352 u'status',
353 353 {u'execution_state':u'idle'},
354 354 parent=parent )
355 355
356 356 def complete_request(self, ident, parent):
357 357 txt, matches = self._complete(parent)
358 358 matches = {'matches' : matches,
359 359 'matched_text' : txt,
360 360 'status' : 'ok'}
361 361 matches = json_clean(matches)
362 362 completion_msg = self.session.send(self.shell_socket, 'complete_reply',
363 363 matches, parent, ident)
364 364 self.log.debug(str(completion_msg))
365 365
366 366 def object_info_request(self, ident, parent):
367 367 object_info = self.shell.object_inspect(parent['content']['oname'])
368 368 # Before we send this object over, we scrub it for JSON usage
369 369 oinfo = json_clean(object_info)
370 370 msg = self.session.send(self.shell_socket, 'object_info_reply',
371 371 oinfo, parent, ident)
372 372 self.log.debug(msg)
373 373
374 374 def history_request(self, ident, parent):
375 375 # We need to pull these out, as passing **kwargs doesn't work with
376 376 # unicode keys before Python 2.6.5.
377 377 hist_access_type = parent['content']['hist_access_type']
378 378 raw = parent['content']['raw']
379 379 output = parent['content']['output']
380 380 if hist_access_type == 'tail':
381 381 n = parent['content']['n']
382 382 hist = self.shell.history_manager.get_tail(n, raw=raw, output=output,
383 383 include_latest=True)
384 384
385 385 elif hist_access_type == 'range':
386 386 session = parent['content']['session']
387 387 start = parent['content']['start']
388 388 stop = parent['content']['stop']
389 389 hist = self.shell.history_manager.get_range(session, start, stop,
390 390 raw=raw, output=output)
391 391
392 392 elif hist_access_type == 'search':
393 393 pattern = parent['content']['pattern']
394 394 hist = self.shell.history_manager.search(pattern, raw=raw,
395 395 output=output)
396 396
397 397 else:
398 398 hist = []
399 399 hist = list(hist)
400 400 content = {'history' : hist}
401 401 content = json_clean(content)
402 402 msg = self.session.send(self.shell_socket, 'history_reply',
403 403 content, parent, ident)
404 404 self.log.debug("Sending history reply with %i entries", len(hist))
405 405
406 406 def connect_request(self, ident, parent):
407 407 if self._recorded_ports is not None:
408 408 content = self._recorded_ports.copy()
409 409 else:
410 410 content = {}
411 411 msg = self.session.send(self.shell_socket, 'connect_reply',
412 412 content, parent, ident)
413 413 self.log.debug(msg)
414 414
415 415 def shutdown_request(self, ident, parent):
416 416 self.shell.exit_now = True
417 417 self._shutdown_message = self.session.msg(u'shutdown_reply',
418 418 parent['content'], parent)
419 419 sys.exit(0)
420 420
421 421 #---------------------------------------------------------------------------
422 422 # Protected interface
423 423 #---------------------------------------------------------------------------
424 424
425 425 def _abort_queue(self):
426 426 while True:
427 427 try:
428 428 ident,msg = self.session.recv(self.shell_socket, zmq.NOBLOCK)
429 429 except Exception:
430 430 self.log.warn("Invalid Message:", exc_info=True)
431 431 continue
432 432 if msg is None:
433 433 break
434 434 else:
435 435 assert ident is not None, \
436 436 "Unexpected missing message part."
437 437
438 438 self.log.debug("Aborting:\n"+str(Message(msg)))
439 439 msg_type = msg['header']['msg_type']
440 440 reply_type = msg_type.split('_')[0] + '_reply'
441 441 reply_msg = self.session.send(self.shell_socket, reply_type,
442 442 {'status' : 'aborted'}, msg, ident=ident)
443 443 self.log.debug(reply_msg)
444 444 # We need to wait a bit for requests to come in. This can probably
445 445 # be set shorter for true asynchronous clients.
446 446 time.sleep(0.1)
447 447
448 448 def _no_raw_input(self):
449 449 """Raise StdinNotImplentedError if active frontend doesn't support
450 450 stdin."""
451 451 raise StdinNotImplementedError("raw_input was called, but this "
452 452 "frontend does not support stdin.")
453 453
454 454 def _raw_input(self, prompt, ident, parent):
455 455 # Flush output before making the request.
456 456 sys.stderr.flush()
457 457 sys.stdout.flush()
458 458
459 459 # Send the input request.
460 460 content = json_clean(dict(prompt=prompt))
461 461 self.session.send(self.stdin_socket, u'input_request', content, parent,
462 462 ident=ident)
463 463
464 464 # Await a response.
465 465 while True:
466 466 try:
467 467 ident, reply = self.session.recv(self.stdin_socket, 0)
468 468 except Exception:
469 469 self.log.warn("Invalid Message:", exc_info=True)
470 470 else:
471 471 break
472 472 try:
473 473 value = reply['content']['value']
474 474 except:
475 475 self.log.error("Got bad raw_input reply: ")
476 476 self.log.error(str(Message(parent)))
477 477 value = ''
478 478 if value == '\x04':
479 479 # EOF
480 480 raise EOFError
481 481 return value
482 482
483 483 def _complete(self, msg):
484 484 c = msg['content']
485 485 try:
486 486 cpos = int(c['cursor_pos'])
487 487 except:
488 488 # If we don't get something that we can convert to an integer, at
489 489 # least attempt the completion guessing the cursor is at the end of
490 490 # the text, if there's any, and otherwise of the line
491 491 cpos = len(c['text'])
492 492 if cpos==0:
493 493 cpos = len(c['line'])
494 494 return self.shell.complete(c['text'], c['line'], cpos)
495 495
496 496 def _object_info(self, context):
497 497 symbol, leftover = self._symbol_from_context(context)
498 498 if symbol is not None and not leftover:
499 499 doc = getattr(symbol, '__doc__', '')
500 500 else:
501 501 doc = ''
502 502 object_info = dict(docstring = doc)
503 503 return object_info
504 504
505 505 def _symbol_from_context(self, context):
506 506 if not context:
507 507 return None, context
508 508
509 509 base_symbol_string = context[0]
510 510 symbol = self.shell.user_ns.get(base_symbol_string, None)
511 511 if symbol is None:
512 512 symbol = __builtin__.__dict__.get(base_symbol_string, None)
513 513 if symbol is None:
514 514 return None, context
515 515
516 516 context = context[1:]
517 517 for i, name in enumerate(context):
518 518 new_symbol = getattr(symbol, name, None)
519 519 if new_symbol is None:
520 520 return symbol, context[i:]
521 521 else:
522 522 symbol = new_symbol
523 523
524 524 return symbol, []
525 525
526 526 def _at_shutdown(self):
527 527 """Actions taken at shutdown by the kernel, called by python's atexit.
528 528 """
529 529 # io.rprint("Kernel at_shutdown") # dbg
530 530 if self._shutdown_message is not None:
531 531 self.session.send(self.shell_socket, self._shutdown_message)
532 532 self.session.send(self.iopub_socket, self._shutdown_message)
533 533 self.log.debug(str(self._shutdown_message))
534 534 # A very short sleep to give zmq time to flush its message buffers
535 535 # before Python truly shuts down.
536 536 time.sleep(0.01)
537 537
538 538 #-----------------------------------------------------------------------------
539 539 # Aliases and Flags for the IPKernelApp
540 540 #-----------------------------------------------------------------------------
541 541
542 542 flags = dict(kernel_flags)
543 543 flags.update(shell_flags)
544 544
545 545 addflag = lambda *args: flags.update(boolean_flag(*args))
546 546
547 547 flags['pylab'] = (
548 548 {'IPKernelApp' : {'pylab' : 'auto'}},
549 549 """Pre-load matplotlib and numpy for interactive use with
550 550 the default matplotlib backend."""
551 551 )
552 552
553 553 aliases = dict(kernel_aliases)
554 554 aliases.update(shell_aliases)
555 555
556 556 # it's possible we don't want short aliases for *all* of these:
557 557 aliases.update(dict(
558 558 pylab='IPKernelApp.pylab',
559 559 ))
560 560
561 561 #-----------------------------------------------------------------------------
562 562 # The IPKernelApp class
563 563 #-----------------------------------------------------------------------------
564 564
565 565 class IPKernelApp(KernelApp, InteractiveShellApp):
566 566 name = 'ipkernel'
567 567
568 568 aliases = Dict(aliases)
569 569 flags = Dict(flags)
570 570 classes = [Kernel, ZMQInteractiveShell, ProfileDir, Session]
571 571 # configurables
572 572 pylab = CaselessStrEnum(['tk', 'qt', 'wx', 'gtk', 'osx', 'inline', 'auto'],
573 573 config=True,
574 574 help="""Pre-load matplotlib and numpy for interactive use,
575 575 selecting a particular matplotlib backend and loop integration.
576 576 """
577 577 )
578 578
579 579 @catch_config_error
580 580 def initialize(self, argv=None):
581 581 super(IPKernelApp, self).initialize(argv)
582 582 self.init_shell()
583 583 self.init_extensions()
584 584 self.init_code()
585 585
586 586 def init_kernel(self):
587 587
588 588 kernel = Kernel(config=self.config, session=self.session,
589 589 shell_socket=self.shell_socket,
590 590 iopub_socket=self.iopub_socket,
591 591 stdin_socket=self.stdin_socket,
592 592 log=self.log,
593 593 profile_dir=self.profile_dir,
594 594 )
595 595 self.kernel = kernel
596 596 kernel.record_ports(self.ports)
597 597 shell = kernel.shell
598 598 if self.pylab:
599 599 try:
600 600 gui, backend = pylabtools.find_gui_and_backend(self.pylab)
601 601 shell.enable_pylab(gui, import_all=self.pylab_import_all)
602 602 except Exception:
603 603 self.log.error("Pylab initialization failed", exc_info=True)
604 604 # print exception straight to stdout, because normally
605 605 # _showtraceback associates the reply with an execution,
606 606 # which means frontends will never draw it, as this exception
607 607 # is not associated with any execute request.
608 608
609 609 # replace pyerr-sending traceback with stdout
610 610 _showtraceback = shell._showtraceback
611 611 def print_tb(etype, evalue, stb):
612 612 print ("Error initializing pylab, pylab mode will not "
613 613 "be active", file=io.stderr)
614 614 print (shell.InteractiveTB.stb2text(stb), file=io.stdout)
615 615 shell._showtraceback = print_tb
616 616
617 617 # send the traceback over stdout
618 618 shell.showtraceback(tb_offset=0)
619 619
620 620 # restore proper _showtraceback method
621 621 shell._showtraceback = _showtraceback
622 622
623 623
624 624 def init_shell(self):
625 625 self.shell = self.kernel.shell
626 626 self.shell.configurables.append(self)
627 627
628 628
629 629 #-----------------------------------------------------------------------------
630 630 # Kernel main and launch functions
631 631 #-----------------------------------------------------------------------------
632 632
633 633 def launch_kernel(*args, **kwargs):
634 634 """Launches a localhost IPython kernel, binding to the specified ports.
635 635
636 636 This function simply calls entry_point.base_launch_kernel with the right
637 637 first command to start an ipkernel. See base_launch_kernel for arguments.
638 638
639 639 Returns
640 640 -------
641 641 A tuple of form:
642 642 (kernel_process, shell_port, iopub_port, stdin_port, hb_port)
643 643 where kernel_process is a Popen object and the ports are integers.
644 644 """
645 645 return base_launch_kernel('from IPython.zmq.ipkernel import main; main()',
646 646 *args, **kwargs)
647 647
648 648
649 649 def main():
650 650 """Run an IPKernel as an application"""
651 651 app = IPKernelApp.instance()
652 652 app.initialize()
653 653 app.start()
654 654
655 655
656 656 if __name__ == '__main__':
657 657 main()
General Comments 0
You need to be logged in to leave comments. Login now