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