##// END OF EJS Templates
Kernel event loop is robust against random SIGINT.
Thomas Kluyver -
Show More
@@ -1,685 +1,689 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 # System library imports.
26 26 import zmq
27 27
28 28 # Local imports.
29 29 from IPython.config.configurable import Configurable
30 30 from IPython.utils import io
31 31 from IPython.utils.jsonutil import json_clean
32 32 from IPython.lib import pylabtools
33 33 from IPython.utils.traitlets import Instance, Float
34 34 from entry_point import (base_launch_kernel, make_argument_parser, make_kernel,
35 35 start_kernel)
36 36 from iostream import OutStream
37 37 from session import Session, Message
38 38 from zmqshell import ZMQInteractiveShell
39 39
40 40 #-----------------------------------------------------------------------------
41 41 # Globals
42 42 #-----------------------------------------------------------------------------
43 43
44 44 # Module-level logger
45 45 logger = logging.getLogger(__name__)
46 46
47 47 # FIXME: this needs to be done more cleanly later, once we have proper
48 48 # configuration support. This is a library, so it shouldn't set a stream
49 49 # handler, see:
50 50 # http://docs.python.org/library/logging.html#configuring-logging-for-a-library
51 51 # But this lets us at least do developer debugging for now by manually turning
52 52 # it on/off. And once we have full config support, the client entry points
53 53 # will select their logging handlers, as well as passing to this library the
54 54 # logging level.
55 55
56 56 if 0: # dbg - set to 1 to actually see the messages.
57 57 logger.addHandler(logging.StreamHandler())
58 58 logger.setLevel(logging.DEBUG)
59 59
60 60 # /FIXME
61 61
62 62 #-----------------------------------------------------------------------------
63 63 # Main kernel class
64 64 #-----------------------------------------------------------------------------
65 65
66 66 class Kernel(Configurable):
67 67
68 68 #---------------------------------------------------------------------------
69 69 # Kernel interface
70 70 #---------------------------------------------------------------------------
71 71
72 72 shell = Instance('IPython.core.interactiveshell.InteractiveShellABC')
73 73 session = Instance(Session)
74 74 reply_socket = Instance('zmq.Socket')
75 75 pub_socket = Instance('zmq.Socket')
76 76 req_socket = Instance('zmq.Socket')
77 77
78 78 # Private interface
79 79
80 80 # Time to sleep after flushing the stdout/err buffers in each execute
81 81 # cycle. While this introduces a hard limit on the minimal latency of the
82 82 # execute cycle, it helps prevent output synchronization problems for
83 83 # clients.
84 84 # Units are in seconds. The minimum zmq latency on local host is probably
85 85 # ~150 microseconds, set this to 500us for now. We may need to increase it
86 86 # a little if it's not enough after more interactive testing.
87 87 _execute_sleep = Float(0.0005, config=True)
88 88
89 89 # Frequency of the kernel's event loop.
90 90 # Units are in seconds, kernel subclasses for GUI toolkits may need to
91 91 # adapt to milliseconds.
92 92 _poll_interval = Float(0.05, config=True)
93 93
94 94 # If the shutdown was requested over the network, we leave here the
95 95 # necessary reply message so it can be sent by our registered atexit
96 96 # handler. This ensures that the reply is only sent to clients truly at
97 97 # the end of our shutdown process (which happens after the underlying
98 98 # IPython shell's own shutdown).
99 99 _shutdown_message = None
100 100
101 101 # This is a dict of port number that the kernel is listening on. It is set
102 102 # by record_ports and used by connect_request.
103 103 _recorded_ports = None
104 104
105 105
106 106 def __init__(self, **kwargs):
107 107 super(Kernel, self).__init__(**kwargs)
108 108
109 109 # Before we even start up the shell, register *first* our exit handlers
110 110 # so they come before the shell's
111 111 atexit.register(self._at_shutdown)
112 112
113 113 # Initialize the InteractiveShell subclass
114 114 self.shell = ZMQInteractiveShell.instance()
115 115 self.shell.displayhook.session = self.session
116 116 self.shell.displayhook.pub_socket = self.pub_socket
117 117 self.shell.display_pub.session = self.session
118 118 self.shell.display_pub.pub_socket = self.pub_socket
119 119
120 120 # TMP - hack while developing
121 121 self.shell._reply_content = None
122 122
123 123 # Build dict of handlers for message types
124 124 msg_types = [ 'execute_request', 'complete_request',
125 125 'object_info_request', 'history_request',
126 126 'connect_request', 'shutdown_request']
127 127 self.handlers = {}
128 128 for msg_type in msg_types:
129 129 self.handlers[msg_type] = getattr(self, msg_type)
130 130
131 131 def do_one_iteration(self):
132 132 """Do one iteration of the kernel's evaluation loop.
133 133 """
134 134 ident,msg = self.session.recv(self.reply_socket, zmq.NOBLOCK)
135 135 if msg is None:
136 136 return
137 137
138 138 # This assert will raise in versions of zeromq 2.0.7 and lesser.
139 139 # We now require 2.0.8 or above, so we can uncomment for safety.
140 140 # print(ident,msg, file=sys.__stdout__)
141 141 assert ident is not None, "Missing message part."
142 142
143 143 # Print some info about this message and leave a '--->' marker, so it's
144 144 # easier to trace visually the message chain when debugging. Each
145 145 # handler prints its message at the end.
146 146 # Eventually we'll move these from stdout to a logger.
147 147 logger.debug('\n*** MESSAGE TYPE:'+str(msg['msg_type'])+'***')
148 148 logger.debug(' Content: '+str(msg['content'])+'\n --->\n ')
149 149
150 150 # Find and call actual handler for message
151 151 handler = self.handlers.get(msg['msg_type'], None)
152 152 if handler is None:
153 153 logger.error("UNKNOWN MESSAGE TYPE:" +str(msg))
154 154 else:
155 155 handler(ident, msg)
156 156
157 157 # Check whether we should exit, in case the incoming message set the
158 158 # exit flag on
159 159 if self.shell.exit_now:
160 160 logger.debug('\nExiting IPython kernel...')
161 161 # We do a normal, clean exit, which allows any actions registered
162 162 # via atexit (such as history saving) to take place.
163 163 sys.exit(0)
164 164
165 165
166 166 def start(self):
167 167 """ Start the kernel main loop.
168 168 """
169 169 while True:
170 time.sleep(self._poll_interval)
171 self.do_one_iteration()
170 try:
171 time.sleep(self._poll_interval)
172 self.do_one_iteration()
173 except KeyboardInterrupt:
174 # Ctrl-C shouldn't crash the kernel
175 continue
172 176
173 177 def record_ports(self, xrep_port, pub_port, req_port, hb_port):
174 178 """Record the ports that this kernel is using.
175 179
176 180 The creator of the Kernel instance must call this methods if they
177 181 want the :meth:`connect_request` method to return the port numbers.
178 182 """
179 183 self._recorded_ports = {
180 184 'xrep_port' : xrep_port,
181 185 'pub_port' : pub_port,
182 186 'req_port' : req_port,
183 187 'hb_port' : hb_port
184 188 }
185 189
186 190 #---------------------------------------------------------------------------
187 191 # Kernel request handlers
188 192 #---------------------------------------------------------------------------
189 193
190 194 def _publish_pyin(self, code, parent):
191 195 """Publish the code request on the pyin stream."""
192 196
193 197 pyin_msg = self.session.send(self.pub_socket, u'pyin',{u'code':code}, parent=parent)
194 198
195 199 def execute_request(self, ident, parent):
196 200
197 201 status_msg = self.session.send(self.pub_socket,
198 202 u'status',
199 203 {u'execution_state':u'busy'},
200 204 parent=parent
201 205 )
202 206
203 207 try:
204 208 content = parent[u'content']
205 209 code = content[u'code']
206 210 silent = content[u'silent']
207 211 except:
208 212 logger.error("Got bad msg: ")
209 213 logger.error(str(Message(parent)))
210 214 return
211 215
212 216 shell = self.shell # we'll need this a lot here
213 217
214 218 # Replace raw_input. Note that is not sufficient to replace
215 219 # raw_input in the user namespace.
216 220 raw_input = lambda prompt='': self._raw_input(prompt, ident, parent)
217 221 __builtin__.raw_input = raw_input
218 222
219 223 # Set the parent message of the display hook and out streams.
220 224 shell.displayhook.set_parent(parent)
221 225 shell.display_pub.set_parent(parent)
222 226 sys.stdout.set_parent(parent)
223 227 sys.stderr.set_parent(parent)
224 228
225 229 # Re-broadcast our input for the benefit of listening clients, and
226 230 # start computing output
227 231 if not silent:
228 232 self._publish_pyin(code, parent)
229 233
230 234 reply_content = {}
231 235 try:
232 236 if silent:
233 237 # run_code uses 'exec' mode, so no displayhook will fire, and it
234 238 # doesn't call logging or history manipulations. Print
235 239 # statements in that code will obviously still execute.
236 240 shell.run_code(code)
237 241 else:
238 242 # FIXME: the shell calls the exception handler itself.
239 243 shell.run_cell(code)
240 244 except:
241 245 status = u'error'
242 246 # FIXME: this code right now isn't being used yet by default,
243 247 # because the run_cell() call above directly fires off exception
244 248 # reporting. This code, therefore, is only active in the scenario
245 249 # where runlines itself has an unhandled exception. We need to
246 250 # uniformize this, for all exception construction to come from a
247 251 # single location in the codbase.
248 252 etype, evalue, tb = sys.exc_info()
249 253 tb_list = traceback.format_exception(etype, evalue, tb)
250 254 reply_content.update(shell._showtraceback(etype, evalue, tb_list))
251 255 else:
252 256 status = u'ok'
253 257
254 258 reply_content[u'status'] = status
255 259
256 260 # Return the execution counter so clients can display prompts
257 261 reply_content['execution_count'] = shell.execution_count -1
258 262
259 263 # FIXME - fish exception info out of shell, possibly left there by
260 264 # runlines. We'll need to clean up this logic later.
261 265 if shell._reply_content is not None:
262 266 reply_content.update(shell._reply_content)
263 267 # reset after use
264 268 shell._reply_content = None
265 269
266 270 # At this point, we can tell whether the main code execution succeeded
267 271 # or not. If it did, we proceed to evaluate user_variables/expressions
268 272 if reply_content['status'] == 'ok':
269 273 reply_content[u'user_variables'] = \
270 274 shell.user_variables(content[u'user_variables'])
271 275 reply_content[u'user_expressions'] = \
272 276 shell.user_expressions(content[u'user_expressions'])
273 277 else:
274 278 # If there was an error, don't even try to compute variables or
275 279 # expressions
276 280 reply_content[u'user_variables'] = {}
277 281 reply_content[u'user_expressions'] = {}
278 282
279 283 # Payloads should be retrieved regardless of outcome, so we can both
280 284 # recover partial output (that could have been generated early in a
281 285 # block, before an error) and clear the payload system always.
282 286 reply_content[u'payload'] = shell.payload_manager.read_payload()
283 287 # Be agressive about clearing the payload because we don't want
284 288 # it to sit in memory until the next execute_request comes in.
285 289 shell.payload_manager.clear_payload()
286 290
287 291 # Flush output before sending the reply.
288 292 sys.stdout.flush()
289 293 sys.stderr.flush()
290 294 # FIXME: on rare occasions, the flush doesn't seem to make it to the
291 295 # clients... This seems to mitigate the problem, but we definitely need
292 296 # to better understand what's going on.
293 297 if self._execute_sleep:
294 298 time.sleep(self._execute_sleep)
295 299
296 300 # Send the reply.
297 301 reply_msg = self.session.send(self.reply_socket, u'execute_reply',
298 302 reply_content, parent, ident=ident)
299 303 logger.debug(str(reply_msg))
300 304
301 305 if reply_msg['content']['status'] == u'error':
302 306 self._abort_queue()
303 307
304 308 status_msg = self.session.send(self.pub_socket,
305 309 u'status',
306 310 {u'execution_state':u'idle'},
307 311 parent=parent
308 312 )
309 313
310 314 def complete_request(self, ident, parent):
311 315 txt, matches = self._complete(parent)
312 316 matches = {'matches' : matches,
313 317 'matched_text' : txt,
314 318 'status' : 'ok'}
315 319 completion_msg = self.session.send(self.reply_socket, 'complete_reply',
316 320 matches, parent, ident)
317 321 logger.debug(str(completion_msg))
318 322
319 323 def object_info_request(self, ident, parent):
320 324 object_info = self.shell.object_inspect(parent['content']['oname'])
321 325 # Before we send this object over, we scrub it for JSON usage
322 326 oinfo = json_clean(object_info)
323 327 msg = self.session.send(self.reply_socket, 'object_info_reply',
324 328 oinfo, parent, ident)
325 329 logger.debug(msg)
326 330
327 331 def history_request(self, ident, parent):
328 332 # We need to pull these out, as passing **kwargs doesn't work with
329 333 # unicode keys before Python 2.6.5.
330 334 hist_access_type = parent['content']['hist_access_type']
331 335 raw = parent['content']['raw']
332 336 output = parent['content']['output']
333 337 if hist_access_type == 'tail':
334 338 n = parent['content']['n']
335 339 hist = self.shell.history_manager.get_tail(n, raw=raw, output=output,
336 340 include_latest=True)
337 341
338 342 elif hist_access_type == 'range':
339 343 session = parent['content']['session']
340 344 start = parent['content']['start']
341 345 stop = parent['content']['stop']
342 346 hist = self.shell.history_manager.get_range(session, start, stop,
343 347 raw=raw, output=output)
344 348
345 349 elif hist_access_type == 'search':
346 350 pattern = parent['content']['pattern']
347 351 hist = self.shell.history_manager.search(pattern, raw=raw, output=output)
348 352
349 353 else:
350 354 hist = []
351 355 content = {'history' : list(hist)}
352 356 msg = self.session.send(self.reply_socket, 'history_reply',
353 357 content, parent, ident)
354 358 logger.debug(str(msg))
355 359
356 360 def connect_request(self, ident, parent):
357 361 if self._recorded_ports is not None:
358 362 content = self._recorded_ports.copy()
359 363 else:
360 364 content = {}
361 365 msg = self.session.send(self.reply_socket, 'connect_reply',
362 366 content, parent, ident)
363 367 logger.debug(msg)
364 368
365 369 def shutdown_request(self, ident, parent):
366 370 self.shell.exit_now = True
367 371 self._shutdown_message = self.session.msg(u'shutdown_reply', parent['content'], parent)
368 372 sys.exit(0)
369 373
370 374 #---------------------------------------------------------------------------
371 375 # Protected interface
372 376 #---------------------------------------------------------------------------
373 377
374 378 def _abort_queue(self):
375 379 while True:
376 380 ident,msg = self.session.recv(self.reply_socket, zmq.NOBLOCK)
377 381 if msg is None:
378 382 break
379 383 else:
380 384 assert ident is not None, \
381 385 "Unexpected missing message part."
382 386
383 387 logger.debug("Aborting:\n"+str(Message(msg)))
384 388 msg_type = msg['msg_type']
385 389 reply_type = msg_type.split('_')[0] + '_reply'
386 390 reply_msg = self.session.send(self.reply_socket, reply_type,
387 391 {'status' : 'aborted'}, msg, ident=ident)
388 392 logger.debug(reply_msg)
389 393 # We need to wait a bit for requests to come in. This can probably
390 394 # be set shorter for true asynchronous clients.
391 395 time.sleep(0.1)
392 396
393 397 def _raw_input(self, prompt, ident, parent):
394 398 # Flush output before making the request.
395 399 sys.stderr.flush()
396 400 sys.stdout.flush()
397 401
398 402 # Send the input request.
399 403 content = dict(prompt=prompt)
400 404 msg = self.session.send(self.req_socket, u'input_request', content, parent)
401 405
402 406 # Await a response.
403 407 ident, reply = self.session.recv(self.req_socket, 0)
404 408 try:
405 409 value = reply['content']['value']
406 410 except:
407 411 logger.error("Got bad raw_input reply: ")
408 412 logger.error(str(Message(parent)))
409 413 value = ''
410 414 return value
411 415
412 416 def _complete(self, msg):
413 417 c = msg['content']
414 418 try:
415 419 cpos = int(c['cursor_pos'])
416 420 except:
417 421 # If we don't get something that we can convert to an integer, at
418 422 # least attempt the completion guessing the cursor is at the end of
419 423 # the text, if there's any, and otherwise of the line
420 424 cpos = len(c['text'])
421 425 if cpos==0:
422 426 cpos = len(c['line'])
423 427 return self.shell.complete(c['text'], c['line'], cpos)
424 428
425 429 def _object_info(self, context):
426 430 symbol, leftover = self._symbol_from_context(context)
427 431 if symbol is not None and not leftover:
428 432 doc = getattr(symbol, '__doc__', '')
429 433 else:
430 434 doc = ''
431 435 object_info = dict(docstring = doc)
432 436 return object_info
433 437
434 438 def _symbol_from_context(self, context):
435 439 if not context:
436 440 return None, context
437 441
438 442 base_symbol_string = context[0]
439 443 symbol = self.shell.user_ns.get(base_symbol_string, None)
440 444 if symbol is None:
441 445 symbol = __builtin__.__dict__.get(base_symbol_string, None)
442 446 if symbol is None:
443 447 return None, context
444 448
445 449 context = context[1:]
446 450 for i, name in enumerate(context):
447 451 new_symbol = getattr(symbol, name, None)
448 452 if new_symbol is None:
449 453 return symbol, context[i:]
450 454 else:
451 455 symbol = new_symbol
452 456
453 457 return symbol, []
454 458
455 459 def _at_shutdown(self):
456 460 """Actions taken at shutdown by the kernel, called by python's atexit.
457 461 """
458 462 # io.rprint("Kernel at_shutdown") # dbg
459 463 if self._shutdown_message is not None:
460 464 self.session.send(self.reply_socket, self._shutdown_message)
461 465 self.session.send(self.pub_socket, self._shutdown_message)
462 466 logger.debug(str(self._shutdown_message))
463 467 # A very short sleep to give zmq time to flush its message buffers
464 468 # before Python truly shuts down.
465 469 time.sleep(0.01)
466 470
467 471
468 472 class QtKernel(Kernel):
469 473 """A Kernel subclass with Qt support."""
470 474
471 475 def start(self):
472 476 """Start a kernel with QtPy4 event loop integration."""
473 477
474 478 from PyQt4 import QtCore
475 479 from IPython.lib.guisupport import get_app_qt4, start_event_loop_qt4
476 480
477 481 self.app = get_app_qt4([" "])
478 482 self.app.setQuitOnLastWindowClosed(False)
479 483 self.timer = QtCore.QTimer()
480 484 self.timer.timeout.connect(self.do_one_iteration)
481 485 # Units for the timer are in milliseconds
482 486 self.timer.start(1000*self._poll_interval)
483 487 start_event_loop_qt4(self.app)
484 488
485 489
486 490 class WxKernel(Kernel):
487 491 """A Kernel subclass with Wx support."""
488 492
489 493 def start(self):
490 494 """Start a kernel with wx event loop support."""
491 495
492 496 import wx
493 497 from IPython.lib.guisupport import start_event_loop_wx
494 498
495 499 doi = self.do_one_iteration
496 500 # Wx uses milliseconds
497 501 poll_interval = int(1000*self._poll_interval)
498 502
499 503 # We have to put the wx.Timer in a wx.Frame for it to fire properly.
500 504 # We make the Frame hidden when we create it in the main app below.
501 505 class TimerFrame(wx.Frame):
502 506 def __init__(self, func):
503 507 wx.Frame.__init__(self, None, -1)
504 508 self.timer = wx.Timer(self)
505 509 # Units for the timer are in milliseconds
506 510 self.timer.Start(poll_interval)
507 511 self.Bind(wx.EVT_TIMER, self.on_timer)
508 512 self.func = func
509 513
510 514 def on_timer(self, event):
511 515 self.func()
512 516
513 517 # We need a custom wx.App to create our Frame subclass that has the
514 518 # wx.Timer to drive the ZMQ event loop.
515 519 class IPWxApp(wx.App):
516 520 def OnInit(self):
517 521 self.frame = TimerFrame(doi)
518 522 self.frame.Show(False)
519 523 return True
520 524
521 525 # The redirect=False here makes sure that wx doesn't replace
522 526 # sys.stdout/stderr with its own classes.
523 527 self.app = IPWxApp(redirect=False)
524 528 start_event_loop_wx(self.app)
525 529
526 530
527 531 class TkKernel(Kernel):
528 532 """A Kernel subclass with Tk support."""
529 533
530 534 def start(self):
531 535 """Start a Tk enabled event loop."""
532 536
533 537 import Tkinter
534 538 doi = self.do_one_iteration
535 539 # Tk uses milliseconds
536 540 poll_interval = int(1000*self._poll_interval)
537 541 # For Tkinter, we create a Tk object and call its withdraw method.
538 542 class Timer(object):
539 543 def __init__(self, func):
540 544 self.app = Tkinter.Tk()
541 545 self.app.withdraw()
542 546 self.func = func
543 547
544 548 def on_timer(self):
545 549 self.func()
546 550 self.app.after(poll_interval, self.on_timer)
547 551
548 552 def start(self):
549 553 self.on_timer() # Call it once to get things going.
550 554 self.app.mainloop()
551 555
552 556 self.timer = Timer(doi)
553 557 self.timer.start()
554 558
555 559
556 560 class GTKKernel(Kernel):
557 561 """A Kernel subclass with GTK support."""
558 562
559 563 def start(self):
560 564 """Start the kernel, coordinating with the GTK event loop"""
561 565 from .gui.gtkembed import GTKEmbed
562 566
563 567 gtk_kernel = GTKEmbed(self)
564 568 gtk_kernel.start()
565 569
566 570
567 571 #-----------------------------------------------------------------------------
568 572 # Kernel main and launch functions
569 573 #-----------------------------------------------------------------------------
570 574
571 575 def launch_kernel(ip=None, xrep_port=0, pub_port=0, req_port=0, hb_port=0,
572 576 stdin=None, stdout=None, stderr=None,
573 577 executable=None, independent=False, pylab=False, colors=None):
574 578 """Launches a localhost kernel, binding to the specified ports.
575 579
576 580 Parameters
577 581 ----------
578 582 ip : str, optional
579 583 The ip address the kernel will bind to.
580 584
581 585 xrep_port : int, optional
582 586 The port to use for XREP channel.
583 587
584 588 pub_port : int, optional
585 589 The port to use for the SUB channel.
586 590
587 591 req_port : int, optional
588 592 The port to use for the REQ (raw input) channel.
589 593
590 594 hb_port : int, optional
591 595 The port to use for the hearbeat REP channel.
592 596
593 597 stdin, stdout, stderr : optional (default None)
594 598 Standards streams, as defined in subprocess.Popen.
595 599
596 600 executable : str, optional (default sys.executable)
597 601 The Python executable to use for the kernel process.
598 602
599 603 independent : bool, optional (default False)
600 604 If set, the kernel process is guaranteed to survive if this process
601 605 dies. If not set, an effort is made to ensure that the kernel is killed
602 606 when this process dies. Note that in this case it is still good practice
603 607 to kill kernels manually before exiting.
604 608
605 609 pylab : bool or string, optional (default False)
606 610 If not False, the kernel will be launched with pylab enabled. If a
607 611 string is passed, matplotlib will use the specified backend. Otherwise,
608 612 matplotlib's default backend will be used.
609 613
610 614 colors : None or string, optional (default None)
611 615 If not None, specify the color scheme. One of (NoColor, LightBG, Linux)
612 616
613 617 Returns
614 618 -------
615 619 A tuple of form:
616 620 (kernel_process, xrep_port, pub_port, req_port)
617 621 where kernel_process is a Popen object and the ports are integers.
618 622 """
619 623 extra_arguments = []
620 624 if pylab:
621 625 extra_arguments.append('--pylab')
622 626 if isinstance(pylab, basestring):
623 627 extra_arguments.append(pylab)
624 628 if ip is not None:
625 629 extra_arguments.append('--ip')
626 630 if isinstance(ip, basestring):
627 631 extra_arguments.append(ip)
628 632 if colors is not None:
629 633 extra_arguments.append('--colors')
630 634 extra_arguments.append(colors)
631 635 return base_launch_kernel('from IPython.zmq.ipkernel import main; main()',
632 636 xrep_port, pub_port, req_port, hb_port,
633 637 stdin, stdout, stderr,
634 638 executable, independent, extra_arguments)
635 639
636 640
637 641 def main():
638 642 """ The IPython kernel main entry point.
639 643 """
640 644 parser = make_argument_parser()
641 645 parser.add_argument('--pylab', type=str, metavar='GUI', nargs='?',
642 646 const='auto', help = \
643 647 "Pre-load matplotlib and numpy for interactive use. If GUI is not \
644 648 given, the GUI backend is matplotlib's, otherwise use one of: \
645 649 ['tk', 'gtk', 'qt', 'wx', 'osx', 'inline'].")
646 650 parser.add_argument('--colors',
647 651 type=str, dest='colors',
648 652 help="Set the color scheme (NoColor, Linux, and LightBG).",
649 653 metavar='ZMQInteractiveShell.colors')
650 654 namespace = parser.parse_args()
651 655
652 656 kernel_class = Kernel
653 657
654 658 kernel_classes = {
655 659 'qt' : QtKernel,
656 660 'qt4': QtKernel,
657 661 'inline': Kernel,
658 662 'osx': TkKernel,
659 663 'wx' : WxKernel,
660 664 'tk' : TkKernel,
661 665 'gtk': GTKKernel,
662 666 }
663 667 if namespace.pylab:
664 668 if namespace.pylab == 'auto':
665 669 gui, backend = pylabtools.find_gui_and_backend()
666 670 else:
667 671 gui, backend = pylabtools.find_gui_and_backend(namespace.pylab)
668 672 kernel_class = kernel_classes.get(gui)
669 673 if kernel_class is None:
670 674 raise ValueError('GUI is not supported: %r' % gui)
671 675 pylabtools.activate_matplotlib(backend)
672 676 if namespace.colors:
673 677 ZMQInteractiveShell.colors=namespace.colors
674 678
675 679 kernel = make_kernel(namespace, kernel_class, OutStream)
676 680
677 681 if namespace.pylab:
678 682 pylabtools.import_pylab(kernel.shell.user_ns, backend,
679 683 shell=kernel.shell)
680 684
681 685 start_kernel(namespace, kernel)
682 686
683 687
684 688 if __name__ == '__main__':
685 689 main()
General Comments 0
You need to be logged in to leave comments. Login now