##// END OF EJS Templates
Using session.send in DisplayPublisher now.
Brian Granger -
Show More
@@ -1,610 +1,613 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 re
22 22
23 23 # Our own
24 24 from IPython.core.interactiveshell import (
25 25 InteractiveShell, InteractiveShellABC
26 26 )
27 27 from IPython.core import page
28 28 from IPython.core.displayhook import DisplayHook
29 29 from IPython.core.displaypub import DisplayPublisher
30 30 from IPython.core.macro import Macro
31 31 from IPython.core.payloadpage import install_payload_page
32 32 from IPython.utils import io
33 33 from IPython.utils.path import get_py_filename
34 34 from IPython.utils.text import StringTypes
35 35 from IPython.utils.traitlets import Instance, Type, Dict
36 36 from IPython.utils.warn import warn
37 37 from IPython.zmq.session import extract_header
38 38 from session import Session
39 39
40 40 #-----------------------------------------------------------------------------
41 41 # Globals and side-effects
42 42 #-----------------------------------------------------------------------------
43 43
44 44 # Install the payload version of page.
45 45 install_payload_page()
46 46
47 47 #-----------------------------------------------------------------------------
48 48 # Functions and classes
49 49 #-----------------------------------------------------------------------------
50 50
51 51 class ZMQDisplayHook(DisplayHook):
52 52 """A displayhook subclass that publishes data using ZeroMQ."""
53 53
54 54 session = Instance(Session)
55 55 pub_socket = Instance('zmq.Socket')
56 56 parent_header = Dict({})
57 57
58 58 def set_parent(self, parent):
59 59 """Set the parent for outbound messages."""
60 60 self.parent_header = extract_header(parent)
61 61
62 62 def start_displayhook(self):
63 63 self.msg = self.session.msg(u'pyout', {}, parent=self.parent_header)
64 64
65 65 def write_output_prompt(self):
66 66 """Write the output prompt."""
67 67 if self.do_full_cache:
68 68 self.msg['content']['execution_count'] = self.prompt_count
69 69
70 70 def write_format_data(self, format_dict):
71 71 self.msg['content']['data'] = format_dict
72 72
73 73 def finish_displayhook(self):
74 74 """Finish up all displayhook activities."""
75 75 self.session.send(self.pub_socket, self.msg)
76 76 self.msg = None
77 77
78 78
79 79 class ZMQDisplayPublisher(DisplayPublisher):
80 80 """A display publisher that publishes data using a ZeroMQ PUB socket."""
81 81
82 82 session = Instance(Session)
83 83 pub_socket = Instance('zmq.Socket')
84 84 parent_header = Dict({})
85 85
86 86 def set_parent(self, parent):
87 87 """Set the parent for outbound messages."""
88 88 self.parent_header = extract_header(parent)
89 89
90 90 def publish(self, source, data, metadata=None):
91 91 if metadata is None:
92 92 metadata = {}
93 93 self._validate_data(source, data, metadata)
94 msg = self.session.msg(u'display_data', {}, parent=self.parent_header)
95 msg['content']['source'] = source
96 msg['content']['data'] = data
97 msg['content']['metadata'] = metadata
98 self.pub_socket.send_json(msg)
94 content = {}
95 content['source'] = source
96 content['data'] = data
97 content['metadata'] = metadata
98 self.session.send(
99 self.pub_socket, u'display_data', content,
100 parent=self.parent_header
101 )
99 102
100 103
101 104 class ZMQInteractiveShell(InteractiveShell):
102 105 """A subclass of InteractiveShell for ZMQ."""
103 106
104 107 displayhook_class = Type(ZMQDisplayHook)
105 108 display_pub_class = Type(ZMQDisplayPublisher)
106 109
107 110 keepkernel_on_exit = None
108 111
109 112 def init_environment(self):
110 113 """Configure the user's environment.
111 114
112 115 """
113 116 env = os.environ
114 117 # These two ensure 'ls' produces nice coloring on BSD-derived systems
115 118 env['TERM'] = 'xterm-color'
116 119 env['CLICOLOR'] = '1'
117 120 # Since normal pagers don't work at all (over pexpect we don't have
118 121 # single-key control of the subprocess), try to disable paging in
119 122 # subprocesses as much as possible.
120 123 env['PAGER'] = 'cat'
121 124 env['GIT_PAGER'] = 'cat'
122 125
123 126 def auto_rewrite_input(self, cmd):
124 127 """Called to show the auto-rewritten input for autocall and friends.
125 128
126 129 FIXME: this payload is currently not correctly processed by the
127 130 frontend.
128 131 """
129 132 new = self.displayhook.prompt1.auto_rewrite() + cmd
130 133 payload = dict(
131 134 source='IPython.zmq.zmqshell.ZMQInteractiveShell.auto_rewrite_input',
132 135 transformed_input=new,
133 136 )
134 137 self.payload_manager.write_payload(payload)
135 138
136 139 def ask_exit(self):
137 140 """Engage the exit actions."""
138 141 payload = dict(
139 142 source='IPython.zmq.zmqshell.ZMQInteractiveShell.ask_exit',
140 143 exit=True,
141 144 keepkernel=self.keepkernel_on_exit,
142 145 )
143 146 self.payload_manager.write_payload(payload)
144 147
145 148 def _showtraceback(self, etype, evalue, stb):
146 149
147 150 exc_content = {
148 151 u'traceback' : stb,
149 152 u'ename' : unicode(etype.__name__),
150 153 u'evalue' : unicode(evalue)
151 154 }
152 155
153 156 dh = self.displayhook
154 157 # Send exception info over pub socket for other clients than the caller
155 158 # to pick up
156 159 exc_msg = dh.session.send(dh.pub_socket, u'pyerr', exc_content, dh.parent_header)
157 160
158 161 # FIXME - Hack: store exception info in shell object. Right now, the
159 162 # caller is reading this info after the fact, we need to fix this logic
160 163 # to remove this hack. Even uglier, we need to store the error status
161 164 # here, because in the main loop, the logic that sets it is being
162 165 # skipped because runlines swallows the exceptions.
163 166 exc_content[u'status'] = u'error'
164 167 self._reply_content = exc_content
165 168 # /FIXME
166 169
167 170 return exc_content
168 171
169 172 #------------------------------------------------------------------------
170 173 # Magic overrides
171 174 #------------------------------------------------------------------------
172 175 # Once the base class stops inheriting from magic, this code needs to be
173 176 # moved into a separate machinery as well. For now, at least isolate here
174 177 # the magics which this class needs to implement differently from the base
175 178 # class, or that are unique to it.
176 179
177 180 def magic_doctest_mode(self,parameter_s=''):
178 181 """Toggle doctest mode on and off.
179 182
180 183 This mode is intended to make IPython behave as much as possible like a
181 184 plain Python shell, from the perspective of how its prompts, exceptions
182 185 and output look. This makes it easy to copy and paste parts of a
183 186 session into doctests. It does so by:
184 187
185 188 - Changing the prompts to the classic ``>>>`` ones.
186 189 - Changing the exception reporting mode to 'Plain'.
187 190 - Disabling pretty-printing of output.
188 191
189 192 Note that IPython also supports the pasting of code snippets that have
190 193 leading '>>>' and '...' prompts in them. This means that you can paste
191 194 doctests from files or docstrings (even if they have leading
192 195 whitespace), and the code will execute correctly. You can then use
193 196 '%history -t' to see the translated history; this will give you the
194 197 input after removal of all the leading prompts and whitespace, which
195 198 can be pasted back into an editor.
196 199
197 200 With these features, you can switch into this mode easily whenever you
198 201 need to do testing and changes to doctests, without having to leave
199 202 your existing IPython session.
200 203 """
201 204
202 205 from IPython.utils.ipstruct import Struct
203 206
204 207 # Shorthands
205 208 shell = self.shell
206 209 disp_formatter = self.shell.display_formatter
207 210 ptformatter = disp_formatter.formatters['text/plain']
208 211 # dstore is a data store kept in the instance metadata bag to track any
209 212 # changes we make, so we can undo them later.
210 213 dstore = shell.meta.setdefault('doctest_mode', Struct())
211 214 save_dstore = dstore.setdefault
212 215
213 216 # save a few values we'll need to recover later
214 217 mode = save_dstore('mode', False)
215 218 save_dstore('rc_pprint', ptformatter.pprint)
216 219 save_dstore('rc_plain_text_only',disp_formatter.plain_text_only)
217 220 save_dstore('xmode', shell.InteractiveTB.mode)
218 221
219 222 if mode == False:
220 223 # turn on
221 224 ptformatter.pprint = False
222 225 disp_formatter.plain_text_only = True
223 226 shell.magic_xmode('Plain')
224 227 else:
225 228 # turn off
226 229 ptformatter.pprint = dstore.rc_pprint
227 230 disp_formatter.plain_text_only = dstore.rc_plain_text_only
228 231 shell.magic_xmode(dstore.xmode)
229 232
230 233 # Store new mode and inform on console
231 234 dstore.mode = bool(1-int(mode))
232 235 mode_label = ['OFF','ON'][dstore.mode]
233 236 print('Doctest mode is:', mode_label)
234 237
235 238 # Send the payload back so that clients can modify their prompt display
236 239 payload = dict(
237 240 source='IPython.zmq.zmqshell.ZMQInteractiveShell.magic_doctest_mode',
238 241 mode=dstore.mode)
239 242 self.payload_manager.write_payload(payload)
240 243
241 244 def magic_edit(self,parameter_s='',last_call=['','']):
242 245 """Bring up an editor and execute the resulting code.
243 246
244 247 Usage:
245 248 %edit [options] [args]
246 249
247 250 %edit runs IPython's editor hook. The default version of this hook is
248 251 set to call the __IPYTHON__.rc.editor command. This is read from your
249 252 environment variable $EDITOR. If this isn't found, it will default to
250 253 vi under Linux/Unix and to notepad under Windows. See the end of this
251 254 docstring for how to change the editor hook.
252 255
253 256 You can also set the value of this editor via the command line option
254 257 '-editor' or in your ipythonrc file. This is useful if you wish to use
255 258 specifically for IPython an editor different from your typical default
256 259 (and for Windows users who typically don't set environment variables).
257 260
258 261 This command allows you to conveniently edit multi-line code right in
259 262 your IPython session.
260 263
261 264 If called without arguments, %edit opens up an empty editor with a
262 265 temporary file and will execute the contents of this file when you
263 266 close it (don't forget to save it!).
264 267
265 268
266 269 Options:
267 270
268 271 -n <number>: open the editor at a specified line number. By default,
269 272 the IPython editor hook uses the unix syntax 'editor +N filename', but
270 273 you can configure this by providing your own modified hook if your
271 274 favorite editor supports line-number specifications with a different
272 275 syntax.
273 276
274 277 -p: this will call the editor with the same data as the previous time
275 278 it was used, regardless of how long ago (in your current session) it
276 279 was.
277 280
278 281 -r: use 'raw' input. This option only applies to input taken from the
279 282 user's history. By default, the 'processed' history is used, so that
280 283 magics are loaded in their transformed version to valid Python. If
281 284 this option is given, the raw input as typed as the command line is
282 285 used instead. When you exit the editor, it will be executed by
283 286 IPython's own processor.
284 287
285 288 -x: do not execute the edited code immediately upon exit. This is
286 289 mainly useful if you are editing programs which need to be called with
287 290 command line arguments, which you can then do using %run.
288 291
289 292
290 293 Arguments:
291 294
292 295 If arguments are given, the following possibilites exist:
293 296
294 297 - The arguments are numbers or pairs of colon-separated numbers (like
295 298 1 4:8 9). These are interpreted as lines of previous input to be
296 299 loaded into the editor. The syntax is the same of the %macro command.
297 300
298 301 - If the argument doesn't start with a number, it is evaluated as a
299 302 variable and its contents loaded into the editor. You can thus edit
300 303 any string which contains python code (including the result of
301 304 previous edits).
302 305
303 306 - If the argument is the name of an object (other than a string),
304 307 IPython will try to locate the file where it was defined and open the
305 308 editor at the point where it is defined. You can use `%edit function`
306 309 to load an editor exactly at the point where 'function' is defined,
307 310 edit it and have the file be executed automatically.
308 311
309 312 If the object is a macro (see %macro for details), this opens up your
310 313 specified editor with a temporary file containing the macro's data.
311 314 Upon exit, the macro is reloaded with the contents of the file.
312 315
313 316 Note: opening at an exact line is only supported under Unix, and some
314 317 editors (like kedit and gedit up to Gnome 2.8) do not understand the
315 318 '+NUMBER' parameter necessary for this feature. Good editors like
316 319 (X)Emacs, vi, jed, pico and joe all do.
317 320
318 321 - If the argument is not found as a variable, IPython will look for a
319 322 file with that name (adding .py if necessary) and load it into the
320 323 editor. It will execute its contents with execfile() when you exit,
321 324 loading any code in the file into your interactive namespace.
322 325
323 326 After executing your code, %edit will return as output the code you
324 327 typed in the editor (except when it was an existing file). This way
325 328 you can reload the code in further invocations of %edit as a variable,
326 329 via _<NUMBER> or Out[<NUMBER>], where <NUMBER> is the prompt number of
327 330 the output.
328 331
329 332 Note that %edit is also available through the alias %ed.
330 333
331 334 This is an example of creating a simple function inside the editor and
332 335 then modifying it. First, start up the editor:
333 336
334 337 In [1]: ed
335 338 Editing... done. Executing edited code...
336 339 Out[1]: 'def foo():n print "foo() was defined in an editing session"n'
337 340
338 341 We can then call the function foo():
339 342
340 343 In [2]: foo()
341 344 foo() was defined in an editing session
342 345
343 346 Now we edit foo. IPython automatically loads the editor with the
344 347 (temporary) file where foo() was previously defined:
345 348
346 349 In [3]: ed foo
347 350 Editing... done. Executing edited code...
348 351
349 352 And if we call foo() again we get the modified version:
350 353
351 354 In [4]: foo()
352 355 foo() has now been changed!
353 356
354 357 Here is an example of how to edit a code snippet successive
355 358 times. First we call the editor:
356 359
357 360 In [5]: ed
358 361 Editing... done. Executing edited code...
359 362 hello
360 363 Out[5]: "print 'hello'n"
361 364
362 365 Now we call it again with the previous output (stored in _):
363 366
364 367 In [6]: ed _
365 368 Editing... done. Executing edited code...
366 369 hello world
367 370 Out[6]: "print 'hello world'n"
368 371
369 372 Now we call it with the output #8 (stored in _8, also as Out[8]):
370 373
371 374 In [7]: ed _8
372 375 Editing... done. Executing edited code...
373 376 hello again
374 377 Out[7]: "print 'hello again'n"
375 378
376 379
377 380 Changing the default editor hook:
378 381
379 382 If you wish to write your own editor hook, you can put it in a
380 383 configuration file which you load at startup time. The default hook
381 384 is defined in the IPython.core.hooks module, and you can use that as a
382 385 starting example for further modifications. That file also has
383 386 general instructions on how to set a new hook for use once you've
384 387 defined it."""
385 388
386 389 # FIXME: This function has become a convoluted mess. It needs a
387 390 # ground-up rewrite with clean, simple logic.
388 391
389 392 def make_filename(arg):
390 393 "Make a filename from the given args"
391 394 try:
392 395 filename = get_py_filename(arg)
393 396 except IOError:
394 397 if args.endswith('.py'):
395 398 filename = arg
396 399 else:
397 400 filename = None
398 401 return filename
399 402
400 403 # custom exceptions
401 404 class DataIsObject(Exception): pass
402 405
403 406 opts,args = self.parse_options(parameter_s,'prn:')
404 407 # Set a few locals from the options for convenience:
405 408 opts_p = opts.has_key('p')
406 409 opts_r = opts.has_key('r')
407 410
408 411 # Default line number value
409 412 lineno = opts.get('n',None)
410 413 if lineno is not None:
411 414 try:
412 415 lineno = int(lineno)
413 416 except:
414 417 warn("The -n argument must be an integer.")
415 418 return
416 419
417 420 if opts_p:
418 421 args = '_%s' % last_call[0]
419 422 if not self.shell.user_ns.has_key(args):
420 423 args = last_call[1]
421 424
422 425 # use last_call to remember the state of the previous call, but don't
423 426 # let it be clobbered by successive '-p' calls.
424 427 try:
425 428 last_call[0] = self.shell.displayhook.prompt_count
426 429 if not opts_p:
427 430 last_call[1] = parameter_s
428 431 except:
429 432 pass
430 433
431 434 # by default this is done with temp files, except when the given
432 435 # arg is a filename
433 436 use_temp = 1
434 437
435 438 if re.match(r'\d',args):
436 439 # Mode where user specifies ranges of lines, like in %macro.
437 440 # This means that you can't edit files whose names begin with
438 441 # numbers this way. Tough.
439 442 ranges = args.split()
440 443 data = ''.join(self.extract_input_slices(ranges,opts_r))
441 444 elif args.endswith('.py'):
442 445 filename = make_filename(args)
443 446 data = ''
444 447 use_temp = 0
445 448 elif args:
446 449 try:
447 450 # Load the parameter given as a variable. If not a string,
448 451 # process it as an object instead (below)
449 452
450 453 #print '*** args',args,'type',type(args) # dbg
451 454 data = eval(args,self.shell.user_ns)
452 455 if not type(data) in StringTypes:
453 456 raise DataIsObject
454 457
455 458 except (NameError,SyntaxError):
456 459 # given argument is not a variable, try as a filename
457 460 filename = make_filename(args)
458 461 if filename is None:
459 462 warn("Argument given (%s) can't be found as a variable "
460 463 "or as a filename." % args)
461 464 return
462 465
463 466 data = ''
464 467 use_temp = 0
465 468 except DataIsObject:
466 469
467 470 # macros have a special edit function
468 471 if isinstance(data,Macro):
469 472 self._edit_macro(args,data)
470 473 return
471 474
472 475 # For objects, try to edit the file where they are defined
473 476 try:
474 477 filename = inspect.getabsfile(data)
475 478 if 'fakemodule' in filename.lower() and inspect.isclass(data):
476 479 # class created by %edit? Try to find source
477 480 # by looking for method definitions instead, the
478 481 # __module__ in those classes is FakeModule.
479 482 attrs = [getattr(data, aname) for aname in dir(data)]
480 483 for attr in attrs:
481 484 if not inspect.ismethod(attr):
482 485 continue
483 486 filename = inspect.getabsfile(attr)
484 487 if filename and 'fakemodule' not in filename.lower():
485 488 # change the attribute to be the edit target instead
486 489 data = attr
487 490 break
488 491
489 492 datafile = 1
490 493 except TypeError:
491 494 filename = make_filename(args)
492 495 datafile = 1
493 496 warn('Could not find file where `%s` is defined.\n'
494 497 'Opening a file named `%s`' % (args,filename))
495 498 # Now, make sure we can actually read the source (if it was in
496 499 # a temp file it's gone by now).
497 500 if datafile:
498 501 try:
499 502 if lineno is None:
500 503 lineno = inspect.getsourcelines(data)[1]
501 504 except IOError:
502 505 filename = make_filename(args)
503 506 if filename is None:
504 507 warn('The file `%s` where `%s` was defined cannot '
505 508 'be read.' % (filename,data))
506 509 return
507 510 use_temp = 0
508 511 else:
509 512 data = ''
510 513
511 514 if use_temp:
512 515 filename = self.shell.mktempfile(data)
513 516 print('IPython will make a temporary file named:', filename)
514 517
515 518 # Make sure we send to the client an absolute path, in case the working
516 519 # directory of client and kernel don't match
517 520 filename = os.path.abspath(filename)
518 521
519 522 payload = {
520 523 'source' : 'IPython.zmq.zmqshell.ZMQInteractiveShell.edit_magic',
521 524 'filename' : filename,
522 525 'line_number' : lineno
523 526 }
524 527 self.payload_manager.write_payload(payload)
525 528
526 529 def magic_gui(self, *args, **kwargs):
527 530 raise NotImplementedError(
528 531 'GUI support must be enabled in command line options.')
529 532
530 533 def magic_pylab(self, *args, **kwargs):
531 534 raise NotImplementedError(
532 535 'pylab support must be enabled in command line options.')
533 536
534 537 # A few magics that are adapted to the specifics of using pexpect and a
535 538 # remote terminal
536 539
537 540 def magic_clear(self, arg_s):
538 541 """Clear the terminal."""
539 542 if os.name == 'posix':
540 543 self.shell.system("clear")
541 544 else:
542 545 self.shell.system("cls")
543 546
544 547 if os.name == 'nt':
545 548 # This is the usual name in windows
546 549 magic_cls = magic_clear
547 550
548 551 # Terminal pagers won't work over pexpect, but we do have our own pager
549 552
550 553 def magic_less(self, arg_s):
551 554 """Show a file through the pager.
552 555
553 556 Files ending in .py are syntax-highlighted."""
554 557 cont = open(arg_s).read()
555 558 if arg_s.endswith('.py'):
556 559 cont = self.shell.pycolorize(cont)
557 560 page.page(cont)
558 561
559 562 magic_more = magic_less
560 563
561 564 # Man calls a pager, so we also need to redefine it
562 565 if os.name == 'posix':
563 566 def magic_man(self, arg_s):
564 567 """Find the man page for the given command and display in pager."""
565 568 page.page(self.shell.getoutput('man %s | col -b' % arg_s,
566 569 split=False))
567 570
568 571 # FIXME: this is specific to the GUI, so we should let the gui app load
569 572 # magics at startup that are only for the gui. Once the gui app has proper
570 573 # profile and configuration management, we can have it initialize a kernel
571 574 # with a special config file that provides these.
572 575 def magic_guiref(self, arg_s):
573 576 """Show a basic reference about the GUI console."""
574 577 from IPython.core.usage import gui_reference
575 578 page.page(gui_reference, auto_html=True)
576 579
577 580 def magic_loadpy(self, arg_s):
578 581 """Load a .py python script into the GUI console.
579 582
580 583 This magic command can either take a local filename or a url::
581 584
582 585 %loadpy myscript.py
583 586 %loadpy http://www.example.com/myscript.py
584 587 """
585 588 if not arg_s.endswith('.py'):
586 589 raise ValueError('%%load only works with .py files: %s' % arg_s)
587 590 if arg_s.startswith('http'):
588 591 import urllib2
589 592 response = urllib2.urlopen(arg_s)
590 593 content = response.read()
591 594 else:
592 595 content = open(arg_s).read()
593 596 payload = dict(
594 597 source='IPython.zmq.zmqshell.ZMQInteractiveShell.magic_loadpy',
595 598 text=content
596 599 )
597 600 self.payload_manager.write_payload(payload)
598 601
599 602 def magic_Exit(self, parameter_s=''):
600 603 """Exit IPython. If the -k option is provided, the kernel will be left
601 604 running. Otherwise, it will shutdown without prompting.
602 605 """
603 606 opts,args = self.parse_options(parameter_s,'k')
604 607 self.shell.keepkernel_on_exit = opts.has_key('k')
605 608 self.shell.ask_exit()
606 609
607 610 # Add aliases as magics so all common forms work: exit, quit, Exit, Quit.
608 611 magic_exit = magic_quit = magic_Quit = magic_Exit
609 612
610 613 InteractiveShellABC.register(ZMQInteractiveShell)
General Comments 0
You need to be logged in to leave comments. Login now