##// END OF EJS Templates
Fix documentation of functions using magic_arguments
Thomas Kluyver -
Show More
@@ -1,647 +1,648 b''
1 1 """Implementation of basic magic functions.
2 2 """
3 3 #-----------------------------------------------------------------------------
4 4 # Copyright (c) 2012 The IPython Development Team.
5 5 #
6 6 # Distributed under the terms of the Modified BSD License.
7 7 #
8 8 # The full license is in the file COPYING.txt, distributed with this software.
9 9 #-----------------------------------------------------------------------------
10 10
11 11 #-----------------------------------------------------------------------------
12 12 # Imports
13 13 #-----------------------------------------------------------------------------
14 14 from __future__ import print_function
15 15
16 16 # Stdlib
17 17 import io
18 18 import json
19 19 import sys
20 20 from pprint import pformat
21 21
22 22 # Our own packages
23 23 from IPython.core import magic_arguments, page
24 24 from IPython.core.error import UsageError
25 25 from IPython.core.magic import Magics, magics_class, line_magic, magic_escapes
26 26 from IPython.utils.text import format_screen, dedent, indent
27 27 from IPython.testing.skipdoctest import skip_doctest
28 28 from IPython.utils.ipstruct import Struct
29 29 from IPython.utils.path import unquote_filename
30 30 from IPython.utils.py3compat import unicode_type
31 31 from IPython.utils.warn import warn, error
32 32
33 33 #-----------------------------------------------------------------------------
34 34 # Magics class implementation
35 35 #-----------------------------------------------------------------------------
36 36
37 37 class MagicsDisplay(object):
38 38 def __init__(self, magics_manager):
39 39 self.magics_manager = magics_manager
40 40
41 41 def _lsmagic(self):
42 42 """The main implementation of the %lsmagic"""
43 43 mesc = magic_escapes['line']
44 44 cesc = magic_escapes['cell']
45 45 mman = self.magics_manager
46 46 magics = mman.lsmagic()
47 47 out = ['Available line magics:',
48 48 mesc + (' '+mesc).join(sorted(magics['line'])),
49 49 '',
50 50 'Available cell magics:',
51 51 cesc + (' '+cesc).join(sorted(magics['cell'])),
52 52 '',
53 53 mman.auto_status()]
54 54 return '\n'.join(out)
55 55
56 56 def _repr_pretty_(self, p, cycle):
57 57 p.text(self._lsmagic())
58 58
59 59 def __str__(self):
60 60 return self._lsmagic()
61 61
62 62 def _jsonable(self):
63 63 """turn magics dict into jsonable dict of the same structure
64 64
65 65 replaces object instances with their class names as strings
66 66 """
67 67 magic_dict = {}
68 68 mman = self.magics_manager
69 69 magics = mman.lsmagic()
70 70 for key, subdict in magics.items():
71 71 d = {}
72 72 magic_dict[key] = d
73 73 for name, obj in subdict.items():
74 74 try:
75 75 classname = obj.__self__.__class__.__name__
76 76 except AttributeError:
77 77 classname = 'Other'
78 78
79 79 d[name] = classname
80 80 return magic_dict
81 81
82 82 def _repr_json_(self):
83 83 return json.dumps(self._jsonable())
84 84
85 85
86 86 @magics_class
87 87 class BasicMagics(Magics):
88 88 """Magics that provide central IPython functionality.
89 89
90 90 These are various magics that don't fit into specific categories but that
91 91 are all part of the base 'IPython experience'."""
92 92
93 93 @magic_arguments.magic_arguments()
94 94 @magic_arguments.argument(
95 95 '-l', '--line', action='store_true',
96 96 help="""Create a line magic alias."""
97 97 )
98 98 @magic_arguments.argument(
99 99 '-c', '--cell', action='store_true',
100 100 help="""Create a cell magic alias."""
101 101 )
102 102 @magic_arguments.argument(
103 103 'name',
104 104 help="""Name of the magic to be created."""
105 105 )
106 106 @magic_arguments.argument(
107 107 'target',
108 108 help="""Name of the existing line or cell magic."""
109 109 )
110 110 @line_magic
111 111 def alias_magic(self, line=''):
112 112 """Create an alias for an existing line or cell magic.
113 113
114 114 Examples
115 115 --------
116 116 ::
117
117 118 In [1]: %alias_magic t timeit
118 119 Created `%t` as an alias for `%timeit`.
119 120 Created `%%t` as an alias for `%%timeit`.
120 121
121 122 In [2]: %t -n1 pass
122 123 1 loops, best of 3: 954 ns per loop
123 124
124 125 In [3]: %%t -n1
125 126 ...: pass
126 127 ...:
127 128 1 loops, best of 3: 954 ns per loop
128 129
129 130 In [4]: %alias_magic --cell whereami pwd
130 131 UsageError: Cell magic function `%%pwd` not found.
131 132 In [5]: %alias_magic --line whereami pwd
132 133 Created `%whereami` as an alias for `%pwd`.
133 134
134 135 In [6]: %whereami
135 136 Out[6]: u'/home/testuser'
136 137 """
137 138 args = magic_arguments.parse_argstring(self.alias_magic, line)
138 139 shell = self.shell
139 140 mman = self.shell.magics_manager
140 141 escs = ''.join(magic_escapes.values())
141 142
142 143 target = args.target.lstrip(escs)
143 144 name = args.name.lstrip(escs)
144 145
145 146 # Find the requested magics.
146 147 m_line = shell.find_magic(target, 'line')
147 148 m_cell = shell.find_magic(target, 'cell')
148 149 if args.line and m_line is None:
149 150 raise UsageError('Line magic function `%s%s` not found.' %
150 151 (magic_escapes['line'], target))
151 152 if args.cell and m_cell is None:
152 153 raise UsageError('Cell magic function `%s%s` not found.' %
153 154 (magic_escapes['cell'], target))
154 155
155 156 # If --line and --cell are not specified, default to the ones
156 157 # that are available.
157 158 if not args.line and not args.cell:
158 159 if not m_line and not m_cell:
159 160 raise UsageError(
160 161 'No line or cell magic with name `%s` found.' % target
161 162 )
162 163 args.line = bool(m_line)
163 164 args.cell = bool(m_cell)
164 165
165 166 if args.line:
166 167 mman.register_alias(name, target, 'line')
167 168 print('Created `%s%s` as an alias for `%s%s`.' % (
168 169 magic_escapes['line'], name,
169 170 magic_escapes['line'], target))
170 171
171 172 if args.cell:
172 173 mman.register_alias(name, target, 'cell')
173 174 print('Created `%s%s` as an alias for `%s%s`.' % (
174 175 magic_escapes['cell'], name,
175 176 magic_escapes['cell'], target))
176 177
177 178 @line_magic
178 179 def lsmagic(self, parameter_s=''):
179 180 """List currently available magic functions."""
180 181 return MagicsDisplay(self.shell.magics_manager)
181 182
182 183 def _magic_docs(self, brief=False, rest=False):
183 184 """Return docstrings from magic functions."""
184 185 mman = self.shell.magics_manager
185 186 docs = mman.lsmagic_docs(brief, missing='No documentation')
186 187
187 188 if rest:
188 189 format_string = '**%s%s**::\n\n%s\n\n'
189 190 else:
190 191 format_string = '%s%s:\n%s\n'
191 192
192 193 return ''.join(
193 194 [format_string % (magic_escapes['line'], fname,
194 195 indent(dedent(fndoc)))
195 196 for fname, fndoc in sorted(docs['line'].items())]
196 197 +
197 198 [format_string % (magic_escapes['cell'], fname,
198 199 indent(dedent(fndoc)))
199 200 for fname, fndoc in sorted(docs['cell'].items())]
200 201 )
201 202
202 203 @line_magic
203 204 def magic(self, parameter_s=''):
204 205 """Print information about the magic function system.
205 206
206 207 Supported formats: -latex, -brief, -rest
207 208 """
208 209
209 210 mode = ''
210 211 try:
211 212 mode = parameter_s.split()[0][1:]
212 213 if mode == 'rest':
213 214 rest_docs = []
214 215 except IndexError:
215 216 pass
216 217
217 218 brief = (mode == 'brief')
218 219 rest = (mode == 'rest')
219 220 magic_docs = self._magic_docs(brief, rest)
220 221
221 222 if mode == 'latex':
222 223 print(self.format_latex(magic_docs))
223 224 return
224 225 else:
225 226 magic_docs = format_screen(magic_docs)
226 227
227 228 out = ["""
228 229 IPython's 'magic' functions
229 230 ===========================
230 231
231 232 The magic function system provides a series of functions which allow you to
232 233 control the behavior of IPython itself, plus a lot of system-type
233 234 features. There are two kinds of magics, line-oriented and cell-oriented.
234 235
235 236 Line magics are prefixed with the % character and work much like OS
236 237 command-line calls: they get as an argument the rest of the line, where
237 238 arguments are passed without parentheses or quotes. For example, this will
238 239 time the given statement::
239 240
240 241 %timeit range(1000)
241 242
242 243 Cell magics are prefixed with a double %%, and they are functions that get as
243 244 an argument not only the rest of the line, but also the lines below it in a
244 245 separate argument. These magics are called with two arguments: the rest of the
245 246 call line and the body of the cell, consisting of the lines below the first.
246 247 For example::
247 248
248 249 %%timeit x = numpy.random.randn((100, 100))
249 250 numpy.linalg.svd(x)
250 251
251 252 will time the execution of the numpy svd routine, running the assignment of x
252 253 as part of the setup phase, which is not timed.
253 254
254 255 In a line-oriented client (the terminal or Qt console IPython), starting a new
255 256 input with %% will automatically enter cell mode, and IPython will continue
256 257 reading input until a blank line is given. In the notebook, simply type the
257 258 whole cell as one entity, but keep in mind that the %% escape can only be at
258 259 the very start of the cell.
259 260
260 261 NOTE: If you have 'automagic' enabled (via the command line option or with the
261 262 %automagic function), you don't need to type in the % explicitly for line
262 263 magics; cell magics always require an explicit '%%' escape. By default,
263 264 IPython ships with automagic on, so you should only rarely need the % escape.
264 265
265 266 Example: typing '%cd mydir' (without the quotes) changes you working directory
266 267 to 'mydir', if it exists.
267 268
268 269 For a list of the available magic functions, use %lsmagic. For a description
269 270 of any of them, type %magic_name?, e.g. '%cd?'.
270 271
271 272 Currently the magic system has the following functions:""",
272 273 magic_docs,
273 274 "Summary of magic functions (from %slsmagic):" % magic_escapes['line'],
274 275 str(self.lsmagic()),
275 276 ]
276 277 page.page('\n'.join(out))
277 278
278 279
279 280 @line_magic
280 281 def page(self, parameter_s=''):
281 282 """Pretty print the object and display it through a pager.
282 283
283 284 %page [options] OBJECT
284 285
285 286 If no object is given, use _ (last output).
286 287
287 288 Options:
288 289
289 290 -r: page str(object), don't pretty-print it."""
290 291
291 292 # After a function contributed by Olivier Aubert, slightly modified.
292 293
293 294 # Process options/args
294 295 opts, args = self.parse_options(parameter_s, 'r')
295 296 raw = 'r' in opts
296 297
297 298 oname = args and args or '_'
298 299 info = self.shell._ofind(oname)
299 300 if info['found']:
300 301 txt = (raw and str or pformat)( info['obj'] )
301 302 page.page(txt)
302 303 else:
303 304 print('Object `%s` not found' % oname)
304 305
305 306 @line_magic
306 307 def profile(self, parameter_s=''):
307 308 """Print your currently active IPython profile."""
308 309 from IPython.core.application import BaseIPythonApplication
309 310 if BaseIPythonApplication.initialized():
310 311 print(BaseIPythonApplication.instance().profile)
311 312 else:
312 313 error("profile is an application-level value, but you don't appear to be in an IPython application")
313 314
314 315 @line_magic
315 316 def pprint(self, parameter_s=''):
316 317 """Toggle pretty printing on/off."""
317 318 ptformatter = self.shell.display_formatter.formatters['text/plain']
318 319 ptformatter.pprint = bool(1 - ptformatter.pprint)
319 320 print('Pretty printing has been turned',
320 321 ['OFF','ON'][ptformatter.pprint])
321 322
322 323 @line_magic
323 324 def colors(self, parameter_s=''):
324 325 """Switch color scheme for prompts, info system and exception handlers.
325 326
326 327 Currently implemented schemes: NoColor, Linux, LightBG.
327 328
328 329 Color scheme names are not case-sensitive.
329 330
330 331 Examples
331 332 --------
332 333 To get a plain black and white terminal::
333 334
334 335 %colors nocolor
335 336 """
336 337 def color_switch_err(name):
337 338 warn('Error changing %s color schemes.\n%s' %
338 339 (name, sys.exc_info()[1]))
339 340
340 341
341 342 new_scheme = parameter_s.strip()
342 343 if not new_scheme:
343 344 raise UsageError(
344 345 "%colors: you must specify a color scheme. See '%colors?'")
345 346 # local shortcut
346 347 shell = self.shell
347 348
348 349 import IPython.utils.rlineimpl as readline
349 350
350 351 if not shell.colors_force and \
351 352 not readline.have_readline and \
352 353 (sys.platform == "win32" or sys.platform == "cli"):
353 354 msg = """\
354 355 Proper color support under MS Windows requires the pyreadline library.
355 356 You can find it at:
356 357 http://ipython.org/pyreadline.html
357 358 Gary's readline needs the ctypes module, from:
358 359 http://starship.python.net/crew/theller/ctypes
359 360 (Note that ctypes is already part of Python versions 2.5 and newer).
360 361
361 362 Defaulting color scheme to 'NoColor'"""
362 363 new_scheme = 'NoColor'
363 364 warn(msg)
364 365
365 366 # readline option is 0
366 367 if not shell.colors_force and not shell.has_readline:
367 368 new_scheme = 'NoColor'
368 369
369 370 # Set prompt colors
370 371 try:
371 372 shell.prompt_manager.color_scheme = new_scheme
372 373 except:
373 374 color_switch_err('prompt')
374 375 else:
375 376 shell.colors = \
376 377 shell.prompt_manager.color_scheme_table.active_scheme_name
377 378 # Set exception colors
378 379 try:
379 380 shell.InteractiveTB.set_colors(scheme = new_scheme)
380 381 shell.SyntaxTB.set_colors(scheme = new_scheme)
381 382 except:
382 383 color_switch_err('exception')
383 384
384 385 # Set info (for 'object?') colors
385 386 if shell.color_info:
386 387 try:
387 388 shell.inspector.set_active_scheme(new_scheme)
388 389 except:
389 390 color_switch_err('object inspector')
390 391 else:
391 392 shell.inspector.set_active_scheme('NoColor')
392 393
393 394 @line_magic
394 395 def xmode(self, parameter_s=''):
395 396 """Switch modes for the exception handlers.
396 397
397 398 Valid modes: Plain, Context and Verbose.
398 399
399 400 If called without arguments, acts as a toggle."""
400 401
401 402 def xmode_switch_err(name):
402 403 warn('Error changing %s exception modes.\n%s' %
403 404 (name,sys.exc_info()[1]))
404 405
405 406 shell = self.shell
406 407 new_mode = parameter_s.strip().capitalize()
407 408 try:
408 409 shell.InteractiveTB.set_mode(mode=new_mode)
409 410 print('Exception reporting mode:',shell.InteractiveTB.mode)
410 411 except:
411 412 xmode_switch_err('user')
412 413
413 414 @line_magic
414 415 def quickref(self,arg):
415 416 """ Show a quick reference sheet """
416 417 from IPython.core.usage import quick_reference
417 418 qr = quick_reference + self._magic_docs(brief=True)
418 419 page.page(qr)
419 420
420 421 @line_magic
421 422 def doctest_mode(self, parameter_s=''):
422 423 """Toggle doctest mode on and off.
423 424
424 425 This mode is intended to make IPython behave as much as possible like a
425 426 plain Python shell, from the perspective of how its prompts, exceptions
426 427 and output look. This makes it easy to copy and paste parts of a
427 428 session into doctests. It does so by:
428 429
429 430 - Changing the prompts to the classic ``>>>`` ones.
430 431 - Changing the exception reporting mode to 'Plain'.
431 432 - Disabling pretty-printing of output.
432 433
433 434 Note that IPython also supports the pasting of code snippets that have
434 435 leading '>>>' and '...' prompts in them. This means that you can paste
435 436 doctests from files or docstrings (even if they have leading
436 437 whitespace), and the code will execute correctly. You can then use
437 438 '%history -t' to see the translated history; this will give you the
438 439 input after removal of all the leading prompts and whitespace, which
439 440 can be pasted back into an editor.
440 441
441 442 With these features, you can switch into this mode easily whenever you
442 443 need to do testing and changes to doctests, without having to leave
443 444 your existing IPython session.
444 445 """
445 446
446 447 # Shorthands
447 448 shell = self.shell
448 449 pm = shell.prompt_manager
449 450 meta = shell.meta
450 451 disp_formatter = self.shell.display_formatter
451 452 ptformatter = disp_formatter.formatters['text/plain']
452 453 # dstore is a data store kept in the instance metadata bag to track any
453 454 # changes we make, so we can undo them later.
454 455 dstore = meta.setdefault('doctest_mode',Struct())
455 456 save_dstore = dstore.setdefault
456 457
457 458 # save a few values we'll need to recover later
458 459 mode = save_dstore('mode',False)
459 460 save_dstore('rc_pprint',ptformatter.pprint)
460 461 save_dstore('xmode',shell.InteractiveTB.mode)
461 462 save_dstore('rc_separate_out',shell.separate_out)
462 463 save_dstore('rc_separate_out2',shell.separate_out2)
463 464 save_dstore('rc_prompts_pad_left',pm.justify)
464 465 save_dstore('rc_separate_in',shell.separate_in)
465 466 save_dstore('rc_active_types',disp_formatter.active_types)
466 467 save_dstore('prompt_templates',(pm.in_template, pm.in2_template, pm.out_template))
467 468
468 469 if mode == False:
469 470 # turn on
470 471 pm.in_template = '>>> '
471 472 pm.in2_template = '... '
472 473 pm.out_template = ''
473 474
474 475 # Prompt separators like plain python
475 476 shell.separate_in = ''
476 477 shell.separate_out = ''
477 478 shell.separate_out2 = ''
478 479
479 480 pm.justify = False
480 481
481 482 ptformatter.pprint = False
482 483 disp_formatter.active_types = ['text/plain']
483 484
484 485 shell.magic('xmode Plain')
485 486 else:
486 487 # turn off
487 488 pm.in_template, pm.in2_template, pm.out_template = dstore.prompt_templates
488 489
489 490 shell.separate_in = dstore.rc_separate_in
490 491
491 492 shell.separate_out = dstore.rc_separate_out
492 493 shell.separate_out2 = dstore.rc_separate_out2
493 494
494 495 pm.justify = dstore.rc_prompts_pad_left
495 496
496 497 ptformatter.pprint = dstore.rc_pprint
497 498 disp_formatter.active_types = dstore.rc_active_types
498 499
499 500 shell.magic('xmode ' + dstore.xmode)
500 501
501 502 # Store new mode and inform
502 503 dstore.mode = bool(1-int(mode))
503 504 mode_label = ['OFF','ON'][dstore.mode]
504 505 print('Doctest mode is:', mode_label)
505 506
506 507 @line_magic
507 508 def gui(self, parameter_s=''):
508 509 """Enable or disable IPython GUI event loop integration.
509 510
510 511 %gui [GUINAME]
511 512
512 513 This magic replaces IPython's threaded shells that were activated
513 514 using the (pylab/wthread/etc.) command line flags. GUI toolkits
514 515 can now be enabled at runtime and keyboard
515 516 interrupts should work without any problems. The following toolkits
516 517 are supported: wxPython, PyQt4, PyGTK, Tk and Cocoa (OSX)::
517 518
518 519 %gui wx # enable wxPython event loop integration
519 520 %gui qt4|qt # enable PyQt4 event loop integration
520 521 %gui gtk # enable PyGTK event loop integration
521 522 %gui gtk3 # enable Gtk3 event loop integration
522 523 %gui tk # enable Tk event loop integration
523 524 %gui osx # enable Cocoa event loop integration
524 525 # (requires %matplotlib 1.1)
525 526 %gui # disable all event loop integration
526 527
527 528 WARNING: after any of these has been called you can simply create
528 529 an application object, but DO NOT start the event loop yourself, as
529 530 we have already handled that.
530 531 """
531 532 opts, arg = self.parse_options(parameter_s, '')
532 533 if arg=='': arg = None
533 534 try:
534 535 return self.shell.enable_gui(arg)
535 536 except Exception as e:
536 537 # print simple error message, rather than traceback if we can't
537 538 # hook up the GUI
538 539 error(str(e))
539 540
540 541 @skip_doctest
541 542 @line_magic
542 543 def precision(self, s=''):
543 544 """Set floating point precision for pretty printing.
544 545
545 546 Can set either integer precision or a format string.
546 547
547 548 If numpy has been imported and precision is an int,
548 549 numpy display precision will also be set, via ``numpy.set_printoptions``.
549 550
550 551 If no argument is given, defaults will be restored.
551 552
552 553 Examples
553 554 --------
554 555 ::
555 556
556 557 In [1]: from math import pi
557 558
558 559 In [2]: %precision 3
559 560 Out[2]: u'%.3f'
560 561
561 562 In [3]: pi
562 563 Out[3]: 3.142
563 564
564 565 In [4]: %precision %i
565 566 Out[4]: u'%i'
566 567
567 568 In [5]: pi
568 569 Out[5]: 3
569 570
570 571 In [6]: %precision %e
571 572 Out[6]: u'%e'
572 573
573 574 In [7]: pi**10
574 575 Out[7]: 9.364805e+04
575 576
576 577 In [8]: %precision
577 578 Out[8]: u'%r'
578 579
579 580 In [9]: pi**10
580 581 Out[9]: 93648.047476082982
581 582 """
582 583 ptformatter = self.shell.display_formatter.formatters['text/plain']
583 584 ptformatter.float_precision = s
584 585 return ptformatter.float_format
585 586
586 587 @magic_arguments.magic_arguments()
587 588 @magic_arguments.argument(
588 589 '-e', '--export', action='store_true', default=False,
589 590 help='Export IPython history as a notebook. The filename argument '
590 591 'is used to specify the notebook name and format. For example '
591 592 'a filename of notebook.ipynb will result in a notebook name '
592 593 'of "notebook" and a format of "json". Likewise using a ".py" '
593 594 'file extension will write the notebook as a Python script'
594 595 )
595 596 @magic_arguments.argument(
596 597 '-f', '--format',
597 598 help='Convert an existing IPython notebook to a new format. This option '
598 599 'specifies the new format and can have the values: json, py. '
599 600 'The target filename is chosen automatically based on the new '
600 601 'format. The filename argument gives the name of the source file.'
601 602 )
602 603 @magic_arguments.argument(
603 604 'filename', type=unicode_type,
604 605 help='Notebook name or filename'
605 606 )
606 607 @line_magic
607 608 def notebook(self, s):
608 609 """Export and convert IPython notebooks.
609 610
610 611 This function can export the current IPython history to a notebook file
611 612 or can convert an existing notebook file into a different format. For
612 613 example, to export the history to "foo.ipynb" do "%notebook -e foo.ipynb".
613 614 To export the history to "foo.py" do "%notebook -e foo.py". To convert
614 615 "foo.ipynb" to "foo.json" do "%notebook -f json foo.ipynb". Possible
615 616 formats include (json/ipynb, py).
616 617 """
617 618 args = magic_arguments.parse_argstring(self.notebook, s)
618 619
619 620 from IPython.nbformat import current
620 621 args.filename = unquote_filename(args.filename)
621 622 if args.export:
622 623 fname, name, format = current.parse_filename(args.filename)
623 624 cells = []
624 625 hist = list(self.shell.history_manager.get_range())
625 626 for session, prompt_number, input in hist[:-1]:
626 627 cells.append(current.new_code_cell(prompt_number=prompt_number,
627 628 input=input))
628 629 worksheet = current.new_worksheet(cells=cells)
629 630 nb = current.new_notebook(name=name,worksheets=[worksheet])
630 631 with io.open(fname, 'w', encoding='utf-8') as f:
631 632 current.write(nb, f, format);
632 633 elif args.format is not None:
633 634 old_fname, old_name, old_format = current.parse_filename(args.filename)
634 635 new_format = args.format
635 636 if new_format == u'xml':
636 637 raise ValueError('Notebooks cannot be written as xml.')
637 638 elif new_format == u'ipynb' or new_format == u'json':
638 639 new_fname = old_name + u'.ipynb'
639 640 new_format = u'json'
640 641 elif new_format == u'py':
641 642 new_fname = old_name + u'.py'
642 643 else:
643 644 raise ValueError('Invalid notebook format: %s' % new_format)
644 645 with io.open(old_fname, 'r', encoding='utf-8') as f:
645 646 nb = current.read(f, old_format)
646 647 with io.open(new_fname, 'w', encoding='utf-8') as f:
647 648 current.write(nb, f, new_format)
@@ -1,341 +1,345 b''
1 1 # -*- coding: utf-8 -*-
2 2 """
3 3 =====================
4 4 Cython related magics
5 5 =====================
6 6
7 7 Magic command interface for interactive work with Cython
8 8
9 9 .. note::
10 10
11 11 The ``Cython`` package needs to be installed separately. It
12 12 can be obtained using ``easy_install`` or ``pip``.
13 13
14 14 Usage
15 15 =====
16 16
17 17 To enable the magics below, execute ``%load_ext cythonmagic``.
18 18
19 19 ``%%cython``
20 20
21 21 {CYTHON_DOC}
22 22
23 23 ``%%cython_inline``
24 24
25 25 {CYTHON_INLINE_DOC}
26 26
27 27 ``%%cython_pyximport``
28 28
29 29 {CYTHON_PYXIMPORT_DOC}
30 30
31 31 Author:
32 32 * Brian Granger
33 33
34 34 Parts of this code were taken from Cython.inline.
35 35 """
36 36 #-----------------------------------------------------------------------------
37 37 # Copyright (C) 2010-2011, IPython Development Team.
38 38 #
39 39 # Distributed under the terms of the Modified BSD License.
40 40 #
41 41 # The full license is in the file COPYING.txt, distributed with this software.
42 42 #-----------------------------------------------------------------------------
43 43
44 44 from __future__ import print_function
45 45
46 46 import imp
47 47 import io
48 48 import os
49 49 import re
50 50 import sys
51 51 import time
52 52
53 53 try:
54 54 reload
55 55 except NameError: # Python 3
56 56 from imp import reload
57 57
58 58 try:
59 59 import hashlib
60 60 except ImportError:
61 61 import md5 as hashlib
62 62
63 63 from distutils.core import Distribution, Extension
64 64 from distutils.command.build_ext import build_ext
65 65
66 66 from IPython.core import display
67 67 from IPython.core import magic_arguments
68 68 from IPython.core.magic import Magics, magics_class, cell_magic
69 69 from IPython.utils import py3compat
70 70 from IPython.utils.path import get_ipython_cache_dir
71 from IPython.utils.text import dedent
71 72
72 73 import Cython
73 74 from Cython.Compiler.Errors import CompileError
74 75 from Cython.Build.Dependencies import cythonize
75 76
76 77
77 78 @magics_class
78 79 class CythonMagics(Magics):
79 80
80 81 def __init__(self, shell):
81 82 super(CythonMagics,self).__init__(shell)
82 83 self._reloads = {}
83 84 self._code_cache = {}
84 85
85 86 def _import_all(self, module):
86 87 for k,v in module.__dict__.items():
87 88 if not k.startswith('__'):
88 89 self.shell.push({k:v})
89 90
90 91 @cell_magic
91 92 def cython_inline(self, line, cell):
92 93 """Compile and run a Cython code cell using Cython.inline.
93 94
94 95 This magic simply passes the body of the cell to Cython.inline
95 96 and returns the result. If the variables `a` and `b` are defined
96 97 in the user's namespace, here is a simple example that returns
97 98 their sum::
98 99
99 100 %%cython_inline
100 101 return a+b
101 102
102 103 For most purposes, we recommend the usage of the `%%cython` magic.
103 104 """
104 105 locs = self.shell.user_global_ns
105 106 globs = self.shell.user_ns
106 107 return Cython.inline(cell, locals=locs, globals=globs)
107 108
108 109 @cell_magic
109 110 def cython_pyximport(self, line, cell):
110 111 """Compile and import a Cython code cell using pyximport.
111 112
112 113 The contents of the cell are written to a `.pyx` file in the current
113 114 working directory, which is then imported using `pyximport`. This
114 115 magic requires a module name to be passed::
115 116
116 117 %%cython_pyximport modulename
117 118 def f(x):
118 119 return 2.0*x
119 120
120 121 The compiled module is then imported and all of its symbols are
121 122 injected into the user's namespace. For most purposes, we recommend
122 123 the usage of the `%%cython` magic.
123 124 """
124 125 module_name = line.strip()
125 126 if not module_name:
126 127 raise ValueError('module name must be given')
127 128 fname = module_name + '.pyx'
128 129 with io.open(fname, 'w', encoding='utf-8') as f:
129 130 f.write(cell)
130 131 if 'pyximport' not in sys.modules:
131 132 import pyximport
132 133 pyximport.install(reload_support=True)
133 134 if module_name in self._reloads:
134 135 module = self._reloads[module_name]
135 136 reload(module)
136 137 else:
137 138 __import__(module_name)
138 139 module = sys.modules[module_name]
139 140 self._reloads[module_name] = module
140 141 self._import_all(module)
141 142
142 143 @magic_arguments.magic_arguments()
143 144 @magic_arguments.argument(
144 145 '-c', '--compile-args', action='append', default=[],
145 146 help="Extra flags to pass to compiler via the `extra_compile_args` "
146 147 "Extension flag (can be specified multiple times)."
147 148 )
148 149 @magic_arguments.argument(
149 150 '--link-args', action='append', default=[],
150 151 help="Extra flags to pass to linker via the `extra_link_args` "
151 152 "Extension flag (can be specified multiple times)."
152 153 )
153 154 @magic_arguments.argument(
154 155 '-l', '--lib', action='append', default=[],
155 156 help="Add a library to link the extension against (can be specified "
156 157 "multiple times)."
157 158 )
158 159 @magic_arguments.argument(
159 160 '-n', '--name',
160 161 help="Specify a name for the Cython module."
161 162 )
162 163 @magic_arguments.argument(
163 164 '-L', dest='library_dirs', metavar='dir', action='append', default=[],
164 165 help="Add a path to the list of libary directories (can be specified "
165 166 "multiple times)."
166 167 )
167 168 @magic_arguments.argument(
168 169 '-I', '--include', action='append', default=[],
169 170 help="Add a path to the list of include directories (can be specified "
170 171 "multiple times)."
171 172 )
172 173 @magic_arguments.argument(
173 174 '-+', '--cplus', action='store_true', default=False,
174 175 help="Output a C++ rather than C file."
175 176 )
176 177 @magic_arguments.argument(
177 178 '-f', '--force', action='store_true', default=False,
178 179 help="Force the compilation of a new module, even if the source has been "
179 180 "previously compiled."
180 181 )
181 182 @magic_arguments.argument(
182 183 '-a', '--annotate', action='store_true', default=False,
183 184 help="Produce a colorized HTML version of the source."
184 185 )
185 186 @cell_magic
186 187 def cython(self, line, cell):
187 188 """Compile and import everything from a Cython code cell.
188 189
189 190 The contents of the cell are written to a `.pyx` file in the
190 191 directory `IPYTHONDIR/cython` using a filename with the hash of the
191 192 code. This file is then cythonized and compiled. The resulting module
192 193 is imported and all of its symbols are injected into the user's
193 194 namespace. The usage is similar to that of `%%cython_pyximport` but
194 195 you don't have to pass a module name::
195 196
196 197 %%cython
197 198 def f(x):
198 199 return 2.0*x
199 200
200 201 To compile OpenMP codes, pass the required `--compile-args`
201 202 and `--link-args`. For example with gcc::
202 203
203 204 %%cython --compile-args=-fopenmp --link-args=-fopenmp
204 205 ...
205 206 """
206 207 args = magic_arguments.parse_argstring(self.cython, line)
207 208 code = cell if cell.endswith('\n') else cell+'\n'
208 209 lib_dir = os.path.join(get_ipython_cache_dir(), 'cython')
209 210 quiet = True
210 211 key = code, sys.version_info, sys.executable, Cython.__version__
211 212
212 213 if not os.path.exists(lib_dir):
213 214 os.makedirs(lib_dir)
214 215
215 216 if args.force:
216 217 # Force a new module name by adding the current time to the
217 218 # key which is hashed to determine the module name.
218 219 key += time.time(),
219 220
220 221 if args.name:
221 222 module_name = py3compat.unicode_to_str(args.name)
222 223 else:
223 224 module_name = "_cython_magic_" + hashlib.md5(str(key).encode('utf-8')).hexdigest()
224 225 module_path = os.path.join(lib_dir, module_name + self.so_ext)
225 226
226 227 have_module = os.path.isfile(module_path)
227 228 need_cythonize = not have_module
228 229
229 230 if args.annotate:
230 231 html_file = os.path.join(lib_dir, module_name + '.html')
231 232 if not os.path.isfile(html_file):
232 233 need_cythonize = True
233 234
234 235 if need_cythonize:
235 236 c_include_dirs = args.include
236 237 if 'numpy' in code:
237 238 import numpy
238 239 c_include_dirs.append(numpy.get_include())
239 240 pyx_file = os.path.join(lib_dir, module_name + '.pyx')
240 241 pyx_file = py3compat.cast_bytes_py2(pyx_file, encoding=sys.getfilesystemencoding())
241 242 with io.open(pyx_file, 'w', encoding='utf-8') as f:
242 243 f.write(code)
243 244 extension = Extension(
244 245 name = module_name,
245 246 sources = [pyx_file],
246 247 include_dirs = c_include_dirs,
247 248 library_dirs = args.library_dirs,
248 249 extra_compile_args = args.compile_args,
249 250 extra_link_args = args.link_args,
250 251 libraries = args.lib,
251 252 language = 'c++' if args.cplus else 'c',
252 253 )
253 254 build_extension = self._get_build_extension()
254 255 try:
255 256 opts = dict(
256 257 quiet=quiet,
257 258 annotate = args.annotate,
258 259 force = True,
259 260 )
260 261 build_extension.extensions = cythonize([extension], **opts)
261 262 except CompileError:
262 263 return
263 264
264 265 if not have_module:
265 266 build_extension.build_temp = os.path.dirname(pyx_file)
266 267 build_extension.build_lib = lib_dir
267 268 build_extension.run()
268 269 self._code_cache[key] = module_name
269 270
270 271 module = imp.load_dynamic(module_name, module_path)
271 272 self._import_all(module)
272 273
273 274 if args.annotate:
274 275 try:
275 276 with io.open(html_file, encoding='utf-8') as f:
276 277 annotated_html = f.read()
277 278 except IOError as e:
278 279 # File could not be opened. Most likely the user has a version
279 280 # of Cython before 0.15.1 (when `cythonize` learned the
280 281 # `force` keyword argument) and has already compiled this
281 282 # exact source without annotation.
282 283 print('Cython completed successfully but the annotated '
283 284 'source could not be read.', file=sys.stderr)
284 285 print(e, file=sys.stderr)
285 286 else:
286 287 return display.HTML(self.clean_annotated_html(annotated_html))
287 288
288 289 @property
289 290 def so_ext(self):
290 291 """The extension suffix for compiled modules."""
291 292 try:
292 293 return self._so_ext
293 294 except AttributeError:
294 295 self._so_ext = self._get_build_extension().get_ext_filename('')
295 296 return self._so_ext
296 297
297 298 def _clear_distutils_mkpath_cache(self):
298 299 """clear distutils mkpath cache
299 300
300 301 prevents distutils from skipping re-creation of dirs that have been removed
301 302 """
302 303 try:
303 304 from distutils.dir_util import _path_created
304 305 except ImportError:
305 306 pass
306 307 else:
307 308 _path_created.clear()
308 309
309 310 def _get_build_extension(self):
310 311 self._clear_distutils_mkpath_cache()
311 312 dist = Distribution()
312 313 config_files = dist.find_config_files()
313 314 try:
314 315 config_files.remove('setup.cfg')
315 316 except ValueError:
316 317 pass
317 318 dist.parse_config_files(config_files)
318 319 build_extension = build_ext(dist)
319 320 build_extension.finalize_options()
320 321 return build_extension
321 322
322 323 @staticmethod
323 324 def clean_annotated_html(html):
324 325 """Clean up the annotated HTML source.
325 326
326 327 Strips the link to the generated C or C++ file, which we do not
327 328 present to the user.
328 329 """
329 330 r = re.compile('<p>Raw output: <a href="(.*)">(.*)</a>')
330 331 html = '\n'.join(l for l in html.splitlines() if not r.match(l))
331 332 return html
332 333
333 334 __doc__ = __doc__.format(
334 CYTHON_DOC = ' '*8 + CythonMagics.cython.__doc__,
335 CYTHON_INLINE_DOC = ' '*8 + CythonMagics.cython_inline.__doc__,
336 CYTHON_PYXIMPORT_DOC = ' '*8 + CythonMagics.cython_pyximport.__doc__,
335 # rST doesn't see the -+ flag as part of an option list, so we
336 # hide it from the module-level docstring.
337 CYTHON_DOC = dedent(CythonMagics.cython.__doc__\
338 .replace('-+, --cplus','--cplus ')),
339 CYTHON_INLINE_DOC = dedent(CythonMagics.cython_inline.__doc__),
340 CYTHON_PYXIMPORT_DOC = dedent(CythonMagics.cython_pyximport.__doc__),
337 341 )
338 342
339 343 def load_ipython_extension(ip):
340 344 """Load the extension in IPython."""
341 345 ip.register_magics(CythonMagics)
@@ -1,371 +1,374 b''
1 1 # -*- coding: utf-8 -*-
2 2 """
3 3 ===========
4 4 octavemagic
5 5 ===========
6 6
7 7 Magics for interacting with Octave via oct2py.
8 8
9 9 .. note::
10 10
11 11 The ``oct2py`` module needs to be installed separately and
12 12 can be obtained using ``easy_install`` or ``pip``.
13 13
14 14 You will also need a working copy of GNU Octave.
15 15
16 16 Usage
17 17 =====
18 18
19 19 To enable the magics below, execute ``%load_ext octavemagic``.
20 20
21 21 ``%octave``
22 22
23 23 {OCTAVE_DOC}
24 24
25 25 ``%octave_push``
26 26
27 27 {OCTAVE_PUSH_DOC}
28 28
29 29 ``%octave_pull``
30 30
31 31 {OCTAVE_PULL_DOC}
32 32
33 33 """
34 34
35 35 #-----------------------------------------------------------------------------
36 36 # Copyright (C) 2012 The IPython Development Team
37 37 #
38 38 # Distributed under the terms of the BSD License. The full license is in
39 39 # the file COPYING, distributed as part of this software.
40 40 #-----------------------------------------------------------------------------
41 41
42 42 import tempfile
43 43 from glob import glob
44 44 from shutil import rmtree
45 45
46 46 import numpy as np
47 47 import oct2py
48 48 from xml.dom import minidom
49 49
50 50 from IPython.core.displaypub import publish_display_data
51 51 from IPython.core.magic import (Magics, magics_class, line_magic,
52 52 line_cell_magic, needs_local_scope)
53 53 from IPython.testing.skipdoctest import skip_doctest
54 54 from IPython.core.magic_arguments import (
55 55 argument, magic_arguments, parse_argstring
56 56 )
57 57 from IPython.utils.py3compat import unicode_to_str
58 from IPython.utils.text import dedent
58 59
59 60 class OctaveMagicError(oct2py.Oct2PyError):
60 61 pass
61 62
62 63 _mimetypes = {'png' : 'image/png',
63 64 'svg' : 'image/svg+xml',
64 65 'jpg' : 'image/jpeg',
65 66 'jpeg': 'image/jpeg'}
66 67
67 68 @magics_class
68 69 class OctaveMagics(Magics):
69 70 """A set of magics useful for interactive work with Octave via oct2py.
70 71 """
71 72 def __init__(self, shell):
72 73 """
73 74 Parameters
74 75 ----------
75 76 shell : IPython shell
76 77
77 78 """
78 79 super(OctaveMagics, self).__init__(shell)
79 80 self._oct = oct2py.Oct2Py()
80 81 self._plot_format = 'png'
81 82
82 83 # Allow publish_display_data to be overridden for
83 84 # testing purposes.
84 85 self._publish_display_data = publish_display_data
85 86
86 87
87 88 def _fix_gnuplot_svg_size(self, image, size=None):
88 89 """
89 90 GnuPlot SVGs do not have height/width attributes. Set
90 91 these to be the same as the viewBox, so that the browser
91 92 scales the image correctly.
92 93
93 94 Parameters
94 95 ----------
95 96 image : str
96 97 SVG data.
97 98 size : tuple of int
98 99 Image width, height.
99 100
100 101 """
101 102 (svg,) = minidom.parseString(image).getElementsByTagName('svg')
102 103 viewbox = svg.getAttribute('viewBox').split(' ')
103 104
104 105 if size is not None:
105 106 width, height = size
106 107 else:
107 108 width, height = viewbox[2:]
108 109
109 110 svg.setAttribute('width', '%dpx' % width)
110 111 svg.setAttribute('height', '%dpx' % height)
111 112 return svg.toxml()
112 113
113 114
114 115 @skip_doctest
115 116 @line_magic
116 117 def octave_push(self, line):
117 118 '''
118 119 Line-level magic that pushes a variable to Octave.
119 120
120 121 `line` should be made up of whitespace separated variable names in the
121 122 IPython namespace::
122 123
123 124 In [7]: import numpy as np
124 125
125 126 In [8]: X = np.arange(5)
126 127
127 128 In [9]: X.mean()
128 129 Out[9]: 2.0
129 130
130 131 In [10]: %octave_push X
131 132
132 133 In [11]: %octave mean(X)
133 134 Out[11]: 2.0
134 135
135 136 '''
136 137 inputs = line.split(' ')
137 138 for input in inputs:
138 139 input = unicode_to_str(input)
139 140 self._oct.put(input, self.shell.user_ns[input])
140 141
141 142
142 143 @skip_doctest
143 144 @line_magic
144 145 def octave_pull(self, line):
145 146 '''
146 147 Line-level magic that pulls a variable from Octave.
147 148
149 ::
150
148 151 In [18]: _ = %octave x = [1 2; 3 4]; y = 'hello'
149 152
150 153 In [19]: %octave_pull x y
151 154
152 155 In [20]: x
153 156 Out[20]:
154 157 array([[ 1., 2.],
155 158 [ 3., 4.]])
156 159
157 160 In [21]: y
158 161 Out[21]: 'hello'
159 162
160 163 '''
161 164 outputs = line.split(' ')
162 165 for output in outputs:
163 166 output = unicode_to_str(output)
164 167 self.shell.push({output: self._oct.get(output)})
165 168
166 169
167 170 @skip_doctest
168 171 @magic_arguments()
169 172 @argument(
170 173 '-i', '--input', action='append',
171 174 help='Names of input variables to be pushed to Octave. Multiple names '
172 175 'can be passed, separated by commas with no whitespace.'
173 176 )
174 177 @argument(
175 178 '-o', '--output', action='append',
176 179 help='Names of variables to be pulled from Octave after executing cell '
177 180 'body. Multiple names can be passed, separated by commas with no '
178 181 'whitespace.'
179 182 )
180 183 @argument(
181 184 '-s', '--size', action='store',
182 185 help='Pixel size of plots, "width,height". Default is "-s 400,250".'
183 186 )
184 187 @argument(
185 188 '-f', '--format', action='store',
186 189 help='Plot format (png, svg or jpg).'
187 190 )
188 191
189 192 @needs_local_scope
190 193 @argument(
191 194 'code',
192 195 nargs='*',
193 196 )
194 197 @line_cell_magic
195 198 def octave(self, line, cell=None, local_ns=None):
196 199 '''
197 200 Execute code in Octave, and pull some of the results back into the
198 Python namespace.
201 Python namespace::
199 202
200 203 In [9]: %octave X = [1 2; 3 4]; mean(X)
201 204 Out[9]: array([[ 2., 3.]])
202 205
203 206 As a cell, this will run a block of Octave code, without returning any
204 207 value::
205 208
206 209 In [10]: %%octave
207 210 ....: p = [-2, -1, 0, 1, 2]
208 211 ....: polyout(p, 'x')
209 212
210 213 -2*x^4 - 1*x^3 + 0*x^2 + 1*x^1 + 2
211 214
212 In the notebook, plots are published as the output of the cell, e.g.
215 In the notebook, plots are published as the output of the cell, e.g.::
213 216
214 %octave plot([1 2 3], [4 5 6])
217 %octave plot([1 2 3], [4 5 6])
215 218
216 219 will create a line plot.
217 220
218 221 Objects can be passed back and forth between Octave and IPython via the
219 222 -i and -o flags in line::
220 223
221 224 In [14]: Z = np.array([1, 4, 5, 10])
222 225
223 226 In [15]: %octave -i Z mean(Z)
224 227 Out[15]: array([ 5.])
225 228
226 229
227 230 In [16]: %octave -o W W = Z * mean(Z)
228 231 Out[16]: array([ 5., 20., 25., 50.])
229 232
230 233 In [17]: W
231 234 Out[17]: array([ 5., 20., 25., 50.])
232 235
233 236 The size and format of output plots can be specified::
234 237
235 238 In [18]: %%octave -s 600,800 -f svg
236 239 ...: plot([1, 2, 3]);
237 240
238 241 '''
239 242 args = parse_argstring(self.octave, line)
240 243
241 244 # arguments 'code' in line are prepended to the cell lines
242 245 if cell is None:
243 246 code = ''
244 247 return_output = True
245 248 else:
246 249 code = cell
247 250 return_output = False
248 251
249 252 code = ' '.join(args.code) + code
250 253
251 254 # if there is no local namespace then default to an empty dict
252 255 if local_ns is None:
253 256 local_ns = {}
254 257
255 258 if args.input:
256 259 for input in ','.join(args.input).split(','):
257 260 input = unicode_to_str(input)
258 261 try:
259 262 val = local_ns[input]
260 263 except KeyError:
261 264 val = self.shell.user_ns[input]
262 265 self._oct.put(input, val)
263 266
264 267 # generate plots in a temporary directory
265 268 plot_dir = tempfile.mkdtemp().replace('\\', '/')
266 269 if args.size is not None:
267 270 size = args.size
268 271 else:
269 272 size = '400,240'
270 273
271 274 if args.format is not None:
272 275 plot_format = args.format
273 276 else:
274 277 plot_format = 'png'
275 278
276 279 pre_call = '''
277 280 global __ipy_figures = [];
278 281 page_screen_output(0);
279 282
280 283 function fig_create(src, event)
281 284 global __ipy_figures;
282 285 __ipy_figures(size(__ipy_figures) + 1) = src;
283 286 set(src, "visible", "off");
284 287 end
285 288
286 289 set(0, 'DefaultFigureCreateFcn', @fig_create);
287 290
288 291 close all;
289 292 clear ans;
290 293
291 294 # ___<end_pre_call>___ #
292 295 '''
293 296
294 297 post_call = '''
295 298 # ___<start_post_call>___ #
296 299
297 300 # Save output of the last execution
298 301 if exist("ans") == 1
299 302 _ = ans;
300 303 else
301 304 _ = nan;
302 305 end
303 306
304 307 for f = __ipy_figures
305 308 outfile = sprintf('%(plot_dir)s/__ipy_oct_fig_%%03d.png', f);
306 309 try
307 310 print(f, outfile, '-d%(plot_format)s', '-tight', '-S%(size)s');
308 311 end
309 312 end
310 313
311 314 ''' % locals()
312 315
313 316 code = ' '.join((pre_call, code, post_call))
314 317 try:
315 318 text_output = self._oct.run(code, verbose=False)
316 319 except (oct2py.Oct2PyError) as exception:
317 320 msg = exception.message
318 321 msg = msg.split('# ___<end_pre_call>___ #')[1]
319 322 msg = msg.split('# ___<start_post_call>___ #')[0]
320 323 raise OctaveMagicError('Octave could not complete execution. '
321 324 'Traceback (currently broken in oct2py): %s'
322 325 % msg)
323 326
324 327 key = 'OctaveMagic.Octave'
325 328 display_data = []
326 329
327 330 # Publish text output
328 331 if text_output:
329 332 display_data.append((key, {'text/plain': text_output}))
330 333
331 334 # Publish images
332 335 images = [open(imgfile, 'rb').read() for imgfile in \
333 336 glob("%s/*" % plot_dir)]
334 337 rmtree(plot_dir)
335 338
336 339 plot_mime_type = _mimetypes.get(plot_format, 'image/png')
337 340 width, height = [int(s) for s in size.split(',')]
338 341 for image in images:
339 342 if plot_format == 'svg':
340 343 image = self._fix_gnuplot_svg_size(image, size=(width, height))
341 344 display_data.append((key, {plot_mime_type: image}))
342 345
343 346 if args.output:
344 347 for output in ','.join(args.output).split(','):
345 348 output = unicode_to_str(output)
346 349 self.shell.push({output: self._oct.get(output)})
347 350
348 351 for source, data in display_data:
349 352 self._publish_display_data(source, data)
350 353
351 354 if return_output:
352 355 ans = self._oct.get('_')
353 356
354 357 # Unfortunately, Octave doesn't have a "None" object,
355 358 # so we can't return any NaN outputs
356 359 if np.isscalar(ans) and np.isnan(ans):
357 360 ans = None
358 361
359 362 return ans
360 363
361 364
362 365 __doc__ = __doc__.format(
363 OCTAVE_DOC = ' '*8 + OctaveMagics.octave.__doc__,
364 OCTAVE_PUSH_DOC = ' '*8 + OctaveMagics.octave_push.__doc__,
365 OCTAVE_PULL_DOC = ' '*8 + OctaveMagics.octave_pull.__doc__
366 OCTAVE_DOC = dedent(OctaveMagics.octave.__doc__),
367 OCTAVE_PUSH_DOC = dedent(OctaveMagics.octave_push.__doc__),
368 OCTAVE_PULL_DOC = dedent(OctaveMagics.octave_pull.__doc__)
366 369 )
367 370
368 371
369 372 def load_ipython_extension(ip):
370 373 """Load the extension in IPython."""
371 374 ip.register_magics(OctaveMagics)
@@ -1,695 +1,696 b''
1 1 # -*- coding: utf-8 -*-
2 2 """
3 3 ======
4 4 Rmagic
5 5 ======
6 6
7 7 Magic command interface for interactive work with R via rpy2
8 8
9 9 .. note::
10 10
11 11 The ``rpy2`` package needs to be installed separately. It
12 12 can be obtained using ``easy_install`` or ``pip``.
13 13
14 14 You will also need a working copy of R.
15 15
16 16 Usage
17 17 =====
18 18
19 19 To enable the magics below, execute ``%load_ext rmagic``.
20 20
21 21 ``%R``
22 22
23 23 {R_DOC}
24 24
25 25 ``%Rpush``
26 26
27 27 {RPUSH_DOC}
28 28
29 29 ``%Rpull``
30 30
31 31 {RPULL_DOC}
32 32
33 33 ``%Rget``
34 34
35 35 {RGET_DOC}
36 36
37 37 """
38 38 from __future__ import print_function
39 39
40 40 #-----------------------------------------------------------------------------
41 41 # Copyright (C) 2012 The IPython Development Team
42 42 #
43 43 # Distributed under the terms of the BSD License. The full license is in
44 44 # the file COPYING, distributed as part of this software.
45 45 #-----------------------------------------------------------------------------
46 46
47 47 import sys
48 48 import tempfile
49 49 from glob import glob
50 50 from shutil import rmtree
51 51
52 52 # numpy and rpy2 imports
53 53
54 54 import numpy as np
55 55
56 56 import rpy2.rinterface as ri
57 57 import rpy2.robjects as ro
58 58 try:
59 59 from rpy2.robjects import pandas2ri
60 60 pandas2ri.activate()
61 61 except ImportError:
62 62 pandas2ri = None
63 63 from rpy2.robjects import numpy2ri
64 64 numpy2ri.activate()
65 65
66 66 # IPython imports
67 67
68 68 from IPython.core.displaypub import publish_display_data
69 69 from IPython.core.magic import (Magics, magics_class, line_magic,
70 70 line_cell_magic, needs_local_scope)
71 71 from IPython.testing.skipdoctest import skip_doctest
72 72 from IPython.core.magic_arguments import (
73 73 argument, magic_arguments, parse_argstring
74 74 )
75 75 from IPython.external.simplegeneric import generic
76 76 from IPython.utils.py3compat import (str_to_unicode, unicode_to_str, PY3,
77 77 unicode_type)
78 from IPython.utils.text import dedent
78 79
79 80 class RInterpreterError(ri.RRuntimeError):
80 81 """An error when running R code in a %%R magic cell."""
81 82 def __init__(self, line, err, stdout):
82 83 self.line = line
83 84 self.err = err.rstrip()
84 85 self.stdout = stdout.rstrip()
85 86
86 87 def __unicode__(self):
87 88 s = 'Failed to parse and evaluate line %r.\nR error message: %r' % \
88 89 (self.line, self.err)
89 90 if self.stdout and (self.stdout != self.err):
90 91 s += '\nR stdout:\n' + self.stdout
91 92 return s
92 93
93 94 if PY3:
94 95 __str__ = __unicode__
95 96 else:
96 97 def __str__(self):
97 98 return unicode_to_str(unicode(self), 'utf-8')
98 99
99 100 def Rconverter(Robj, dataframe=False):
100 101 """
101 102 Convert an object in R's namespace to one suitable
102 103 for ipython's namespace.
103 104
104 105 For a data.frame, it tries to return a structured array.
105 106 It first checks for colnames, then names.
106 107 If all are NULL, it returns np.asarray(Robj), else
107 108 it tries to construct a recarray
108 109
109 110 Parameters
110 111 ----------
111 112
112 113 Robj: an R object returned from rpy2
113 114 """
114 115 is_data_frame = ro.r('is.data.frame')
115 116 colnames = ro.r('colnames')
116 117 rownames = ro.r('rownames') # with pandas, these could be used for the index
117 118 names = ro.r('names')
118 119
119 120 if dataframe:
120 121 as_data_frame = ro.r('as.data.frame')
121 122 cols = colnames(Robj)
122 123 _names = names(Robj)
123 124 if cols != ri.NULL:
124 125 Robj = as_data_frame(Robj)
125 126 names = tuple(np.array(cols))
126 127 elif _names != ri.NULL:
127 128 names = tuple(np.array(_names))
128 129 else: # failed to find names
129 130 return np.asarray(Robj)
130 131 Robj = np.rec.fromarrays(Robj, names = names)
131 132 return np.asarray(Robj)
132 133
133 134 @generic
134 135 def pyconverter(pyobj):
135 136 """Convert Python objects to R objects. Add types using the decorator:
136 137
137 138 @pyconverter.when_type
138 139 """
139 140 return pyobj
140 141
141 142 # The default conversion for lists seems to make them a nested list. That has
142 143 # some advantages, but is rarely convenient, so for interactive use, we convert
143 144 # lists to a numpy array, which becomes an R vector.
144 145 @pyconverter.when_type(list)
145 146 def pyconverter_list(pyobj):
146 147 return np.asarray(pyobj)
147 148
148 149 if pandas2ri is None:
149 150 # pandas2ri was new in rpy2 2.3.3, so for now we'll fallback to pandas'
150 151 # conversion function.
151 152 try:
152 153 from pandas import DataFrame
153 154 from pandas.rpy.common import convert_to_r_dataframe
154 155 @pyconverter.when_type(DataFrame)
155 156 def pyconverter_dataframe(pyobj):
156 157 return convert_to_r_dataframe(pyobj, strings_as_factors=True)
157 158 except ImportError:
158 159 pass
159 160
160 161 @magics_class
161 162 class RMagics(Magics):
162 163 """A set of magics useful for interactive work with R via rpy2.
163 164 """
164 165
165 166 def __init__(self, shell, Rconverter=Rconverter,
166 167 pyconverter=pyconverter,
167 168 cache_display_data=False):
168 169 """
169 170 Parameters
170 171 ----------
171 172
172 173 shell : IPython shell
173 174
174 175 Rconverter : callable
175 176 To be called on values taken from R before putting them in the
176 177 IPython namespace.
177 178
178 179 pyconverter : callable
179 180 To be called on values in ipython namespace before
180 181 assigning to variables in rpy2.
181 182
182 183 cache_display_data : bool
183 184 If True, the published results of the final call to R are
184 185 cached in the variable 'display_cache'.
185 186
186 187 """
187 188 super(RMagics, self).__init__(shell)
188 189 self.cache_display_data = cache_display_data
189 190
190 191 self.r = ro.R()
191 192
192 193 self.Rstdout_cache = []
193 194 self.pyconverter = pyconverter
194 195 self.Rconverter = Rconverter
195 196
196 197 def eval(self, line):
197 198 '''
198 199 Parse and evaluate a line of R code with rpy2.
199 200 Returns the output to R's stdout() connection,
200 201 the value generated by evaluating the code, and a
201 202 boolean indicating whether the return value would be
202 203 visible if the line of code were evaluated in an R REPL.
203 204
204 205 R Code evaluation and visibility determination are
205 206 done via an R call of the form withVisible({<code>})
206 207
207 208 '''
208 209 old_writeconsole = ri.get_writeconsole()
209 210 ri.set_writeconsole(self.write_console)
210 211 try:
211 212 res = ro.r("withVisible({%s})" % line)
212 213 value = res[0] #value (R object)
213 214 visible = ro.conversion.ri2py(res[1])[0] #visible (boolean)
214 215 except (ri.RRuntimeError, ValueError) as exception:
215 216 warning_or_other_msg = self.flush() # otherwise next return seems to have copy of error
216 217 raise RInterpreterError(line, str_to_unicode(str(exception)), warning_or_other_msg)
217 218 text_output = self.flush()
218 219 ri.set_writeconsole(old_writeconsole)
219 220 return text_output, value, visible
220 221
221 222 def write_console(self, output):
222 223 '''
223 224 A hook to capture R's stdout in a cache.
224 225 '''
225 226 self.Rstdout_cache.append(output)
226 227
227 228 def flush(self):
228 229 '''
229 230 Flush R's stdout cache to a string, returning the string.
230 231 '''
231 232 value = ''.join([str_to_unicode(s, 'utf-8') for s in self.Rstdout_cache])
232 233 self.Rstdout_cache = []
233 234 return value
234 235
235 236 @skip_doctest
236 237 @needs_local_scope
237 238 @line_magic
238 239 def Rpush(self, line, local_ns=None):
239 240 '''
240 241 A line-level magic for R that pushes
241 242 variables from python to rpy2. The line should be made up
242 243 of whitespace separated variable names in the IPython
243 244 namespace::
244 245
245 246 In [7]: import numpy as np
246 247
247 248 In [8]: X = np.array([4.5,6.3,7.9])
248 249
249 250 In [9]: X.mean()
250 251 Out[9]: 6.2333333333333343
251 252
252 253 In [10]: %Rpush X
253 254
254 255 In [11]: %R mean(X)
255 256 Out[11]: array([ 6.23333333])
256 257
257 258 '''
258 259 if local_ns is None:
259 260 local_ns = {}
260 261
261 262 inputs = line.split(' ')
262 263 for input in inputs:
263 264 try:
264 265 val = local_ns[input]
265 266 except KeyError:
266 267 try:
267 268 val = self.shell.user_ns[input]
268 269 except KeyError:
269 270 # reraise the KeyError as a NameError so that it looks like
270 271 # the standard python behavior when you use an unnamed
271 272 # variable
272 273 raise NameError("name '%s' is not defined" % input)
273 274
274 275 self.r.assign(input, self.pyconverter(val))
275 276
276 277 @skip_doctest
277 278 @magic_arguments()
278 279 @argument(
279 280 '-d', '--as_dataframe', action='store_true',
280 281 default=False,
281 282 help='Convert objects to data.frames before returning to ipython.'
282 283 )
283 284 @argument(
284 285 'outputs',
285 286 nargs='*',
286 287 )
287 288 @line_magic
288 289 def Rpull(self, line):
289 290 '''
290 291 A line-level magic for R that pulls
291 292 variables from python to rpy2::
292 293
293 294 In [18]: _ = %R x = c(3,4,6.7); y = c(4,6,7); z = c('a',3,4)
294 295
295 296 In [19]: %Rpull x y z
296 297
297 298 In [20]: x
298 299 Out[20]: array([ 3. , 4. , 6.7])
299 300
300 301 In [21]: y
301 302 Out[21]: array([ 4., 6., 7.])
302 303
303 304 In [22]: z
304 305 Out[22]:
305 306 array(['a', '3', '4'],
306 307 dtype='|S1')
307 308
308 309
309 310 If --as_dataframe, then each object is returned as a structured array
310 311 after first passed through "as.data.frame" in R before
311 312 being calling self.Rconverter.
312 313 This is useful when a structured array is desired as output, or
313 314 when the object in R has mixed data types.
314 315 See the %%R docstring for more examples.
315 316
316 317 Notes
317 318 -----
318 319
319 320 Beware that R names can have '.' so this is not fool proof.
320 321 To avoid this, don't name your R objects with '.'s...
321 322
322 323 '''
323 324 args = parse_argstring(self.Rpull, line)
324 325 outputs = args.outputs
325 326 for output in outputs:
326 327 self.shell.push({output:self.Rconverter(self.r(output),dataframe=args.as_dataframe)})
327 328
328 329 @skip_doctest
329 330 @magic_arguments()
330 331 @argument(
331 332 '-d', '--as_dataframe', action='store_true',
332 333 default=False,
333 334 help='Convert objects to data.frames before returning to ipython.'
334 335 )
335 336 @argument(
336 337 'output',
337 338 nargs=1,
338 339 type=str,
339 340 )
340 341 @line_magic
341 342 def Rget(self, line):
342 343 '''
343 344 Return an object from rpy2, possibly as a structured array (if possible).
344 345 Similar to Rpull except only one argument is accepted and the value is
345 346 returned rather than pushed to self.shell.user_ns::
346 347
347 348 In [3]: dtype=[('x', '<i4'), ('y', '<f8'), ('z', '|S1')]
348 349
349 350 In [4]: datapy = np.array([(1, 2.9, 'a'), (2, 3.5, 'b'), (3, 2.1, 'c'), (4, 5, 'e')], dtype=dtype)
350 351
351 352 In [5]: %R -i datapy
352 353
353 354 In [6]: %Rget datapy
354 355 Out[6]:
355 356 array([['1', '2', '3', '4'],
356 357 ['2', '3', '2', '5'],
357 358 ['a', 'b', 'c', 'e']],
358 359 dtype='|S1')
359 360
360 361 In [7]: %Rget -d datapy
361 362 Out[7]:
362 363 array([(1, 2.9, 'a'), (2, 3.5, 'b'), (3, 2.1, 'c'), (4, 5.0, 'e')],
363 364 dtype=[('x', '<i4'), ('y', '<f8'), ('z', '|S1')])
364 365
365 366 '''
366 367 args = parse_argstring(self.Rget, line)
367 368 output = args.output
368 369 return self.Rconverter(self.r(output[0]),dataframe=args.as_dataframe)
369 370
370 371
371 372 @skip_doctest
372 373 @magic_arguments()
373 374 @argument(
374 375 '-i', '--input', action='append',
375 376 help='Names of input variable from shell.user_ns to be assigned to R variables of the same names after calling self.pyconverter. Multiple names can be passed separated only by commas with no whitespace.'
376 377 )
377 378 @argument(
378 379 '-o', '--output', action='append',
379 380 help='Names of variables to be pushed from rpy2 to shell.user_ns after executing cell body and applying self.Rconverter. Multiple names can be passed separated only by commas with no whitespace.'
380 381 )
381 382 @argument(
382 383 '-w', '--width', type=int,
383 384 help='Width of png plotting device sent as an argument to *png* in R.'
384 385 )
385 386 @argument(
386 387 '-h', '--height', type=int,
387 388 help='Height of png plotting device sent as an argument to *png* in R.'
388 389 )
389 390
390 391 @argument(
391 392 '-d', '--dataframe', action='append',
392 393 help='Convert these objects to data.frames and return as structured arrays.'
393 394 )
394 395 @argument(
395 396 '-u', '--units', type=unicode_type, choices=["px", "in", "cm", "mm"],
396 397 help='Units of png plotting device sent as an argument to *png* in R. One of ["px", "in", "cm", "mm"].'
397 398 )
398 399 @argument(
399 400 '-r', '--res', type=int,
400 401 help='Resolution of png plotting device sent as an argument to *png* in R. Defaults to 72 if *units* is one of ["in", "cm", "mm"].'
401 402 )
402 403 @argument(
403 404 '-p', '--pointsize', type=int,
404 405 help='Pointsize of png plotting device sent as an argument to *png* in R.'
405 406 )
406 407 @argument(
407 408 '-b', '--bg',
408 409 help='Background of png plotting device sent as an argument to *png* in R.'
409 410 )
410 411 @argument(
411 412 '-n', '--noreturn',
412 413 help='Force the magic to not return anything.',
413 414 action='store_true',
414 415 default=False
415 416 )
416 417 @argument(
417 418 'code',
418 419 nargs='*',
419 420 )
420 421 @needs_local_scope
421 422 @line_cell_magic
422 423 def R(self, line, cell=None, local_ns=None):
423 424 '''
424 425 Execute code in R, and pull some of the results back into the Python namespace.
425 426
426 427 In line mode, this will evaluate an expression and convert the returned value to a Python object.
427 428 The return value is determined by rpy2's behaviour of returning the result of evaluating the
428 429 final line.
429 430
430 431 Multiple R lines can be executed by joining them with semicolons::
431 432
432 433 In [9]: %R X=c(1,4,5,7); sd(X); mean(X)
433 434 Out[9]: array([ 4.25])
434 435
435 436 In cell mode, this will run a block of R code. The resulting value
436 437 is printed if it would printed be when evaluating the same code
437 438 within a standard R REPL.
438 439
439 440 Nothing is returned to python by default in cell mode::
440 441
441 442 In [10]: %%R
442 443 ....: Y = c(2,4,3,9)
443 444 ....: summary(lm(Y~X))
444 445
445 446 Call:
446 447 lm(formula = Y ~ X)
447 448
448 449 Residuals:
449 450 1 2 3 4
450 451 0.88 -0.24 -2.28 1.64
451 452
452 453 Coefficients:
453 454 Estimate Std. Error t value Pr(>|t|)
454 455 (Intercept) 0.0800 2.3000 0.035 0.975
455 456 X 1.0400 0.4822 2.157 0.164
456 457
457 458 Residual standard error: 2.088 on 2 degrees of freedom
458 459 Multiple R-squared: 0.6993,Adjusted R-squared: 0.549
459 460 F-statistic: 4.651 on 1 and 2 DF, p-value: 0.1638
460 461
461 462 In the notebook, plots are published as the output of the cell::
462 463
463 464 %R plot(X, Y)
464 465
465 466 will create a scatter plot of X bs Y.
466 467
467 468 If cell is not None and line has some R code, it is prepended to
468 469 the R code in cell.
469 470
470 471 Objects can be passed back and forth between rpy2 and python via the -i -o flags in line::
471 472
472 473 In [14]: Z = np.array([1,4,5,10])
473 474
474 475 In [15]: %R -i Z mean(Z)
475 476 Out[15]: array([ 5.])
476 477
477 478
478 479 In [16]: %R -o W W=Z*mean(Z)
479 480 Out[16]: array([ 5., 20., 25., 50.])
480 481
481 482 In [17]: W
482 483 Out[17]: array([ 5., 20., 25., 50.])
483 484
484 485 The return value is determined by these rules:
485 486
486 487 * If the cell is not None, the magic returns None.
487 488
488 489 * If the cell evaluates as False, the resulting value is returned
489 490 unless the final line prints something to the console, in
490 491 which case None is returned.
491 492
492 493 * If the final line results in a NULL value when evaluated
493 494 by rpy2, then None is returned.
494 495
495 496 * No attempt is made to convert the final value to a structured array.
496 497 Use the --dataframe flag or %Rget to push / return a structured array.
497 498
498 499 * If the -n flag is present, there is no return value.
499 500
500 501 * A trailing ';' will also result in no return value as the last
501 502 value in the line is an empty string.
502 503
503 504 The --dataframe argument will attempt to return structured arrays.
504 505 This is useful for dataframes with
505 506 mixed data types. Note also that for a data.frame,
506 507 if it is returned as an ndarray, it is transposed::
507 508
508 509 In [18]: dtype=[('x', '<i4'), ('y', '<f8'), ('z', '|S1')]
509 510
510 511 In [19]: datapy = np.array([(1, 2.9, 'a'), (2, 3.5, 'b'), (3, 2.1, 'c'), (4, 5, 'e')], dtype=dtype)
511 512
512 513 In [20]: %%R -o datar
513 514 datar = datapy
514 515 ....:
515 516
516 517 In [21]: datar
517 518 Out[21]:
518 519 array([['1', '2', '3', '4'],
519 520 ['2', '3', '2', '5'],
520 521 ['a', 'b', 'c', 'e']],
521 522 dtype='|S1')
522 523
523 524 In [22]: %%R -d datar
524 525 datar = datapy
525 526 ....:
526 527
527 528 In [23]: datar
528 529 Out[23]:
529 530 array([(1, 2.9, 'a'), (2, 3.5, 'b'), (3, 2.1, 'c'), (4, 5.0, 'e')],
530 531 dtype=[('x', '<i4'), ('y', '<f8'), ('z', '|S1')])
531 532
532 533 The --dataframe argument first tries colnames, then names.
533 534 If both are NULL, it returns an ndarray (i.e. unstructured)::
534 535
535 536 In [1]: %R mydata=c(4,6,8.3); NULL
536 537
537 538 In [2]: %R -d mydata
538 539
539 540 In [3]: mydata
540 541 Out[3]: array([ 4. , 6. , 8.3])
541 542
542 543 In [4]: %R names(mydata) = c('a','b','c'); NULL
543 544
544 545 In [5]: %R -d mydata
545 546
546 547 In [6]: mydata
547 548 Out[6]:
548 549 array((4.0, 6.0, 8.3),
549 550 dtype=[('a', '<f8'), ('b', '<f8'), ('c', '<f8')])
550 551
551 552 In [7]: %R -o mydata
552 553
553 554 In [8]: mydata
554 555 Out[8]: array([ 4. , 6. , 8.3])
555 556
556 557 '''
557 558
558 559 args = parse_argstring(self.R, line)
559 560
560 561 # arguments 'code' in line are prepended to
561 562 # the cell lines
562 563
563 564 if cell is None:
564 565 code = ''
565 566 return_output = True
566 567 line_mode = True
567 568 else:
568 569 code = cell
569 570 return_output = False
570 571 line_mode = False
571 572
572 573 code = ' '.join(args.code) + code
573 574
574 575 # if there is no local namespace then default to an empty dict
575 576 if local_ns is None:
576 577 local_ns = {}
577 578
578 579 if args.input:
579 580 for input in ','.join(args.input).split(','):
580 581 try:
581 582 val = local_ns[input]
582 583 except KeyError:
583 584 try:
584 585 val = self.shell.user_ns[input]
585 586 except KeyError:
586 587 raise NameError("name '%s' is not defined" % input)
587 588 self.r.assign(input, self.pyconverter(val))
588 589
589 590 if getattr(args, 'units') is not None:
590 591 if args.units != "px" and getattr(args, 'res') is None:
591 592 args.res = 72
592 593 args.units = '"%s"' % args.units
593 594
594 595 png_argdict = dict([(n, getattr(args, n)) for n in ['units', 'res', 'height', 'width', 'bg', 'pointsize']])
595 596 png_args = ','.join(['%s=%s' % (o,v) for o, v in png_argdict.items() if v is not None])
596 597 # execute the R code in a temporary directory
597 598
598 599 tmpd = tempfile.mkdtemp()
599 600 self.r('png("%s/Rplots%%03d.png",%s)' % (tmpd.replace('\\', '/'), png_args))
600 601
601 602 text_output = ''
602 603 try:
603 604 if line_mode:
604 605 for line in code.split(';'):
605 606 text_result, result, visible = self.eval(line)
606 607 text_output += text_result
607 608 if text_result:
608 609 # the last line printed something to the console so we won't return it
609 610 return_output = False
610 611 else:
611 612 text_result, result, visible = self.eval(code)
612 613 text_output += text_result
613 614 if visible:
614 615 old_writeconsole = ri.get_writeconsole()
615 616 ri.set_writeconsole(self.write_console)
616 617 ro.r.show(result)
617 618 text_output += self.flush()
618 619 ri.set_writeconsole(old_writeconsole)
619 620
620 621 except RInterpreterError as e:
621 622 print(e.stdout)
622 623 if not e.stdout.endswith(e.err):
623 624 print(e.err)
624 625 rmtree(tmpd)
625 626 return
626 627
627 628 self.r('dev.off()')
628 629
629 630 # read out all the saved .png files
630 631
631 632 images = [open(imgfile, 'rb').read() for imgfile in glob("%s/Rplots*png" % tmpd)]
632 633
633 634 # now publish the images
634 635 # mimicking IPython/zmq/pylab/backend_inline.py
635 636 fmt = 'png'
636 637 mimetypes = { 'png' : 'image/png', 'svg' : 'image/svg+xml' }
637 638 mime = mimetypes[fmt]
638 639
639 640 # publish the printed R objects, if any
640 641
641 642 display_data = []
642 643 if text_output:
643 644 display_data.append(('RMagic.R', {'text/plain':text_output}))
644 645
645 646 # flush text streams before sending figures, helps a little with output
646 647 for image in images:
647 648 # synchronization in the console (though it's a bandaid, not a real sln)
648 649 sys.stdout.flush(); sys.stderr.flush()
649 650 display_data.append(('RMagic.R', {mime: image}))
650 651
651 652 # kill the temporary directory
652 653 rmtree(tmpd)
653 654
654 655 # try to turn every output into a numpy array
655 656 # this means that output are assumed to be castable
656 657 # as numpy arrays
657 658
658 659 if args.output:
659 660 for output in ','.join(args.output).split(','):
660 661 self.shell.push({output:self.Rconverter(self.r(output), dataframe=False)})
661 662
662 663 if args.dataframe:
663 664 for output in ','.join(args.dataframe).split(','):
664 665 self.shell.push({output:self.Rconverter(self.r(output), dataframe=True)})
665 666
666 667 for tag, disp_d in display_data:
667 668 publish_display_data(tag, disp_d)
668 669
669 670 # this will keep a reference to the display_data
670 671 # which might be useful to other objects who happen to use
671 672 # this method
672 673
673 674 if self.cache_display_data:
674 675 self.display_cache = display_data
675 676
676 677 # if in line mode and return_output, return the result as an ndarray
677 678 if return_output and not args.noreturn:
678 679 if result != ri.NULL:
679 680 return self.Rconverter(result, dataframe=False)
680 681
681 682 __doc__ = __doc__.format(
682 R_DOC = ' '*8 + RMagics.R.__doc__,
683 RPUSH_DOC = ' '*8 + RMagics.Rpush.__doc__,
684 RPULL_DOC = ' '*8 + RMagics.Rpull.__doc__,
685 RGET_DOC = ' '*8 + RMagics.Rget.__doc__
683 R_DOC = dedent(RMagics.R.__doc__),
684 RPUSH_DOC = dedent(RMagics.Rpush.__doc__),
685 RPULL_DOC = dedent(RMagics.Rpull.__doc__),
686 RGET_DOC = dedent(RMagics.Rget.__doc__)
686 687 )
687 688
688 689
689 690 def load_ipython_extension(ip):
690 691 """Load the extension in IPython."""
691 692 ip.register_magics(RMagics)
692 693 # Initialising rpy2 interferes with readline. Since, at this point, we've
693 694 # probably just loaded rpy2, we reset the delimiters. See issue gh-2759.
694 695 if ip.has_readline:
695 696 ip.readline.set_completer_delims(ip.readline_delims)
@@ -1,442 +1,441 b''
1 1 # encoding: utf-8
2 2 """
3 3 =============
4 4 parallelmagic
5 5 =============
6 6
7 7 Magic command interface for interactive parallel work.
8 8
9 9 Usage
10 10 =====
11 11
12 12 ``%autopx``
13 13
14 14 {AUTOPX_DOC}
15 15
16 16 ``%px``
17 17
18 18 {PX_DOC}
19 19
20 20 ``%pxresult``
21 21
22 22 {RESULT_DOC}
23 23
24 24 ``%pxconfig``
25 25
26 26 {CONFIG_DOC}
27 27
28 28 """
29 29 from __future__ import print_function
30 30
31 31 #-----------------------------------------------------------------------------
32 32 # Copyright (C) 2008 The IPython Development Team
33 33 #
34 34 # Distributed under the terms of the BSD License. The full license is in
35 35 # the file COPYING, distributed as part of this software.
36 36 #-----------------------------------------------------------------------------
37 37
38 38 #-----------------------------------------------------------------------------
39 39 # Imports
40 40 #-----------------------------------------------------------------------------
41 41
42 42 import ast
43 43 import re
44 44
45 45 from IPython.core.error import UsageError
46 46 from IPython.core.magic import Magics
47 47 from IPython.core import magic_arguments
48 48 from IPython.testing.skipdoctest import skip_doctest
49 from IPython.utils.text import dedent
49 50
50 51 #-----------------------------------------------------------------------------
51 52 # Definitions of magic functions for use with IPython
52 53 #-----------------------------------------------------------------------------
53 54
54 55
55 56 NO_LAST_RESULT = "%pxresult recalls last %px result, which has not yet been used."
56 57
57 58 def exec_args(f):
58 59 """decorator for adding block/targets args for execution
59 60
60 61 applied to %pxconfig and %%px
61 62 """
62 63 args = [
63 64 magic_arguments.argument('-b', '--block', action="store_const",
64 65 const=True, dest='block',
65 66 help="use blocking (sync) execution",
66 67 ),
67 68 magic_arguments.argument('-a', '--noblock', action="store_const",
68 69 const=False, dest='block',
69 70 help="use non-blocking (async) execution",
70 71 ),
71 72 magic_arguments.argument('-t', '--targets', type=str,
72 73 help="specify the targets on which to execute",
73 74 ),
74 75 magic_arguments.argument('--local', action="store_const",
75 76 const=True, dest="local",
76 77 help="also execute the cell in the local namespace",
77 78 ),
78 79 magic_arguments.argument('--verbose', action="store_const",
79 80 const=True, dest="set_verbose",
80 81 help="print a message at each execution",
81 82 ),
82 83 magic_arguments.argument('--no-verbose', action="store_const",
83 84 const=False, dest="set_verbose",
84 85 help="don't print any messages",
85 86 ),
86 87 ]
87 88 for a in args:
88 89 f = a(f)
89 90 return f
90 91
91 92 def output_args(f):
92 93 """decorator for output-formatting args
93 94
94 95 applied to %pxresult and %%px
95 96 """
96 97 args = [
97 98 magic_arguments.argument('-r', action="store_const", dest='groupby',
98 99 const='order',
99 100 help="collate outputs in order (same as group-outputs=order)"
100 101 ),
101 102 magic_arguments.argument('-e', action="store_const", dest='groupby',
102 103 const='engine',
103 104 help="group outputs by engine (same as group-outputs=engine)"
104 105 ),
105 106 magic_arguments.argument('--group-outputs', dest='groupby', type=str,
106 107 choices=['engine', 'order', 'type'], default='type',
107 108 help="""Group the outputs in a particular way.
108 109
109 110 Choices are:
110 111
111 type: group outputs of all engines by type (stdout, stderr, displaypub, etc.).
112
113 engine: display all output for each engine together.
114
115 order: like type, but individual displaypub output from each engine is collated.
116 For example, if multiple plots are generated by each engine, the first
117 figure of each engine will be displayed, then the second of each, etc.
112 **type**: group outputs of all engines by type (stdout, stderr, displaypub, etc.).
113 **engine**: display all output for each engine together.
114 **order**: like type, but individual displaypub output from each engine is collated.
115 For example, if multiple plots are generated by each engine, the first
116 figure of each engine will be displayed, then the second of each, etc.
118 117 """
119 118 ),
120 119 magic_arguments.argument('-o', '--out', dest='save_name', type=str,
121 120 help="""store the AsyncResult object for this computation
122 121 in the global namespace under this name.
123 122 """
124 123 ),
125 124 ]
126 125 for a in args:
127 126 f = a(f)
128 127 return f
129 128
130 129 class ParallelMagics(Magics):
131 130 """A set of magics useful when controlling a parallel IPython cluster.
132 131 """
133 132
134 133 # magic-related
135 134 magics = None
136 135 registered = True
137 136
138 137 # suffix for magics
139 138 suffix = ''
140 139 # A flag showing if autopx is activated or not
141 140 _autopx = False
142 141 # the current view used by the magics:
143 142 view = None
144 143 # last result cache for %pxresult
145 144 last_result = None
146 145 # verbose flag
147 146 verbose = False
148 147
149 148 def __init__(self, shell, view, suffix=''):
150 149 self.view = view
151 150 self.suffix = suffix
152 151
153 152 # register magics
154 153 self.magics = dict(cell={},line={})
155 154 line_magics = self.magics['line']
156 155
157 156 px = 'px' + suffix
158 157 if not suffix:
159 158 # keep %result for legacy compatibility
160 159 line_magics['result'] = self.result
161 160
162 161 line_magics['pxresult' + suffix] = self.result
163 162 line_magics[px] = self.px
164 163 line_magics['pxconfig' + suffix] = self.pxconfig
165 164 line_magics['auto' + px] = self.autopx
166 165
167 166 self.magics['cell'][px] = self.cell_px
168 167
169 168 super(ParallelMagics, self).__init__(shell=shell)
170 169
171 170 def _eval_target_str(self, ts):
172 171 if ':' in ts:
173 172 targets = eval("self.view.client.ids[%s]" % ts)
174 173 elif 'all' in ts:
175 174 targets = 'all'
176 175 else:
177 176 targets = eval(ts)
178 177 return targets
179 178
180 179 @magic_arguments.magic_arguments()
181 180 @exec_args
182 181 def pxconfig(self, line):
183 182 """configure default targets/blocking for %px magics"""
184 183 args = magic_arguments.parse_argstring(self.pxconfig, line)
185 184 if args.targets:
186 185 self.view.targets = self._eval_target_str(args.targets)
187 186 if args.block is not None:
188 187 self.view.block = args.block
189 188 if args.set_verbose is not None:
190 189 self.verbose = args.set_verbose
191 190
192 191 @magic_arguments.magic_arguments()
193 192 @output_args
194 193 @skip_doctest
195 194 def result(self, line=''):
196 195 """Print the result of the last asynchronous %px command.
197 196
198 197 This lets you recall the results of %px computations after
199 198 asynchronous submission (block=False).
200 199
201 200 Examples
202 201 --------
203 202 ::
204 203
205 204 In [23]: %px os.getpid()
206 205 Async parallel execution on engine(s): all
207 206
208 207 In [24]: %pxresult
209 208 Out[8:10]: 60920
210 209 Out[9:10]: 60921
211 210 Out[10:10]: 60922
212 211 Out[11:10]: 60923
213 212 """
214 213 args = magic_arguments.parse_argstring(self.result, line)
215 214
216 215 if self.last_result is None:
217 216 raise UsageError(NO_LAST_RESULT)
218 217
219 218 self.last_result.get()
220 219 self.last_result.display_outputs(groupby=args.groupby)
221 220
222 221 @skip_doctest
223 222 def px(self, line=''):
224 223 """Executes the given python command in parallel.
225 224
226 225 Examples
227 226 --------
228 227 ::
229 228
230 229 In [24]: %px a = os.getpid()
231 230 Parallel execution on engine(s): all
232 231
233 232 In [25]: %px print a
234 233 [stdout:0] 1234
235 234 [stdout:1] 1235
236 235 [stdout:2] 1236
237 236 [stdout:3] 1237
238 237 """
239 238 return self.parallel_execute(line)
240 239
241 240 def parallel_execute(self, cell, block=None, groupby='type', save_name=None):
242 241 """implementation used by %px and %%parallel"""
243 242
244 243 # defaults:
245 244 block = self.view.block if block is None else block
246 245
247 246 base = "Parallel" if block else "Async parallel"
248 247
249 248 targets = self.view.targets
250 249 if isinstance(targets, list) and len(targets) > 10:
251 250 str_targets = str(targets[:4])[:-1] + ', ..., ' + str(targets[-4:])[1:]
252 251 else:
253 252 str_targets = str(targets)
254 253 if self.verbose:
255 254 print(base + " execution on engine(s): %s" % str_targets)
256 255
257 256 result = self.view.execute(cell, silent=False, block=False)
258 257 self.last_result = result
259 258
260 259 if save_name:
261 260 self.shell.user_ns[save_name] = result
262 261
263 262 if block:
264 263 result.get()
265 264 result.display_outputs(groupby)
266 265 else:
267 266 # return AsyncResult only on non-blocking submission
268 267 return result
269 268
270 269 @magic_arguments.magic_arguments()
271 270 @exec_args
272 271 @output_args
273 272 @skip_doctest
274 273 def cell_px(self, line='', cell=None):
275 274 """Executes the cell in parallel.
276 275
277 276 Examples
278 277 --------
279 278 ::
280 279
281 280 In [24]: %%px --noblock
282 281 ....: a = os.getpid()
283 282 Async parallel execution on engine(s): all
284 283
285 284 In [25]: %%px
286 285 ....: print a
287 286 [stdout:0] 1234
288 287 [stdout:1] 1235
289 288 [stdout:2] 1236
290 289 [stdout:3] 1237
291 290 """
292 291
293 292 args = magic_arguments.parse_argstring(self.cell_px, line)
294 293
295 294 if args.targets:
296 295 save_targets = self.view.targets
297 296 self.view.targets = self._eval_target_str(args.targets)
298 297 # if running local, don't block until after local has run
299 298 block = False if args.local else args.block
300 299 try:
301 300 ar = self.parallel_execute(cell, block=block,
302 301 groupby=args.groupby,
303 302 save_name=args.save_name,
304 303 )
305 304 finally:
306 305 if args.targets:
307 306 self.view.targets = save_targets
308 307
309 308 # run locally after submitting remote
310 309 block = self.view.block if args.block is None else args.block
311 310 if args.local:
312 311 self.shell.run_cell(cell)
313 312 # now apply blocking behavor to remote execution
314 313 if block:
315 314 ar.get()
316 315 ar.display_outputs(args.groupby)
317 316 if not block:
318 317 return ar
319 318
320 319 @skip_doctest
321 320 def autopx(self, line=''):
322 321 """Toggles auto parallel mode.
323 322
324 323 Once this is called, all commands typed at the command line are send to
325 324 the engines to be executed in parallel. To control which engine are
326 325 used, the ``targets`` attribute of the view before
327 326 entering ``%autopx`` mode.
328 327
329 328
330 329 Then you can do the following::
331 330
332 331 In [25]: %autopx
333 332 %autopx to enabled
334 333
335 334 In [26]: a = 10
336 335 Parallel execution on engine(s): [0,1,2,3]
337 336 In [27]: print a
338 337 Parallel execution on engine(s): [0,1,2,3]
339 338 [stdout:0] 10
340 339 [stdout:1] 10
341 340 [stdout:2] 10
342 341 [stdout:3] 10
343 342
344 343
345 344 In [27]: %autopx
346 345 %autopx disabled
347 346 """
348 347 if self._autopx:
349 348 self._disable_autopx()
350 349 else:
351 350 self._enable_autopx()
352 351
353 352 def _enable_autopx(self):
354 353 """Enable %autopx mode by saving the original run_cell and installing
355 354 pxrun_cell.
356 355 """
357 356 # override run_cell
358 357 self._original_run_cell = self.shell.run_cell
359 358 self.shell.run_cell = self.pxrun_cell
360 359
361 360 self._autopx = True
362 361 print("%autopx enabled")
363 362
364 363 def _disable_autopx(self):
365 364 """Disable %autopx by restoring the original InteractiveShell.run_cell.
366 365 """
367 366 if self._autopx:
368 367 self.shell.run_cell = self._original_run_cell
369 368 self._autopx = False
370 369 print("%autopx disabled")
371 370
372 371 def pxrun_cell(self, raw_cell, store_history=False, silent=False):
373 372 """drop-in replacement for InteractiveShell.run_cell.
374 373
375 374 This executes code remotely, instead of in the local namespace.
376 375
377 376 See InteractiveShell.run_cell for details.
378 377 """
379 378
380 379 if (not raw_cell) or raw_cell.isspace():
381 380 return
382 381
383 382 ipself = self.shell
384 383
385 384 with ipself.builtin_trap:
386 385 cell = ipself.prefilter_manager.prefilter_lines(raw_cell)
387 386
388 387 # Store raw and processed history
389 388 if store_history:
390 389 ipself.history_manager.store_inputs(ipself.execution_count,
391 390 cell, raw_cell)
392 391
393 392 # ipself.logger.log(cell, raw_cell)
394 393
395 394 cell_name = ipself.compile.cache(cell, ipself.execution_count)
396 395
397 396 try:
398 397 ast.parse(cell, filename=cell_name)
399 398 except (OverflowError, SyntaxError, ValueError, TypeError,
400 399 MemoryError):
401 400 # Case 1
402 401 ipself.showsyntaxerror()
403 402 ipself.execution_count += 1
404 403 return None
405 404 except NameError:
406 405 # ignore name errors, because we don't know the remote keys
407 406 pass
408 407
409 408 if store_history:
410 409 # Write output to the database. Does nothing unless
411 410 # history output logging is enabled.
412 411 ipself.history_manager.store_output(ipself.execution_count)
413 412 # Each cell is a *single* input, regardless of how many lines it has
414 413 ipself.execution_count += 1
415 414 if re.search(r'get_ipython\(\)\.magic\(u?["\']%?autopx', cell):
416 415 self._disable_autopx()
417 416 return False
418 417 else:
419 418 try:
420 419 result = self.view.execute(cell, silent=False, block=False)
421 420 except:
422 421 ipself.showtraceback()
423 422 return True
424 423 else:
425 424 if self.view.block:
426 425 try:
427 426 result.get()
428 427 except:
429 428 self.shell.showtraceback()
430 429 return True
431 430 else:
432 431 with ipself.builtin_trap:
433 432 result.display_outputs()
434 433 return False
435 434
436 435
437 436 __doc__ = __doc__.format(
438 AUTOPX_DOC = ' '*8 + ParallelMagics.autopx.__doc__,
439 PX_DOC = ' '*8 + ParallelMagics.px.__doc__,
440 RESULT_DOC = ' '*8 + ParallelMagics.result.__doc__,
441 CONFIG_DOC = ' '*8 + ParallelMagics.pxconfig.__doc__,
437 AUTOPX_DOC = dedent(ParallelMagics.autopx.__doc__),
438 PX_DOC = dedent(ParallelMagics.px.__doc__),
439 RESULT_DOC = dedent(ParallelMagics.result.__doc__),
440 CONFIG_DOC = dedent(ParallelMagics.pxconfig.__doc__),
442 441 )
General Comments 0
You need to be logged in to leave comments. Login now