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