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