##// END OF EJS Templates
skip magic_edit doctest in zmqshell...
MinRK -
Show More
@@ -1,523 +1,525 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 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 34 from IPython.lib.kernel import (
35 35 get_connection_file, get_connection_info, connect_qtconsole
36 36 )
37 from IPython.testing.skipdoctest import skip_doctest
37 38 from IPython.utils import io
38 39 from IPython.utils.jsonutil import json_clean
39 40 from IPython.utils.path import get_py_filename
40 41 from IPython.utils.process import arg_split
41 42 from IPython.utils.traitlets import Instance, Type, Dict, CBool
42 43 from IPython.utils.warn import warn, error
43 44 from IPython.zmq.displayhook import ZMQShellDisplayHook, _encode_binary
44 45 from IPython.zmq.session import extract_header
45 46 from session import Session
46 47
47 48
48 49 #-----------------------------------------------------------------------------
49 50 # Functions and classes
50 51 #-----------------------------------------------------------------------------
51 52
52 53 class ZMQDisplayPublisher(DisplayPublisher):
53 54 """A display publisher that publishes data using a ZeroMQ PUB socket."""
54 55
55 56 session = Instance(Session)
56 57 pub_socket = Instance('zmq.Socket')
57 58 parent_header = Dict({})
58 59
59 60 def set_parent(self, parent):
60 61 """Set the parent for outbound messages."""
61 62 self.parent_header = extract_header(parent)
62 63
63 64 def _flush_streams(self):
64 65 """flush IO Streams prior to display"""
65 66 sys.stdout.flush()
66 67 sys.stderr.flush()
67 68
68 69 def publish(self, source, data, metadata=None):
69 70 self._flush_streams()
70 71 if metadata is None:
71 72 metadata = {}
72 73 self._validate_data(source, data, metadata)
73 74 content = {}
74 75 content['source'] = source
75 76 _encode_binary(data)
76 77 content['data'] = data
77 78 content['metadata'] = metadata
78 79 self.session.send(
79 80 self.pub_socket, u'display_data', json_clean(content),
80 81 parent=self.parent_header
81 82 )
82 83
83 84 def clear_output(self, stdout=True, stderr=True, other=True):
84 85 content = dict(stdout=stdout, stderr=stderr, other=other)
85 86
86 87 if stdout:
87 88 print('\r', file=sys.stdout, end='')
88 89 if stderr:
89 90 print('\r', file=sys.stderr, end='')
90 91
91 92 self._flush_streams()
92 93
93 94 self.session.send(
94 95 self.pub_socket, u'clear_output', content,
95 96 parent=self.parent_header
96 97 )
97 98
98 99 class ZMQInteractiveShell(InteractiveShell):
99 100 """A subclass of InteractiveShell for ZMQ."""
100 101
101 102 displayhook_class = Type(ZMQShellDisplayHook)
102 103 display_pub_class = Type(ZMQDisplayPublisher)
103 104
104 105 # Override the traitlet in the parent class, because there's no point using
105 106 # readline for the kernel. Can be removed when the readline code is moved
106 107 # to the terminal frontend.
107 108 colors_force = CBool(True)
108 109 readline_use = CBool(False)
109 110 # autoindent has no meaning in a zmqshell, and attempting to enable it
110 111 # will print a warning in the absence of readline.
111 112 autoindent = CBool(False)
112 113
113 114 exiter = Instance(ZMQExitAutocall)
114 115 def _exiter_default(self):
115 116 return ZMQExitAutocall(self)
116 117
117 118 keepkernel_on_exit = None
118 119
119 120 # Over ZeroMQ, GUI control isn't done with PyOS_InputHook as there is no
120 121 # interactive input being read; we provide event loop support in ipkernel
121 122 from .eventloops import enable_gui
122 123 enable_gui = staticmethod(enable_gui)
123 124
124 125 def init_environment(self):
125 126 """Configure the user's environment.
126 127
127 128 """
128 129 env = os.environ
129 130 # These two ensure 'ls' produces nice coloring on BSD-derived systems
130 131 env['TERM'] = 'xterm-color'
131 132 env['CLICOLOR'] = '1'
132 133 # Since normal pagers don't work at all (over pexpect we don't have
133 134 # single-key control of the subprocess), try to disable paging in
134 135 # subprocesses as much as possible.
135 136 env['PAGER'] = 'cat'
136 137 env['GIT_PAGER'] = 'cat'
137 138
138 139 # And install the payload version of page.
139 140 install_payload_page()
140 141
141 142 def auto_rewrite_input(self, cmd):
142 143 """Called to show the auto-rewritten input for autocall and friends.
143 144
144 145 FIXME: this payload is currently not correctly processed by the
145 146 frontend.
146 147 """
147 148 new = self.prompt_manager.render('rewrite') + cmd
148 149 payload = dict(
149 150 source='IPython.zmq.zmqshell.ZMQInteractiveShell.auto_rewrite_input',
150 151 transformed_input=new,
151 152 )
152 153 self.payload_manager.write_payload(payload)
153 154
154 155 def ask_exit(self):
155 156 """Engage the exit actions."""
156 157 payload = dict(
157 158 source='IPython.zmq.zmqshell.ZMQInteractiveShell.ask_exit',
158 159 exit=True,
159 160 keepkernel=self.keepkernel_on_exit,
160 161 )
161 162 self.payload_manager.write_payload(payload)
162 163
163 164 def _showtraceback(self, etype, evalue, stb):
164 165
165 166 exc_content = {
166 167 u'traceback' : stb,
167 168 u'ename' : unicode(etype.__name__),
168 169 u'evalue' : unicode(evalue)
169 170 }
170 171
171 172 dh = self.displayhook
172 173 # Send exception info over pub socket for other clients than the caller
173 174 # to pick up
174 175 exc_msg = dh.session.send(dh.pub_socket, u'pyerr', json_clean(exc_content), dh.parent_header)
175 176
176 177 # FIXME - Hack: store exception info in shell object. Right now, the
177 178 # caller is reading this info after the fact, we need to fix this logic
178 179 # to remove this hack. Even uglier, we need to store the error status
179 180 # here, because in the main loop, the logic that sets it is being
180 181 # skipped because runlines swallows the exceptions.
181 182 exc_content[u'status'] = u'error'
182 183 self._reply_content = exc_content
183 184 # /FIXME
184 185
185 186 return exc_content
186 187
187 188 #------------------------------------------------------------------------
188 189 # Magic overrides
189 190 #------------------------------------------------------------------------
190 191 # Once the base class stops inheriting from magic, this code needs to be
191 192 # moved into a separate machinery as well. For now, at least isolate here
192 193 # the magics which this class needs to implement differently from the base
193 194 # class, or that are unique to it.
194 195
195 196 def magic_doctest_mode(self,parameter_s=''):
196 197 """Toggle doctest mode on and off.
197 198
198 199 This mode is intended to make IPython behave as much as possible like a
199 200 plain Python shell, from the perspective of how its prompts, exceptions
200 201 and output look. This makes it easy to copy and paste parts of a
201 202 session into doctests. It does so by:
202 203
203 204 - Changing the prompts to the classic ``>>>`` ones.
204 205 - Changing the exception reporting mode to 'Plain'.
205 206 - Disabling pretty-printing of output.
206 207
207 208 Note that IPython also supports the pasting of code snippets that have
208 209 leading '>>>' and '...' prompts in them. This means that you can paste
209 210 doctests from files or docstrings (even if they have leading
210 211 whitespace), and the code will execute correctly. You can then use
211 212 '%history -t' to see the translated history; this will give you the
212 213 input after removal of all the leading prompts and whitespace, which
213 214 can be pasted back into an editor.
214 215
215 216 With these features, you can switch into this mode easily whenever you
216 217 need to do testing and changes to doctests, without having to leave
217 218 your existing IPython session.
218 219 """
219 220
220 221 from IPython.utils.ipstruct import Struct
221 222
222 223 # Shorthands
223 224 shell = self.shell
224 225 disp_formatter = self.shell.display_formatter
225 226 ptformatter = disp_formatter.formatters['text/plain']
226 227 # dstore is a data store kept in the instance metadata bag to track any
227 228 # changes we make, so we can undo them later.
228 229 dstore = shell.meta.setdefault('doctest_mode', Struct())
229 230 save_dstore = dstore.setdefault
230 231
231 232 # save a few values we'll need to recover later
232 233 mode = save_dstore('mode', False)
233 234 save_dstore('rc_pprint', ptformatter.pprint)
234 235 save_dstore('rc_plain_text_only',disp_formatter.plain_text_only)
235 236 save_dstore('xmode', shell.InteractiveTB.mode)
236 237
237 238 if mode == False:
238 239 # turn on
239 240 ptformatter.pprint = False
240 241 disp_formatter.plain_text_only = True
241 242 shell.magic_xmode('Plain')
242 243 else:
243 244 # turn off
244 245 ptformatter.pprint = dstore.rc_pprint
245 246 disp_formatter.plain_text_only = dstore.rc_plain_text_only
246 247 shell.magic_xmode(dstore.xmode)
247 248
248 249 # Store new mode and inform on console
249 250 dstore.mode = bool(1-int(mode))
250 251 mode_label = ['OFF','ON'][dstore.mode]
251 252 print('Doctest mode is:', mode_label)
252 253
253 254 # Send the payload back so that clients can modify their prompt display
254 255 payload = dict(
255 256 source='IPython.zmq.zmqshell.ZMQInteractiveShell.magic_doctest_mode',
256 257 mode=dstore.mode)
257 258 self.payload_manager.write_payload(payload)
258 259
260 @skip_doctest
259 261 def magic_edit(self,parameter_s='',last_call=['','']):
260 262 """Bring up an editor and execute the resulting code.
261 263
262 264 Usage:
263 265 %edit [options] [args]
264 266
265 267 %edit runs an external text editor. You will need to set the command for
266 268 this editor via the ``TerminalInteractiveShell.editor`` option in your
267 269 configuration file before it will work.
268 270
269 271 This command allows you to conveniently edit multi-line code right in
270 272 your IPython session.
271 273
272 274 If called without arguments, %edit opens up an empty editor with a
273 275 temporary file and will execute the contents of this file when you
274 276 close it (don't forget to save it!).
275 277
276 278
277 279 Options:
278 280
279 281 -n <number>: open the editor at a specified line number. By default,
280 282 the IPython editor hook uses the unix syntax 'editor +N filename', but
281 283 you can configure this by providing your own modified hook if your
282 284 favorite editor supports line-number specifications with a different
283 285 syntax.
284 286
285 287 -p: this will call the editor with the same data as the previous time
286 288 it was used, regardless of how long ago (in your current session) it
287 289 was.
288 290
289 291 -r: use 'raw' input. This option only applies to input taken from the
290 292 user's history. By default, the 'processed' history is used, so that
291 293 magics are loaded in their transformed version to valid Python. If
292 294 this option is given, the raw input as typed as the command line is
293 295 used instead. When you exit the editor, it will be executed by
294 296 IPython's own processor.
295 297
296 298 -x: do not execute the edited code immediately upon exit. This is
297 299 mainly useful if you are editing programs which need to be called with
298 300 command line arguments, which you can then do using %run.
299 301
300 302
301 303 Arguments:
302 304
303 305 If arguments are given, the following possibilites exist:
304 306
305 307 - The arguments are numbers or pairs of colon-separated numbers (like
306 308 1 4:8 9). These are interpreted as lines of previous input to be
307 309 loaded into the editor. The syntax is the same of the %macro command.
308 310
309 311 - If the argument doesn't start with a number, it is evaluated as a
310 312 variable and its contents loaded into the editor. You can thus edit
311 313 any string which contains python code (including the result of
312 314 previous edits).
313 315
314 316 - If the argument is the name of an object (other than a string),
315 317 IPython will try to locate the file where it was defined and open the
316 318 editor at the point where it is defined. You can use `%edit function`
317 319 to load an editor exactly at the point where 'function' is defined,
318 320 edit it and have the file be executed automatically.
319 321
320 322 If the object is a macro (see %macro for details), this opens up your
321 323 specified editor with a temporary file containing the macro's data.
322 324 Upon exit, the macro is reloaded with the contents of the file.
323 325
324 326 Note: opening at an exact line is only supported under Unix, and some
325 327 editors (like kedit and gedit up to Gnome 2.8) do not understand the
326 328 '+NUMBER' parameter necessary for this feature. Good editors like
327 329 (X)Emacs, vi, jed, pico and joe all do.
328 330
329 331 - If the argument is not found as a variable, IPython will look for a
330 332 file with that name (adding .py if necessary) and load it into the
331 333 editor. It will execute its contents with execfile() when you exit,
332 334 loading any code in the file into your interactive namespace.
333 335
334 336 After executing your code, %edit will return as output the code you
335 337 typed in the editor (except when it was an existing file). This way
336 338 you can reload the code in further invocations of %edit as a variable,
337 339 via _<NUMBER> or Out[<NUMBER>], where <NUMBER> is the prompt number of
338 340 the output.
339 341
340 342 Note that %edit is also available through the alias %ed.
341 343
342 344 This is an example of creating a simple function inside the editor and
343 345 then modifying it. First, start up the editor:
344 346
345 347 In [1]: ed
346 348 Editing... done. Executing edited code...
347 349 Out[1]: 'def foo():n print "foo() was defined in an editing session"n'
348 350
349 351 We can then call the function foo():
350 352
351 353 In [2]: foo()
352 354 foo() was defined in an editing session
353 355
354 356 Now we edit foo. IPython automatically loads the editor with the
355 357 (temporary) file where foo() was previously defined:
356 358
357 359 In [3]: ed foo
358 360 Editing... done. Executing edited code...
359 361
360 362 And if we call foo() again we get the modified version:
361 363
362 364 In [4]: foo()
363 365 foo() has now been changed!
364 366
365 367 Here is an example of how to edit a code snippet successive
366 368 times. First we call the editor:
367 369
368 370 In [5]: ed
369 371 Editing... done. Executing edited code...
370 372 hello
371 373 Out[5]: "print 'hello'n"
372 374
373 375 Now we call it again with the previous output (stored in _):
374 376
375 377 In [6]: ed _
376 378 Editing... done. Executing edited code...
377 379 hello world
378 380 Out[6]: "print 'hello world'n"
379 381
380 382 Now we call it with the output #8 (stored in _8, also as Out[8]):
381 383
382 384 In [7]: ed _8
383 385 Editing... done. Executing edited code...
384 386 hello again
385 387 Out[7]: "print 'hello again'n"
386 388 """
387 389
388 390 opts,args = self.parse_options(parameter_s,'prn:')
389 391
390 392 try:
391 393 filename, lineno, _ = self._find_edit_target(args, opts, last_call)
392 394 except MacroToEdit as e:
393 395 # TODO: Implement macro editing over 2 processes.
394 396 print("Macro editing not yet implemented in 2-process model.")
395 397 return
396 398
397 399 # Make sure we send to the client an absolute path, in case the working
398 400 # directory of client and kernel don't match
399 401 filename = os.path.abspath(filename)
400 402
401 403 payload = {
402 404 'source' : 'IPython.zmq.zmqshell.ZMQInteractiveShell.edit_magic',
403 405 'filename' : filename,
404 406 'line_number' : lineno
405 407 }
406 408 self.payload_manager.write_payload(payload)
407 409
408 410 # A few magics that are adapted to the specifics of using pexpect and a
409 411 # remote terminal
410 412
411 413 def magic_clear(self, arg_s):
412 414 """Clear the terminal."""
413 415 if os.name == 'posix':
414 416 self.shell.system("clear")
415 417 else:
416 418 self.shell.system("cls")
417 419
418 420 if os.name == 'nt':
419 421 # This is the usual name in windows
420 422 magic_cls = magic_clear
421 423
422 424 # Terminal pagers won't work over pexpect, but we do have our own pager
423 425
424 426 def magic_less(self, arg_s):
425 427 """Show a file through the pager.
426 428
427 429 Files ending in .py are syntax-highlighted."""
428 430 cont = open(arg_s).read()
429 431 if arg_s.endswith('.py'):
430 432 cont = self.shell.pycolorize(cont)
431 433 page.page(cont)
432 434
433 435 magic_more = magic_less
434 436
435 437 # Man calls a pager, so we also need to redefine it
436 438 if os.name == 'posix':
437 439 def magic_man(self, arg_s):
438 440 """Find the man page for the given command and display in pager."""
439 441 page.page(self.shell.getoutput('man %s | col -b' % arg_s,
440 442 split=False))
441 443
442 444 # FIXME: this is specific to the GUI, so we should let the gui app load
443 445 # magics at startup that are only for the gui. Once the gui app has proper
444 446 # profile and configuration management, we can have it initialize a kernel
445 447 # with a special config file that provides these.
446 448 def magic_guiref(self, arg_s):
447 449 """Show a basic reference about the GUI console."""
448 450 from IPython.core.usage import gui_reference
449 451 page.page(gui_reference, auto_html=True)
450 452
451 453 def magic_connect_info(self, arg_s):
452 454 """Print information for connecting other clients to this kernel
453 455
454 456 It will print the contents of this session's connection file, as well as
455 457 shortcuts for local clients.
456 458
457 459 In the simplest case, when called from the most recently launched kernel,
458 460 secondary clients can be connected, simply with:
459 461
460 462 $> ipython <app> --existing
461 463
462 464 """
463 465
464 466 from IPython.core.application import BaseIPythonApplication as BaseIPApp
465 467
466 468 if BaseIPApp.initialized():
467 469 app = BaseIPApp.instance()
468 470 security_dir = app.profile_dir.security_dir
469 471 profile = app.profile
470 472 else:
471 473 profile = 'default'
472 474 security_dir = ''
473 475
474 476 try:
475 477 connection_file = get_connection_file()
476 478 info = get_connection_info(unpack=False)
477 479 except Exception as e:
478 480 error("Could not get connection info: %r" % e)
479 481 return
480 482
481 483 # add profile flag for non-default profile
482 484 profile_flag = "--profile %s" % profile if profile != 'default' else ""
483 485
484 486 # if it's in the security dir, truncate to basename
485 487 if security_dir == os.path.dirname(connection_file):
486 488 connection_file = os.path.basename(connection_file)
487 489
488 490
489 491 print (info + '\n')
490 492 print ("Paste the above JSON into a file, and connect with:\n"
491 493 " $> ipython <app> --existing <file>\n"
492 494 "or, if you are local, you can connect with just:\n"
493 495 " $> ipython <app> --existing {0} {1}\n"
494 496 "or even just:\n"
495 497 " $> ipython <app> --existing {1}\n"
496 498 "if this is the most recent IPython session you have started.".format(
497 499 connection_file, profile_flag
498 500 )
499 501 )
500 502
501 503 def magic_qtconsole(self, arg_s):
502 504 """Open a qtconsole connected to this kernel.
503 505
504 506 Useful for connecting a qtconsole to running notebooks, for better
505 507 debugging.
506 508 """
507 509 try:
508 510 p = connect_qtconsole(argv=arg_split(arg_s, os.name=='posix'))
509 511 except Exception as e:
510 512 error("Could not start qtconsole: %r" % e)
511 513 return
512 514
513 515 def set_next_input(self, text):
514 516 """Send the specified text to the frontend to be presented at the next
515 517 input cell."""
516 518 payload = dict(
517 519 source='IPython.zmq.zmqshell.ZMQInteractiveShell.set_next_input',
518 520 text=text
519 521 )
520 522 self.payload_manager.write_payload(payload)
521 523
522 524
523 525 InteractiveShellABC.register(ZMQInteractiveShell)
General Comments 0
You need to be logged in to leave comments. Login now