##// END OF EJS Templates
Fix import of pylabtools that I missed yesterday.
Fernando Perez -
Show More
@@ -1,635 +1,635 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
26 26 # System library imports.
27 27 import zmq
28 28
29 29 # Local imports.
30 from IPython.core import pylabtools
30 31 from IPython.config.configurable import Configurable
31 32 from IPython.config.application import boolean_flag, catch_config_error
32 33 from IPython.core.application import ProfileDir
33 34 from IPython.core.error import StdinNotImplementedError
34 35 from IPython.core.shellapp import (
35 36 InteractiveShellApp, shell_flags, shell_aliases
36 37 )
37 38 from IPython.utils import io
38 39 from IPython.utils import py3compat
39 40 from IPython.utils.jsonutil import json_clean
40 from IPython.lib import pylabtools
41 41 from IPython.utils.traitlets import (
42 42 Any, Instance, Float, Dict, CaselessStrEnum
43 43 )
44 44
45 45 from entry_point import base_launch_kernel
46 46 from kernelapp import KernelApp, kernel_flags, kernel_aliases
47 47 from session import Session, Message
48 48 from zmqshell import ZMQInteractiveShell
49 49
50 50
51 51 #-----------------------------------------------------------------------------
52 52 # Main kernel class
53 53 #-----------------------------------------------------------------------------
54 54
55 55 class Kernel(Configurable):
56 56
57 57 #---------------------------------------------------------------------------
58 58 # Kernel interface
59 59 #---------------------------------------------------------------------------
60 60
61 61 # attribute to override with a GUI
62 62 eventloop = Any(None)
63 63
64 64 shell = Instance('IPython.core.interactiveshell.InteractiveShellABC')
65 65 session = Instance(Session)
66 66 profile_dir = Instance('IPython.core.profiledir.ProfileDir')
67 67 shell_socket = Instance('zmq.Socket')
68 68 iopub_socket = Instance('zmq.Socket')
69 69 stdin_socket = Instance('zmq.Socket')
70 70 log = Instance(logging.Logger)
71 71
72 72 # Private interface
73 73
74 74 # Time to sleep after flushing the stdout/err buffers in each execute
75 75 # cycle. While this introduces a hard limit on the minimal latency of the
76 76 # execute cycle, it helps prevent output synchronization problems for
77 77 # clients.
78 78 # Units are in seconds. The minimum zmq latency on local host is probably
79 79 # ~150 microseconds, set this to 500us for now. We may need to increase it
80 80 # a little if it's not enough after more interactive testing.
81 81 _execute_sleep = Float(0.0005, config=True)
82 82
83 83 # Frequency of the kernel's event loop.
84 84 # Units are in seconds, kernel subclasses for GUI toolkits may need to
85 85 # adapt to milliseconds.
86 86 _poll_interval = Float(0.05, config=True)
87 87
88 88 # If the shutdown was requested over the network, we leave here the
89 89 # necessary reply message so it can be sent by our registered atexit
90 90 # handler. This ensures that the reply is only sent to clients truly at
91 91 # the end of our shutdown process (which happens after the underlying
92 92 # IPython shell's own shutdown).
93 93 _shutdown_message = None
94 94
95 95 # This is a dict of port number that the kernel is listening on. It is set
96 96 # by record_ports and used by connect_request.
97 97 _recorded_ports = Dict()
98 98
99 99
100 100
101 101 def __init__(self, **kwargs):
102 102 super(Kernel, self).__init__(**kwargs)
103 103
104 104 # Before we even start up the shell, register *first* our exit handlers
105 105 # so they come before the shell's
106 106 atexit.register(self._at_shutdown)
107 107
108 108 # Initialize the InteractiveShell subclass
109 109 self.shell = ZMQInteractiveShell.instance(config=self.config,
110 110 profile_dir = self.profile_dir,
111 111 )
112 112 self.shell.displayhook.session = self.session
113 113 self.shell.displayhook.pub_socket = self.iopub_socket
114 114 self.shell.display_pub.session = self.session
115 115 self.shell.display_pub.pub_socket = self.iopub_socket
116 116
117 117 # TMP - hack while developing
118 118 self.shell._reply_content = None
119 119
120 120 # Build dict of handlers for message types
121 121 msg_types = [ 'execute_request', 'complete_request',
122 122 'object_info_request', 'history_request',
123 123 'connect_request', 'shutdown_request']
124 124 self.handlers = {}
125 125 for msg_type in msg_types:
126 126 self.handlers[msg_type] = getattr(self, msg_type)
127 127
128 128 def do_one_iteration(self):
129 129 """Do one iteration of the kernel's evaluation loop.
130 130 """
131 131 try:
132 132 ident,msg = self.session.recv(self.shell_socket, zmq.NOBLOCK)
133 133 except Exception:
134 134 self.log.warn("Invalid Message:", exc_info=True)
135 135 return
136 136 if msg is None:
137 137 return
138 138
139 139 msg_type = msg['header']['msg_type']
140 140
141 141 # This assert will raise in versions of zeromq 2.0.7 and lesser.
142 142 # We now require 2.0.8 or above, so we can uncomment for safety.
143 143 # print(ident,msg, file=sys.__stdout__)
144 144 assert ident is not None, "Missing message part."
145 145
146 146 # Print some info about this message and leave a '--->' marker, so it's
147 147 # easier to trace visually the message chain when debugging. Each
148 148 # handler prints its message at the end.
149 149 self.log.debug('\n*** MESSAGE TYPE:'+str(msg_type)+'***')
150 150 self.log.debug(' Content: '+str(msg['content'])+'\n --->\n ')
151 151
152 152 # Find and call actual handler for message
153 153 handler = self.handlers.get(msg_type, None)
154 154 if handler is None:
155 155 self.log.error("UNKNOWN MESSAGE TYPE:" +str(msg))
156 156 else:
157 157 handler(ident, msg)
158 158
159 159 # Check whether we should exit, in case the incoming message set the
160 160 # exit flag on
161 161 if self.shell.exit_now:
162 162 self.log.debug('\nExiting IPython kernel...')
163 163 # We do a normal, clean exit, which allows any actions registered
164 164 # via atexit (such as history saving) to take place.
165 165 sys.exit(0)
166 166
167 167
168 168 def start(self):
169 169 """ Start the kernel main loop.
170 170 """
171 171 poller = zmq.Poller()
172 172 poller.register(self.shell_socket, zmq.POLLIN)
173 173 # loop while self.eventloop has not been overridden
174 174 while self.eventloop is None:
175 175 try:
176 176 # scale by extra factor of 10, because there is no
177 177 # reason for this to be anything less than ~ 0.1s
178 178 # since it is a real poller and will respond
179 179 # to events immediately
180 180
181 181 # double nested try/except, to properly catch KeyboardInterrupt
182 182 # due to pyzmq Issue #130
183 183 try:
184 184 poller.poll(10*1000*self._poll_interval)
185 185 self.do_one_iteration()
186 186 except:
187 187 raise
188 188 except KeyboardInterrupt:
189 189 # Ctrl-C shouldn't crash the kernel
190 190 io.raw_print("KeyboardInterrupt caught in kernel")
191 191 if self.eventloop is not None:
192 192 try:
193 193 self.eventloop(self)
194 194 except KeyboardInterrupt:
195 195 # Ctrl-C shouldn't crash the kernel
196 196 io.raw_print("KeyboardInterrupt caught in kernel")
197 197
198 198
199 199 def record_ports(self, ports):
200 200 """Record the ports that this kernel is using.
201 201
202 202 The creator of the Kernel instance must call this methods if they
203 203 want the :meth:`connect_request` method to return the port numbers.
204 204 """
205 205 self._recorded_ports = ports
206 206
207 207 #---------------------------------------------------------------------------
208 208 # Kernel request handlers
209 209 #---------------------------------------------------------------------------
210 210
211 211 def _publish_pyin(self, code, parent):
212 212 """Publish the code request on the pyin stream."""
213 213
214 214 self.session.send(self.iopub_socket, u'pyin', {u'code':code},
215 215 parent=parent)
216 216
217 217 def execute_request(self, ident, parent):
218 218
219 219 self.session.send(self.iopub_socket,
220 220 u'status',
221 221 {u'execution_state':u'busy'},
222 222 parent=parent )
223 223
224 224 try:
225 225 content = parent[u'content']
226 226 code = content[u'code']
227 227 silent = content[u'silent']
228 228 except:
229 229 self.log.error("Got bad msg: ")
230 230 self.log.error(str(Message(parent)))
231 231 return
232 232
233 233 shell = self.shell # we'll need this a lot here
234 234
235 235 # Replace raw_input. Note that is not sufficient to replace
236 236 # raw_input in the user namespace.
237 237 if content.get('allow_stdin', False):
238 238 raw_input = lambda prompt='': self._raw_input(prompt, ident, parent)
239 239 else:
240 240 raw_input = lambda prompt='' : self._no_raw_input()
241 241
242 242 if py3compat.PY3:
243 243 __builtin__.input = raw_input
244 244 else:
245 245 __builtin__.raw_input = raw_input
246 246
247 247 # Set the parent message of the display hook and out streams.
248 248 shell.displayhook.set_parent(parent)
249 249 shell.display_pub.set_parent(parent)
250 250 sys.stdout.set_parent(parent)
251 251 sys.stderr.set_parent(parent)
252 252
253 253 # Re-broadcast our input for the benefit of listening clients, and
254 254 # start computing output
255 255 if not silent:
256 256 self._publish_pyin(code, parent)
257 257
258 258 reply_content = {}
259 259 try:
260 260 if silent:
261 261 # run_code uses 'exec' mode, so no displayhook will fire, and it
262 262 # doesn't call logging or history manipulations. Print
263 263 # statements in that code will obviously still execute.
264 264 shell.run_code(code)
265 265 else:
266 266 # FIXME: the shell calls the exception handler itself.
267 267 shell.run_cell(code, store_history=True)
268 268 except:
269 269 status = u'error'
270 270 # FIXME: this code right now isn't being used yet by default,
271 271 # because the run_cell() call above directly fires off exception
272 272 # reporting. This code, therefore, is only active in the scenario
273 273 # where runlines itself has an unhandled exception. We need to
274 274 # uniformize this, for all exception construction to come from a
275 275 # single location in the codbase.
276 276 etype, evalue, tb = sys.exc_info()
277 277 tb_list = traceback.format_exception(etype, evalue, tb)
278 278 reply_content.update(shell._showtraceback(etype, evalue, tb_list))
279 279 else:
280 280 status = u'ok'
281 281
282 282 reply_content[u'status'] = status
283 283
284 284 # Return the execution counter so clients can display prompts
285 285 reply_content['execution_count'] = shell.execution_count -1
286 286
287 287 # FIXME - fish exception info out of shell, possibly left there by
288 288 # runlines. We'll need to clean up this logic later.
289 289 if shell._reply_content is not None:
290 290 reply_content.update(shell._reply_content)
291 291 # reset after use
292 292 shell._reply_content = None
293 293
294 294 # At this point, we can tell whether the main code execution succeeded
295 295 # or not. If it did, we proceed to evaluate user_variables/expressions
296 296 if reply_content['status'] == 'ok':
297 297 reply_content[u'user_variables'] = \
298 298 shell.user_variables(content[u'user_variables'])
299 299 reply_content[u'user_expressions'] = \
300 300 shell.user_expressions(content[u'user_expressions'])
301 301 else:
302 302 # If there was an error, don't even try to compute variables or
303 303 # expressions
304 304 reply_content[u'user_variables'] = {}
305 305 reply_content[u'user_expressions'] = {}
306 306
307 307 # Payloads should be retrieved regardless of outcome, so we can both
308 308 # recover partial output (that could have been generated early in a
309 309 # block, before an error) and clear the payload system always.
310 310 reply_content[u'payload'] = shell.payload_manager.read_payload()
311 311 # Be agressive about clearing the payload because we don't want
312 312 # it to sit in memory until the next execute_request comes in.
313 313 shell.payload_manager.clear_payload()
314 314
315 315 # Flush output before sending the reply.
316 316 sys.stdout.flush()
317 317 sys.stderr.flush()
318 318 # FIXME: on rare occasions, the flush doesn't seem to make it to the
319 319 # clients... This seems to mitigate the problem, but we definitely need
320 320 # to better understand what's going on.
321 321 if self._execute_sleep:
322 322 time.sleep(self._execute_sleep)
323 323
324 324 # Send the reply.
325 325 reply_content = json_clean(reply_content)
326 326 reply_msg = self.session.send(self.shell_socket, u'execute_reply',
327 327 reply_content, parent, ident=ident)
328 328 self.log.debug(str(reply_msg))
329 329
330 330 if reply_msg['content']['status'] == u'error':
331 331 self._abort_queue()
332 332
333 333 self.session.send(self.iopub_socket,
334 334 u'status',
335 335 {u'execution_state':u'idle'},
336 336 parent=parent )
337 337
338 338 def complete_request(self, ident, parent):
339 339 txt, matches = self._complete(parent)
340 340 matches = {'matches' : matches,
341 341 'matched_text' : txt,
342 342 'status' : 'ok'}
343 343 matches = json_clean(matches)
344 344 completion_msg = self.session.send(self.shell_socket, 'complete_reply',
345 345 matches, parent, ident)
346 346 self.log.debug(str(completion_msg))
347 347
348 348 def object_info_request(self, ident, parent):
349 349 object_info = self.shell.object_inspect(parent['content']['oname'])
350 350 # Before we send this object over, we scrub it for JSON usage
351 351 oinfo = json_clean(object_info)
352 352 msg = self.session.send(self.shell_socket, 'object_info_reply',
353 353 oinfo, parent, ident)
354 354 self.log.debug(msg)
355 355
356 356 def history_request(self, ident, parent):
357 357 # We need to pull these out, as passing **kwargs doesn't work with
358 358 # unicode keys before Python 2.6.5.
359 359 hist_access_type = parent['content']['hist_access_type']
360 360 raw = parent['content']['raw']
361 361 output = parent['content']['output']
362 362 if hist_access_type == 'tail':
363 363 n = parent['content']['n']
364 364 hist = self.shell.history_manager.get_tail(n, raw=raw, output=output,
365 365 include_latest=True)
366 366
367 367 elif hist_access_type == 'range':
368 368 session = parent['content']['session']
369 369 start = parent['content']['start']
370 370 stop = parent['content']['stop']
371 371 hist = self.shell.history_manager.get_range(session, start, stop,
372 372 raw=raw, output=output)
373 373
374 374 elif hist_access_type == 'search':
375 375 pattern = parent['content']['pattern']
376 376 hist = self.shell.history_manager.search(pattern, raw=raw,
377 377 output=output)
378 378
379 379 else:
380 380 hist = []
381 381 content = {'history' : list(hist)}
382 382 content = json_clean(content)
383 383 msg = self.session.send(self.shell_socket, 'history_reply',
384 384 content, parent, ident)
385 385 self.log.debug(str(msg))
386 386
387 387 def connect_request(self, ident, parent):
388 388 if self._recorded_ports is not None:
389 389 content = self._recorded_ports.copy()
390 390 else:
391 391 content = {}
392 392 msg = self.session.send(self.shell_socket, 'connect_reply',
393 393 content, parent, ident)
394 394 self.log.debug(msg)
395 395
396 396 def shutdown_request(self, ident, parent):
397 397 self.shell.exit_now = True
398 398 self._shutdown_message = self.session.msg(u'shutdown_reply',
399 399 parent['content'], parent)
400 400 sys.exit(0)
401 401
402 402 #---------------------------------------------------------------------------
403 403 # Protected interface
404 404 #---------------------------------------------------------------------------
405 405
406 406 def _abort_queue(self):
407 407 while True:
408 408 try:
409 409 ident,msg = self.session.recv(self.shell_socket, zmq.NOBLOCK)
410 410 except Exception:
411 411 self.log.warn("Invalid Message:", exc_info=True)
412 412 continue
413 413 if msg is None:
414 414 break
415 415 else:
416 416 assert ident is not None, \
417 417 "Unexpected missing message part."
418 418
419 419 self.log.debug("Aborting:\n"+str(Message(msg)))
420 420 msg_type = msg['header']['msg_type']
421 421 reply_type = msg_type.split('_')[0] + '_reply'
422 422 reply_msg = self.session.send(self.shell_socket, reply_type,
423 423 {'status' : 'aborted'}, msg, ident=ident)
424 424 self.log.debug(reply_msg)
425 425 # We need to wait a bit for requests to come in. This can probably
426 426 # be set shorter for true asynchronous clients.
427 427 time.sleep(0.1)
428 428
429 429 def _no_raw_input(self):
430 430 """Raise StdinNotImplentedError if active frontend doesn't support
431 431 stdin."""
432 432 raise StdinNotImplementedError("raw_input was called, but this "
433 433 "frontend does not support stdin.")
434 434
435 435 def _raw_input(self, prompt, ident, parent):
436 436 # Flush output before making the request.
437 437 sys.stderr.flush()
438 438 sys.stdout.flush()
439 439
440 440 # Send the input request.
441 441 content = json_clean(dict(prompt=prompt))
442 442 self.session.send(self.stdin_socket, u'input_request', content, parent,
443 443 ident=ident)
444 444
445 445 # Await a response.
446 446 while True:
447 447 try:
448 448 ident, reply = self.session.recv(self.stdin_socket, 0)
449 449 except Exception:
450 450 self.log.warn("Invalid Message:", exc_info=True)
451 451 else:
452 452 break
453 453 try:
454 454 value = reply['content']['value']
455 455 except:
456 456 self.log.error("Got bad raw_input reply: ")
457 457 self.log.error(str(Message(parent)))
458 458 value = ''
459 459 return value
460 460
461 461 def _complete(self, msg):
462 462 c = msg['content']
463 463 try:
464 464 cpos = int(c['cursor_pos'])
465 465 except:
466 466 # If we don't get something that we can convert to an integer, at
467 467 # least attempt the completion guessing the cursor is at the end of
468 468 # the text, if there's any, and otherwise of the line
469 469 cpos = len(c['text'])
470 470 if cpos==0:
471 471 cpos = len(c['line'])
472 472 return self.shell.complete(c['text'], c['line'], cpos)
473 473
474 474 def _object_info(self, context):
475 475 symbol, leftover = self._symbol_from_context(context)
476 476 if symbol is not None and not leftover:
477 477 doc = getattr(symbol, '__doc__', '')
478 478 else:
479 479 doc = ''
480 480 object_info = dict(docstring = doc)
481 481 return object_info
482 482
483 483 def _symbol_from_context(self, context):
484 484 if not context:
485 485 return None, context
486 486
487 487 base_symbol_string = context[0]
488 488 symbol = self.shell.user_ns.get(base_symbol_string, None)
489 489 if symbol is None:
490 490 symbol = __builtin__.__dict__.get(base_symbol_string, None)
491 491 if symbol is None:
492 492 return None, context
493 493
494 494 context = context[1:]
495 495 for i, name in enumerate(context):
496 496 new_symbol = getattr(symbol, name, None)
497 497 if new_symbol is None:
498 498 return symbol, context[i:]
499 499 else:
500 500 symbol = new_symbol
501 501
502 502 return symbol, []
503 503
504 504 def _at_shutdown(self):
505 505 """Actions taken at shutdown by the kernel, called by python's atexit.
506 506 """
507 507 # io.rprint("Kernel at_shutdown") # dbg
508 508 if self._shutdown_message is not None:
509 509 self.session.send(self.shell_socket, self._shutdown_message)
510 510 self.session.send(self.iopub_socket, self._shutdown_message)
511 511 self.log.debug(str(self._shutdown_message))
512 512 # A very short sleep to give zmq time to flush its message buffers
513 513 # before Python truly shuts down.
514 514 time.sleep(0.01)
515 515
516 516 #-----------------------------------------------------------------------------
517 517 # Aliases and Flags for the IPKernelApp
518 518 #-----------------------------------------------------------------------------
519 519
520 520 flags = dict(kernel_flags)
521 521 flags.update(shell_flags)
522 522
523 523 addflag = lambda *args: flags.update(boolean_flag(*args))
524 524
525 525 flags['pylab'] = (
526 526 {'IPKernelApp' : {'pylab' : 'auto'}},
527 527 """Pre-load matplotlib and numpy for interactive use with
528 528 the default matplotlib backend."""
529 529 )
530 530
531 531 aliases = dict(kernel_aliases)
532 532 aliases.update(shell_aliases)
533 533
534 534 # it's possible we don't want short aliases for *all* of these:
535 535 aliases.update(dict(
536 536 pylab='IPKernelApp.pylab',
537 537 ))
538 538
539 539 #-----------------------------------------------------------------------------
540 540 # The IPKernelApp class
541 541 #-----------------------------------------------------------------------------
542 542
543 543 class IPKernelApp(KernelApp, InteractiveShellApp):
544 544 name = 'ipkernel'
545 545
546 546 aliases = Dict(aliases)
547 547 flags = Dict(flags)
548 548 classes = [Kernel, ZMQInteractiveShell, ProfileDir, Session]
549 549 # configurables
550 550 pylab = CaselessStrEnum(['tk', 'qt', 'wx', 'gtk', 'osx', 'inline', 'auto'],
551 551 config=True,
552 552 help="""Pre-load matplotlib and numpy for interactive use,
553 553 selecting a particular matplotlib backend and loop integration.
554 554 """
555 555 )
556 556
557 557 @catch_config_error
558 558 def initialize(self, argv=None):
559 559 super(IPKernelApp, self).initialize(argv)
560 560 self.init_shell()
561 561 self.init_extensions()
562 562 self.init_code()
563 563
564 564 def init_kernel(self):
565 565
566 566 kernel = Kernel(config=self.config, session=self.session,
567 567 shell_socket=self.shell_socket,
568 568 iopub_socket=self.iopub_socket,
569 569 stdin_socket=self.stdin_socket,
570 570 log=self.log,
571 571 profile_dir=self.profile_dir,
572 572 )
573 573 self.kernel = kernel
574 574 kernel.record_ports(self.ports)
575 575 shell = kernel.shell
576 576 if self.pylab:
577 577 try:
578 578 gui, backend = pylabtools.find_gui_and_backend(self.pylab)
579 579 shell.enable_pylab(gui, import_all=self.pylab_import_all)
580 580 except Exception:
581 581 self.log.error("Pylab initialization failed", exc_info=True)
582 582 # print exception straight to stdout, because normally
583 583 # _showtraceback associates the reply with an execution,
584 584 # which means frontends will never draw it, as this exception
585 585 # is not associated with any execute request.
586 586
587 587 # replace pyerr-sending traceback with stdout
588 588 _showtraceback = shell._showtraceback
589 589 def print_tb(etype, evalue, stb):
590 590 print ("Error initializing pylab, pylab mode will not "
591 591 "be active", file=io.stderr)
592 592 print (shell.InteractiveTB.stb2text(stb), file=io.stdout)
593 593 shell._showtraceback = print_tb
594 594
595 595 # send the traceback over stdout
596 596 shell.showtraceback(tb_offset=0)
597 597
598 598 # restore proper _showtraceback method
599 599 shell._showtraceback = _showtraceback
600 600
601 601
602 602 def init_shell(self):
603 603 self.shell = self.kernel.shell
604 604 self.shell.configurables.append(self)
605 605
606 606
607 607 #-----------------------------------------------------------------------------
608 608 # Kernel main and launch functions
609 609 #-----------------------------------------------------------------------------
610 610
611 611 def launch_kernel(*args, **kwargs):
612 612 """Launches a localhost IPython kernel, binding to the specified ports.
613 613
614 614 This function simply calls entry_point.base_launch_kernel with the right
615 615 first command to start an ipkernel. See base_launch_kernel for arguments.
616 616
617 617 Returns
618 618 -------
619 619 A tuple of form:
620 620 (kernel_process, shell_port, iopub_port, stdin_port, hb_port)
621 621 where kernel_process is a Popen object and the ports are integers.
622 622 """
623 623 return base_launch_kernel('from IPython.zmq.ipkernel import main; main()',
624 624 *args, **kwargs)
625 625
626 626
627 627 def main():
628 628 """Run an IPKernel as an application"""
629 629 app = IPKernelApp.instance()
630 630 app.initialize()
631 631 app.start()
632 632
633 633
634 634 if __name__ == '__main__':
635 635 main()
@@ -1,513 +1,512 b''
1 1 """A ZMQ-based subclass of InteractiveShell.
2 2
3 3 This code is meant to ease the refactoring of the base InteractiveShell into
4 4 something with a cleaner architecture for 2-process use, without actually
5 5 breaking InteractiveShell itself. So we're doing something a bit ugly, where
6 6 we subclass and override what we want to fix. Once this is working well, we
7 7 can go back to the base class and refactor the code for a cleaner inheritance
8 8 implementation that doesn't rely on so much monkeypatching.
9 9
10 10 But this lets us maintain a fully working IPython as we develop the new
11 11 machinery. This should thus be thought of as scaffolding.
12 12 """
13 13 #-----------------------------------------------------------------------------
14 14 # Imports
15 15 #-----------------------------------------------------------------------------
16 16 from __future__ import print_function
17 17
18 18 # Stdlib
19 19 import inspect
20 20 import os
21 21 import sys
22 22 from subprocess import Popen, PIPE
23 23
24 24 # Our own
25 25 from IPython.core.interactiveshell import (
26 26 InteractiveShell, InteractiveShellABC
27 27 )
28 from IPython.core import page
28 from IPython.core import page, pylabtools
29 29 from IPython.core.autocall import ZMQExitAutocall
30 30 from IPython.core.displaypub import DisplayPublisher
31 31 from IPython.core.macro import Macro
32 32 from IPython.core.magic import MacroToEdit
33 33 from IPython.core.payloadpage import install_payload_page
34 from IPython.lib import pylabtools
35 34 from IPython.lib.kernel import (
36 35 get_connection_file, get_connection_info, connect_qtconsole
37 36 )
38 37 from IPython.utils import io
39 38 from IPython.utils.jsonutil import json_clean
40 39 from IPython.utils.path import get_py_filename
41 40 from IPython.utils.process import arg_split
42 41 from IPython.utils.traitlets import Instance, Type, Dict, CBool
43 42 from IPython.utils.warn import warn, error
44 43 from IPython.zmq.displayhook import ZMQShellDisplayHook, _encode_binary
45 44 from IPython.zmq.session import extract_header
46 45 from session import Session
47 46
48 47 #-----------------------------------------------------------------------------
49 48 # Globals and side-effects
50 49 #-----------------------------------------------------------------------------
51 50
52 51 # Install the payload version of page.
53 52 install_payload_page()
54 53
55 54 #-----------------------------------------------------------------------------
56 55 # Functions and classes
57 56 #-----------------------------------------------------------------------------
58 57
59 58 class ZMQDisplayPublisher(DisplayPublisher):
60 59 """A display publisher that publishes data using a ZeroMQ PUB socket."""
61 60
62 61 session = Instance(Session)
63 62 pub_socket = Instance('zmq.Socket')
64 63 parent_header = Dict({})
65 64
66 65 def set_parent(self, parent):
67 66 """Set the parent for outbound messages."""
68 67 self.parent_header = extract_header(parent)
69 68
70 69 def publish(self, source, data, metadata=None):
71 70 if metadata is None:
72 71 metadata = {}
73 72 self._validate_data(source, data, metadata)
74 73 content = {}
75 74 content['source'] = source
76 75 _encode_binary(data)
77 76 content['data'] = data
78 77 content['metadata'] = metadata
79 78 self.session.send(
80 79 self.pub_socket, u'display_data', json_clean(content),
81 80 parent=self.parent_header
82 81 )
83 82
84 83 def clear_output(self, stdout=True, stderr=True, other=True):
85 84 content = dict(stdout=stdout, stderr=stderr, other=other)
86 85 self.session.send(
87 86 self.pub_socket, u'clear_output', content,
88 87 parent=self.parent_header
89 88 )
90 89
91 90 class ZMQInteractiveShell(InteractiveShell):
92 91 """A subclass of InteractiveShell for ZMQ."""
93 92
94 93 displayhook_class = Type(ZMQShellDisplayHook)
95 94 display_pub_class = Type(ZMQDisplayPublisher)
96 95
97 96 # Override the traitlet in the parent class, because there's no point using
98 97 # readline for the kernel. Can be removed when the readline code is moved
99 98 # to the terminal frontend.
100 99 colors_force = CBool(True)
101 100 readline_use = CBool(False)
102 101 # autoindent has no meaning in a zmqshell, and attempting to enable it
103 102 # will print a warning in the absence of readline.
104 103 autoindent = CBool(False)
105 104
106 105 exiter = Instance(ZMQExitAutocall)
107 106 def _exiter_default(self):
108 107 return ZMQExitAutocall(self)
109 108
110 109 keepkernel_on_exit = None
111 110
112 111 # Over ZeroMQ, GUI control isn't done with PyOS_InputHook as there is no
113 112 # interactive input being read; we provide event loop support in ipkernel
114 113 from .eventloops import enable_gui
115 114 enable_gui = staticmethod(enable_gui)
116 115
117 116 def init_environment(self):
118 117 """Configure the user's environment.
119 118
120 119 """
121 120 env = os.environ
122 121 # These two ensure 'ls' produces nice coloring on BSD-derived systems
123 122 env['TERM'] = 'xterm-color'
124 123 env['CLICOLOR'] = '1'
125 124 # Since normal pagers don't work at all (over pexpect we don't have
126 125 # single-key control of the subprocess), try to disable paging in
127 126 # subprocesses as much as possible.
128 127 env['PAGER'] = 'cat'
129 128 env['GIT_PAGER'] = 'cat'
130 129
131 130 def auto_rewrite_input(self, cmd):
132 131 """Called to show the auto-rewritten input for autocall and friends.
133 132
134 133 FIXME: this payload is currently not correctly processed by the
135 134 frontend.
136 135 """
137 136 new = self.displayhook.prompt1.auto_rewrite() + cmd
138 137 payload = dict(
139 138 source='IPython.zmq.zmqshell.ZMQInteractiveShell.auto_rewrite_input',
140 139 transformed_input=new,
141 140 )
142 141 self.payload_manager.write_payload(payload)
143 142
144 143 def ask_exit(self):
145 144 """Engage the exit actions."""
146 145 payload = dict(
147 146 source='IPython.zmq.zmqshell.ZMQInteractiveShell.ask_exit',
148 147 exit=True,
149 148 keepkernel=self.keepkernel_on_exit,
150 149 )
151 150 self.payload_manager.write_payload(payload)
152 151
153 152 def _showtraceback(self, etype, evalue, stb):
154 153
155 154 exc_content = {
156 155 u'traceback' : stb,
157 156 u'ename' : unicode(etype.__name__),
158 157 u'evalue' : unicode(evalue)
159 158 }
160 159
161 160 dh = self.displayhook
162 161 # Send exception info over pub socket for other clients than the caller
163 162 # to pick up
164 163 exc_msg = dh.session.send(dh.pub_socket, u'pyerr', json_clean(exc_content), dh.parent_header)
165 164
166 165 # FIXME - Hack: store exception info in shell object. Right now, the
167 166 # caller is reading this info after the fact, we need to fix this logic
168 167 # to remove this hack. Even uglier, we need to store the error status
169 168 # here, because in the main loop, the logic that sets it is being
170 169 # skipped because runlines swallows the exceptions.
171 170 exc_content[u'status'] = u'error'
172 171 self._reply_content = exc_content
173 172 # /FIXME
174 173
175 174 return exc_content
176 175
177 176 #------------------------------------------------------------------------
178 177 # Magic overrides
179 178 #------------------------------------------------------------------------
180 179 # Once the base class stops inheriting from magic, this code needs to be
181 180 # moved into a separate machinery as well. For now, at least isolate here
182 181 # the magics which this class needs to implement differently from the base
183 182 # class, or that are unique to it.
184 183
185 184 def magic_doctest_mode(self,parameter_s=''):
186 185 """Toggle doctest mode on and off.
187 186
188 187 This mode is intended to make IPython behave as much as possible like a
189 188 plain Python shell, from the perspective of how its prompts, exceptions
190 189 and output look. This makes it easy to copy and paste parts of a
191 190 session into doctests. It does so by:
192 191
193 192 - Changing the prompts to the classic ``>>>`` ones.
194 193 - Changing the exception reporting mode to 'Plain'.
195 194 - Disabling pretty-printing of output.
196 195
197 196 Note that IPython also supports the pasting of code snippets that have
198 197 leading '>>>' and '...' prompts in them. This means that you can paste
199 198 doctests from files or docstrings (even if they have leading
200 199 whitespace), and the code will execute correctly. You can then use
201 200 '%history -t' to see the translated history; this will give you the
202 201 input after removal of all the leading prompts and whitespace, which
203 202 can be pasted back into an editor.
204 203
205 204 With these features, you can switch into this mode easily whenever you
206 205 need to do testing and changes to doctests, without having to leave
207 206 your existing IPython session.
208 207 """
209 208
210 209 from IPython.utils.ipstruct import Struct
211 210
212 211 # Shorthands
213 212 shell = self.shell
214 213 disp_formatter = self.shell.display_formatter
215 214 ptformatter = disp_formatter.formatters['text/plain']
216 215 # dstore is a data store kept in the instance metadata bag to track any
217 216 # changes we make, so we can undo them later.
218 217 dstore = shell.meta.setdefault('doctest_mode', Struct())
219 218 save_dstore = dstore.setdefault
220 219
221 220 # save a few values we'll need to recover later
222 221 mode = save_dstore('mode', False)
223 222 save_dstore('rc_pprint', ptformatter.pprint)
224 223 save_dstore('rc_plain_text_only',disp_formatter.plain_text_only)
225 224 save_dstore('xmode', shell.InteractiveTB.mode)
226 225
227 226 if mode == False:
228 227 # turn on
229 228 ptformatter.pprint = False
230 229 disp_formatter.plain_text_only = True
231 230 shell.magic_xmode('Plain')
232 231 else:
233 232 # turn off
234 233 ptformatter.pprint = dstore.rc_pprint
235 234 disp_formatter.plain_text_only = dstore.rc_plain_text_only
236 235 shell.magic_xmode(dstore.xmode)
237 236
238 237 # Store new mode and inform on console
239 238 dstore.mode = bool(1-int(mode))
240 239 mode_label = ['OFF','ON'][dstore.mode]
241 240 print('Doctest mode is:', mode_label)
242 241
243 242 # Send the payload back so that clients can modify their prompt display
244 243 payload = dict(
245 244 source='IPython.zmq.zmqshell.ZMQInteractiveShell.magic_doctest_mode',
246 245 mode=dstore.mode)
247 246 self.payload_manager.write_payload(payload)
248 247
249 248 def magic_edit(self,parameter_s='',last_call=['','']):
250 249 """Bring up an editor and execute the resulting code.
251 250
252 251 Usage:
253 252 %edit [options] [args]
254 253
255 254 %edit runs an external text editor. You will need to set the command for
256 255 this editor via the ``TerminalInteractiveShell.editor`` option in your
257 256 configuration file before it will work.
258 257
259 258 This command allows you to conveniently edit multi-line code right in
260 259 your IPython session.
261 260
262 261 If called without arguments, %edit opens up an empty editor with a
263 262 temporary file and will execute the contents of this file when you
264 263 close it (don't forget to save it!).
265 264
266 265
267 266 Options:
268 267
269 268 -n <number>: open the editor at a specified line number. By default,
270 269 the IPython editor hook uses the unix syntax 'editor +N filename', but
271 270 you can configure this by providing your own modified hook if your
272 271 favorite editor supports line-number specifications with a different
273 272 syntax.
274 273
275 274 -p: this will call the editor with the same data as the previous time
276 275 it was used, regardless of how long ago (in your current session) it
277 276 was.
278 277
279 278 -r: use 'raw' input. This option only applies to input taken from the
280 279 user's history. By default, the 'processed' history is used, so that
281 280 magics are loaded in their transformed version to valid Python. If
282 281 this option is given, the raw input as typed as the command line is
283 282 used instead. When you exit the editor, it will be executed by
284 283 IPython's own processor.
285 284
286 285 -x: do not execute the edited code immediately upon exit. This is
287 286 mainly useful if you are editing programs which need to be called with
288 287 command line arguments, which you can then do using %run.
289 288
290 289
291 290 Arguments:
292 291
293 292 If arguments are given, the following possibilites exist:
294 293
295 294 - The arguments are numbers or pairs of colon-separated numbers (like
296 295 1 4:8 9). These are interpreted as lines of previous input to be
297 296 loaded into the editor. The syntax is the same of the %macro command.
298 297
299 298 - If the argument doesn't start with a number, it is evaluated as a
300 299 variable and its contents loaded into the editor. You can thus edit
301 300 any string which contains python code (including the result of
302 301 previous edits).
303 302
304 303 - If the argument is the name of an object (other than a string),
305 304 IPython will try to locate the file where it was defined and open the
306 305 editor at the point where it is defined. You can use `%edit function`
307 306 to load an editor exactly at the point where 'function' is defined,
308 307 edit it and have the file be executed automatically.
309 308
310 309 If the object is a macro (see %macro for details), this opens up your
311 310 specified editor with a temporary file containing the macro's data.
312 311 Upon exit, the macro is reloaded with the contents of the file.
313 312
314 313 Note: opening at an exact line is only supported under Unix, and some
315 314 editors (like kedit and gedit up to Gnome 2.8) do not understand the
316 315 '+NUMBER' parameter necessary for this feature. Good editors like
317 316 (X)Emacs, vi, jed, pico and joe all do.
318 317
319 318 - If the argument is not found as a variable, IPython will look for a
320 319 file with that name (adding .py if necessary) and load it into the
321 320 editor. It will execute its contents with execfile() when you exit,
322 321 loading any code in the file into your interactive namespace.
323 322
324 323 After executing your code, %edit will return as output the code you
325 324 typed in the editor (except when it was an existing file). This way
326 325 you can reload the code in further invocations of %edit as a variable,
327 326 via _<NUMBER> or Out[<NUMBER>], where <NUMBER> is the prompt number of
328 327 the output.
329 328
330 329 Note that %edit is also available through the alias %ed.
331 330
332 331 This is an example of creating a simple function inside the editor and
333 332 then modifying it. First, start up the editor:
334 333
335 334 In [1]: ed
336 335 Editing... done. Executing edited code...
337 336 Out[1]: 'def foo():n print "foo() was defined in an editing session"n'
338 337
339 338 We can then call the function foo():
340 339
341 340 In [2]: foo()
342 341 foo() was defined in an editing session
343 342
344 343 Now we edit foo. IPython automatically loads the editor with the
345 344 (temporary) file where foo() was previously defined:
346 345
347 346 In [3]: ed foo
348 347 Editing... done. Executing edited code...
349 348
350 349 And if we call foo() again we get the modified version:
351 350
352 351 In [4]: foo()
353 352 foo() has now been changed!
354 353
355 354 Here is an example of how to edit a code snippet successive
356 355 times. First we call the editor:
357 356
358 357 In [5]: ed
359 358 Editing... done. Executing edited code...
360 359 hello
361 360 Out[5]: "print 'hello'n"
362 361
363 362 Now we call it again with the previous output (stored in _):
364 363
365 364 In [6]: ed _
366 365 Editing... done. Executing edited code...
367 366 hello world
368 367 Out[6]: "print 'hello world'n"
369 368
370 369 Now we call it with the output #8 (stored in _8, also as Out[8]):
371 370
372 371 In [7]: ed _8
373 372 Editing... done. Executing edited code...
374 373 hello again
375 374 Out[7]: "print 'hello again'n"
376 375 """
377 376
378 377 opts,args = self.parse_options(parameter_s,'prn:')
379 378
380 379 try:
381 380 filename, lineno, _ = self._find_edit_target(args, opts, last_call)
382 381 except MacroToEdit as e:
383 382 # TODO: Implement macro editing over 2 processes.
384 383 print("Macro editing not yet implemented in 2-process model.")
385 384 return
386 385
387 386 # Make sure we send to the client an absolute path, in case the working
388 387 # directory of client and kernel don't match
389 388 filename = os.path.abspath(filename)
390 389
391 390 payload = {
392 391 'source' : 'IPython.zmq.zmqshell.ZMQInteractiveShell.edit_magic',
393 392 'filename' : filename,
394 393 'line_number' : lineno
395 394 }
396 395 self.payload_manager.write_payload(payload)
397 396
398 397 # A few magics that are adapted to the specifics of using pexpect and a
399 398 # remote terminal
400 399
401 400 def magic_clear(self, arg_s):
402 401 """Clear the terminal."""
403 402 if os.name == 'posix':
404 403 self.shell.system("clear")
405 404 else:
406 405 self.shell.system("cls")
407 406
408 407 if os.name == 'nt':
409 408 # This is the usual name in windows
410 409 magic_cls = magic_clear
411 410
412 411 # Terminal pagers won't work over pexpect, but we do have our own pager
413 412
414 413 def magic_less(self, arg_s):
415 414 """Show a file through the pager.
416 415
417 416 Files ending in .py are syntax-highlighted."""
418 417 cont = open(arg_s).read()
419 418 if arg_s.endswith('.py'):
420 419 cont = self.shell.pycolorize(cont)
421 420 page.page(cont)
422 421
423 422 magic_more = magic_less
424 423
425 424 # Man calls a pager, so we also need to redefine it
426 425 if os.name == 'posix':
427 426 def magic_man(self, arg_s):
428 427 """Find the man page for the given command and display in pager."""
429 428 page.page(self.shell.getoutput('man %s | col -b' % arg_s,
430 429 split=False))
431 430
432 431 # FIXME: this is specific to the GUI, so we should let the gui app load
433 432 # magics at startup that are only for the gui. Once the gui app has proper
434 433 # profile and configuration management, we can have it initialize a kernel
435 434 # with a special config file that provides these.
436 435 def magic_guiref(self, arg_s):
437 436 """Show a basic reference about the GUI console."""
438 437 from IPython.core.usage import gui_reference
439 438 page.page(gui_reference, auto_html=True)
440 439
441 440 def magic_connect_info(self, arg_s):
442 441 """Print information for connecting other clients to this kernel
443 442
444 443 It will print the contents of this session's connection file, as well as
445 444 shortcuts for local clients.
446 445
447 446 In the simplest case, when called from the most recently launched kernel,
448 447 secondary clients can be connected, simply with:
449 448
450 449 $> ipython <app> --existing
451 450
452 451 """
453 452
454 453 from IPython.core.application import BaseIPythonApplication as BaseIPApp
455 454
456 455 if BaseIPApp.initialized():
457 456 app = BaseIPApp.instance()
458 457 security_dir = app.profile_dir.security_dir
459 458 profile = app.profile
460 459 else:
461 460 profile = 'default'
462 461 security_dir = ''
463 462
464 463 try:
465 464 connection_file = get_connection_file()
466 465 info = get_connection_info(unpack=False)
467 466 except Exception as e:
468 467 error("Could not get connection info: %r" % e)
469 468 return
470 469
471 470 # add profile flag for non-default profile
472 471 profile_flag = "--profile %s" % profile if profile != 'default' else ""
473 472
474 473 # if it's in the security dir, truncate to basename
475 474 if security_dir == os.path.dirname(connection_file):
476 475 connection_file = os.path.basename(connection_file)
477 476
478 477
479 478 print (info + '\n')
480 479 print ("Paste the above JSON into a file, and connect with:\n"
481 480 " $> ipython <app> --existing <file>\n"
482 481 "or, if you are local, you can connect with just:\n"
483 482 " $> ipython <app> --existing {0} {1}\n"
484 483 "or even just:\n"
485 484 " $> ipython <app> --existing {1}\n"
486 485 "if this is the most recent IPython session you have started.".format(
487 486 connection_file, profile_flag
488 487 )
489 488 )
490 489
491 490 def magic_qtconsole(self, arg_s):
492 491 """Open a qtconsole connected to this kernel.
493 492
494 493 Useful for connecting a qtconsole to running notebooks, for better
495 494 debugging.
496 495 """
497 496 try:
498 497 p = connect_qtconsole(argv=arg_split(arg_s, os.name=='posix'))
499 498 except Exception as e:
500 499 error("Could not start qtconsole: %r" % e)
501 500 return
502 501
503 502 def set_next_input(self, text):
504 503 """Send the specified text to the frontend to be presented at the next
505 504 input cell."""
506 505 payload = dict(
507 506 source='IPython.zmq.zmqshell.ZMQInteractiveShell.set_next_input',
508 507 text=text
509 508 )
510 509 self.payload_manager.write_payload(payload)
511 510
512 511
513 512 InteractiveShellABC.register(ZMQInteractiveShell)
General Comments 0
You need to be logged in to leave comments. Login now