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