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