##// END OF EJS Templates
removed the message about h5py dependence from octavemagic.py
Cavendish McKay -
Show More
@@ -1,364 +1,364 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 in turn depends
11 The ``oct2py`` module needs to be installed separately and
12 on ``h5py``. Both 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 ``%octave``
17 ``%octave``
18
18
19 {OCTAVE_DOC}
19 {OCTAVE_DOC}
20
20
21 ``%octave_push``
21 ``%octave_push``
22
22
23 {OCTAVE_PUSH_DOC}
23 {OCTAVE_PUSH_DOC}
24
24
25 ``%octave_pull``
25 ``%octave_pull``
26
26
27 {OCTAVE_PULL_DOC}
27 {OCTAVE_PULL_DOC}
28
28
29 """
29 """
30
30
31 #-----------------------------------------------------------------------------
31 #-----------------------------------------------------------------------------
32 # Copyright (C) 2012 The IPython Development Team
32 # Copyright (C) 2012 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 import tempfile
38 import tempfile
39 from glob import glob
39 from glob import glob
40 from shutil import rmtree
40 from shutil import rmtree
41
41
42 import numpy as np
42 import numpy as np
43 import oct2py
43 import oct2py
44 from xml.dom import minidom
44 from xml.dom import minidom
45
45
46 from IPython.core.displaypub import publish_display_data
46 from IPython.core.displaypub import publish_display_data
47 from IPython.core.magic import (Magics, magics_class, line_magic,
47 from IPython.core.magic import (Magics, magics_class, line_magic,
48 line_cell_magic)
48 line_cell_magic)
49 from IPython.testing.skipdoctest import skip_doctest
49 from IPython.testing.skipdoctest import skip_doctest
50 from IPython.core.magic_arguments import (
50 from IPython.core.magic_arguments import (
51 argument, magic_arguments, parse_argstring
51 argument, magic_arguments, parse_argstring
52 )
52 )
53 from IPython.utils.py3compat import unicode_to_str
53 from IPython.utils.py3compat import unicode_to_str
54
54
55 class OctaveMagicError(oct2py.Oct2PyError):
55 class OctaveMagicError(oct2py.Oct2PyError):
56 pass
56 pass
57
57
58 _mimetypes = {'png' : 'image/png',
58 _mimetypes = {'png' : 'image/png',
59 'svg' : 'image/svg+xml',
59 'svg' : 'image/svg+xml',
60 'jpg' : 'image/jpeg',
60 'jpg' : 'image/jpeg',
61 'jpeg': 'image/jpeg'}
61 'jpeg': 'image/jpeg'}
62
62
63 @magics_class
63 @magics_class
64 class OctaveMagics(Magics):
64 class OctaveMagics(Magics):
65 """A set of magics useful for interactive work with Octave via oct2py.
65 """A set of magics useful for interactive work with Octave via oct2py.
66 """
66 """
67 def __init__(self, shell):
67 def __init__(self, shell):
68 """
68 """
69 Parameters
69 Parameters
70 ----------
70 ----------
71 shell : IPython shell
71 shell : IPython shell
72
72
73 """
73 """
74 super(OctaveMagics, self).__init__(shell)
74 super(OctaveMagics, self).__init__(shell)
75 self._oct = oct2py.Oct2Py()
75 self._oct = oct2py.Oct2Py()
76 self._plot_format = 'png'
76 self._plot_format = 'png'
77
77
78 # Allow publish_display_data to be overridden for
78 # Allow publish_display_data to be overridden for
79 # testing purposes.
79 # testing purposes.
80 self._publish_display_data = publish_display_data
80 self._publish_display_data = publish_display_data
81
81
82
82
83 def _fix_gnuplot_svg_size(self, image, size=None):
83 def _fix_gnuplot_svg_size(self, image, size=None):
84 """
84 """
85 GnuPlot SVGs do not have height/width attributes. Set
85 GnuPlot SVGs do not have height/width attributes. Set
86 these to be the same as the viewBox, so that the browser
86 these to be the same as the viewBox, so that the browser
87 scales the image correctly.
87 scales the image correctly.
88
88
89 Parameters
89 Parameters
90 ----------
90 ----------
91 image : str
91 image : str
92 SVG data.
92 SVG data.
93 size : tuple of int
93 size : tuple of int
94 Image width, height.
94 Image width, height.
95
95
96 """
96 """
97 (svg,) = minidom.parseString(image).getElementsByTagName('svg')
97 (svg,) = minidom.parseString(image).getElementsByTagName('svg')
98 viewbox = svg.getAttribute('viewBox').split(' ')
98 viewbox = svg.getAttribute('viewBox').split(' ')
99
99
100 if size is not None:
100 if size is not None:
101 width, height = size
101 width, height = size
102 else:
102 else:
103 width, height = viewbox[2:]
103 width, height = viewbox[2:]
104
104
105 svg.setAttribute('width', '%dpx' % width)
105 svg.setAttribute('width', '%dpx' % width)
106 svg.setAttribute('height', '%dpx' % height)
106 svg.setAttribute('height', '%dpx' % height)
107 return svg.toxml()
107 return svg.toxml()
108
108
109
109
110 @skip_doctest
110 @skip_doctest
111 @line_magic
111 @line_magic
112 def octave_push(self, line):
112 def octave_push(self, line):
113 '''
113 '''
114 Line-level magic that pushes a variable to Octave.
114 Line-level magic that pushes a variable to Octave.
115
115
116 `line` should be made up of whitespace separated variable names in the
116 `line` should be made up of whitespace separated variable names in the
117 IPython namespace::
117 IPython namespace::
118
118
119 In [7]: import numpy as np
119 In [7]: import numpy as np
120
120
121 In [8]: X = np.arange(5)
121 In [8]: X = np.arange(5)
122
122
123 In [9]: X.mean()
123 In [9]: X.mean()
124 Out[9]: 2.0
124 Out[9]: 2.0
125
125
126 In [10]: %octave_push X
126 In [10]: %octave_push X
127
127
128 In [11]: %octave mean(X)
128 In [11]: %octave mean(X)
129 Out[11]: 2.0
129 Out[11]: 2.0
130
130
131 '''
131 '''
132 inputs = line.split(' ')
132 inputs = line.split(' ')
133 for input in inputs:
133 for input in inputs:
134 input = unicode_to_str(input)
134 input = unicode_to_str(input)
135 self._oct.put(input, self.shell.user_ns[input])
135 self._oct.put(input, self.shell.user_ns[input])
136
136
137
137
138 @skip_doctest
138 @skip_doctest
139 @line_magic
139 @line_magic
140 def octave_pull(self, line):
140 def octave_pull(self, line):
141 '''
141 '''
142 Line-level magic that pulls a variable from Octave.
142 Line-level magic that pulls a variable from Octave.
143
143
144 In [18]: _ = %octave x = [1 2; 3 4]; y = 'hello'
144 In [18]: _ = %octave x = [1 2; 3 4]; y = 'hello'
145
145
146 In [19]: %octave_pull x y
146 In [19]: %octave_pull x y
147
147
148 In [20]: x
148 In [20]: x
149 Out[20]:
149 Out[20]:
150 array([[ 1., 2.],
150 array([[ 1., 2.],
151 [ 3., 4.]])
151 [ 3., 4.]])
152
152
153 In [21]: y
153 In [21]: y
154 Out[21]: 'hello'
154 Out[21]: 'hello'
155
155
156 '''
156 '''
157 outputs = line.split(' ')
157 outputs = line.split(' ')
158 for output in outputs:
158 for output in outputs:
159 output = unicode_to_str(output)
159 output = unicode_to_str(output)
160 self.shell.push({output: self._oct.get(output)})
160 self.shell.push({output: self._oct.get(output)})
161
161
162
162
163 @skip_doctest
163 @skip_doctest
164 @magic_arguments()
164 @magic_arguments()
165 @argument(
165 @argument(
166 '-i', '--input', action='append',
166 '-i', '--input', action='append',
167 help='Names of input variables to be pushed to Octave. Multiple names '
167 help='Names of input variables to be pushed to Octave. Multiple names '
168 'can be passed, separated by commas with no whitespace.'
168 'can be passed, separated by commas with no whitespace.'
169 )
169 )
170 @argument(
170 @argument(
171 '-o', '--output', action='append',
171 '-o', '--output', action='append',
172 help='Names of variables to be pulled from Octave after executing cell '
172 help='Names of variables to be pulled from Octave after executing cell '
173 'body. Multiple names can be passed, separated by commas with no '
173 'body. Multiple names can be passed, separated by commas with no '
174 'whitespace.'
174 'whitespace.'
175 )
175 )
176 @argument(
176 @argument(
177 '-s', '--size', action='store',
177 '-s', '--size', action='store',
178 help='Pixel size of plots, "width,height". Default is "-s 400,250".'
178 help='Pixel size of plots, "width,height". Default is "-s 400,250".'
179 )
179 )
180 @argument(
180 @argument(
181 '-f', '--format', action='store',
181 '-f', '--format', action='store',
182 help='Plot format (png, svg or jpg).'
182 help='Plot format (png, svg or jpg).'
183 )
183 )
184
184
185 @argument(
185 @argument(
186 'code',
186 'code',
187 nargs='*',
187 nargs='*',
188 )
188 )
189 @line_cell_magic
189 @line_cell_magic
190 def octave(self, line, cell=None):
190 def octave(self, line, cell=None):
191 '''
191 '''
192 Execute code in Octave, and pull some of the results back into the
192 Execute code in Octave, and pull some of the results back into the
193 Python namespace.
193 Python namespace.
194
194
195 In [9]: %octave X = [1 2; 3 4]; mean(X)
195 In [9]: %octave X = [1 2; 3 4]; mean(X)
196 Out[9]: array([[ 2., 3.]])
196 Out[9]: array([[ 2., 3.]])
197
197
198 As a cell, this will run a block of Octave code, without returning any
198 As a cell, this will run a block of Octave code, without returning any
199 value::
199 value::
200
200
201 In [10]: %%octave
201 In [10]: %%octave
202 ....: p = [-2, -1, 0, 1, 2]
202 ....: p = [-2, -1, 0, 1, 2]
203 ....: polyout(p, 'x')
203 ....: polyout(p, 'x')
204
204
205 -2*x^4 - 1*x^3 + 0*x^2 + 1*x^1 + 2
205 -2*x^4 - 1*x^3 + 0*x^2 + 1*x^1 + 2
206
206
207 In the notebook, plots are published as the output of the cell, e.g.
207 In the notebook, plots are published as the output of the cell, e.g.
208
208
209 %octave plot([1 2 3], [4 5 6])
209 %octave plot([1 2 3], [4 5 6])
210
210
211 will create a line plot.
211 will create a line plot.
212
212
213 Objects can be passed back and forth between Octave and IPython via the
213 Objects can be passed back and forth between Octave and IPython via the
214 -i and -o flags in line::
214 -i and -o flags in line::
215
215
216 In [14]: Z = np.array([1, 4, 5, 10])
216 In [14]: Z = np.array([1, 4, 5, 10])
217
217
218 In [15]: %octave -i Z mean(Z)
218 In [15]: %octave -i Z mean(Z)
219 Out[15]: array([ 5.])
219 Out[15]: array([ 5.])
220
220
221
221
222 In [16]: %octave -o W W = Z * mean(Z)
222 In [16]: %octave -o W W = Z * mean(Z)
223 Out[16]: array([ 5., 20., 25., 50.])
223 Out[16]: array([ 5., 20., 25., 50.])
224
224
225 In [17]: W
225 In [17]: W
226 Out[17]: array([ 5., 20., 25., 50.])
226 Out[17]: array([ 5., 20., 25., 50.])
227
227
228 The size and format of output plots can be specified::
228 The size and format of output plots can be specified::
229
229
230 In [18]: %%octave -s 600,800 -f svg
230 In [18]: %%octave -s 600,800 -f svg
231 ...: plot([1, 2, 3]);
231 ...: plot([1, 2, 3]);
232
232
233 '''
233 '''
234 args = parse_argstring(self.octave, line)
234 args = parse_argstring(self.octave, line)
235
235
236 # arguments 'code' in line are prepended to the cell lines
236 # arguments 'code' in line are prepended to the cell lines
237 if cell is None:
237 if cell is None:
238 code = ''
238 code = ''
239 return_output = True
239 return_output = True
240 line_mode = True
240 line_mode = True
241 else:
241 else:
242 code = cell
242 code = cell
243 return_output = False
243 return_output = False
244 line_mode = False
244 line_mode = False
245
245
246 code = ' '.join(args.code) + code
246 code = ' '.join(args.code) + code
247
247
248 if args.input:
248 if args.input:
249 for input in ','.join(args.input).split(','):
249 for input in ','.join(args.input).split(','):
250 input = unicode_to_str(input)
250 input = unicode_to_str(input)
251 self._oct.put(input, self.shell.user_ns[input])
251 self._oct.put(input, self.shell.user_ns[input])
252
252
253 # generate plots in a temporary directory
253 # generate plots in a temporary directory
254 plot_dir = tempfile.mkdtemp()
254 plot_dir = tempfile.mkdtemp()
255 if args.size is not None:
255 if args.size is not None:
256 size = args.size
256 size = args.size
257 else:
257 else:
258 size = '400,240'
258 size = '400,240'
259
259
260 if args.format is not None:
260 if args.format is not None:
261 plot_format = args.format
261 plot_format = args.format
262 else:
262 else:
263 plot_format = 'png'
263 plot_format = 'png'
264
264
265 pre_call = '''
265 pre_call = '''
266 global __ipy_figures = [];
266 global __ipy_figures = [];
267 page_screen_output(0);
267 page_screen_output(0);
268
268
269 function fig_create(src, event)
269 function fig_create(src, event)
270 global __ipy_figures;
270 global __ipy_figures;
271 __ipy_figures(size(__ipy_figures) + 1) = src;
271 __ipy_figures(size(__ipy_figures) + 1) = src;
272 set(src, "visible", "off");
272 set(src, "visible", "off");
273 end
273 end
274
274
275 set(0, 'DefaultFigureCreateFcn', @fig_create);
275 set(0, 'DefaultFigureCreateFcn', @fig_create);
276
276
277 close all;
277 close all;
278 clear ans;
278 clear ans;
279
279
280 # ___<end_pre_call>___ #
280 # ___<end_pre_call>___ #
281 '''
281 '''
282
282
283 post_call = '''
283 post_call = '''
284 # ___<start_post_call>___ #
284 # ___<start_post_call>___ #
285
285
286 # Save output of the last execution
286 # Save output of the last execution
287 if exist("ans") == 1
287 if exist("ans") == 1
288 _ = ans;
288 _ = ans;
289 else
289 else
290 _ = nan;
290 _ = nan;
291 end
291 end
292
292
293 for f = __ipy_figures
293 for f = __ipy_figures
294 outfile = sprintf('%(plot_dir)s/__ipy_oct_fig_%%03d.png', f);
294 outfile = sprintf('%(plot_dir)s/__ipy_oct_fig_%%03d.png', f);
295 try
295 try
296 print(f, outfile, '-d%(plot_format)s', '-tight', '-S%(size)s');
296 print(f, outfile, '-d%(plot_format)s', '-tight', '-S%(size)s');
297 end
297 end
298 end
298 end
299
299
300 ''' % locals()
300 ''' % locals()
301
301
302 code = ' '.join((pre_call, code, post_call))
302 code = ' '.join((pre_call, code, post_call))
303 try:
303 try:
304 text_output = self._oct.run(code, verbose=False)
304 text_output = self._oct.run(code, verbose=False)
305 except (oct2py.Oct2PyError) as exception:
305 except (oct2py.Oct2PyError) as exception:
306 msg = exception.message
306 msg = exception.message
307 msg = msg.split('# ___<end_pre_call>___ #')[1]
307 msg = msg.split('# ___<end_pre_call>___ #')[1]
308 msg = msg.split('# ___<start_post_call>___ #')[0]
308 msg = msg.split('# ___<start_post_call>___ #')[0]
309 raise OctaveMagicError('Octave could not complete execution. '
309 raise OctaveMagicError('Octave could not complete execution. '
310 'Traceback (currently broken in oct2py): %s'
310 'Traceback (currently broken in oct2py): %s'
311 % msg)
311 % msg)
312
312
313 key = 'OctaveMagic.Octave'
313 key = 'OctaveMagic.Octave'
314 display_data = []
314 display_data = []
315
315
316 # Publish text output
316 # Publish text output
317 if text_output:
317 if text_output:
318 display_data.append((key, {'text/plain': text_output}))
318 display_data.append((key, {'text/plain': text_output}))
319
319
320 # Publish images
320 # Publish images
321 images = [open(imgfile, 'rb').read() for imgfile in \
321 images = [open(imgfile, 'rb').read() for imgfile in \
322 glob("%s/*" % plot_dir)]
322 glob("%s/*" % plot_dir)]
323 rmtree(plot_dir)
323 rmtree(plot_dir)
324
324
325 plot_mime_type = _mimetypes.get(plot_format, 'image/png')
325 plot_mime_type = _mimetypes.get(plot_format, 'image/png')
326 width, height = [int(s) for s in size.split(',')]
326 width, height = [int(s) for s in size.split(',')]
327 for image in images:
327 for image in images:
328 if plot_format == 'svg':
328 if plot_format == 'svg':
329 image = self._fix_gnuplot_svg_size(image, size=(width, height))
329 image = self._fix_gnuplot_svg_size(image, size=(width, height))
330 display_data.append((key, {plot_mime_type: image}))
330 display_data.append((key, {plot_mime_type: image}))
331
331
332 if args.output:
332 if args.output:
333 for output in ','.join(args.output).split(','):
333 for output in ','.join(args.output).split(','):
334 output = unicode_to_str(output)
334 output = unicode_to_str(output)
335 self.shell.push({output: self._oct.get(output)})
335 self.shell.push({output: self._oct.get(output)})
336
336
337 for source, data in display_data:
337 for source, data in display_data:
338 self._publish_display_data(source, data)
338 self._publish_display_data(source, data)
339
339
340 if return_output:
340 if return_output:
341 ans = self._oct.get('_')
341 ans = self._oct.get('_')
342
342
343 # Unfortunately, Octave doesn't have a "None" object,
343 # Unfortunately, Octave doesn't have a "None" object,
344 # so we can't return any NaN outputs
344 # so we can't return any NaN outputs
345 if np.isscalar(ans) and np.isnan(ans):
345 if np.isscalar(ans) and np.isnan(ans):
346 ans = None
346 ans = None
347
347
348 return ans
348 return ans
349
349
350
350
351 __doc__ = __doc__.format(
351 __doc__ = __doc__.format(
352 OCTAVE_DOC = ' '*8 + OctaveMagics.octave.__doc__,
352 OCTAVE_DOC = ' '*8 + OctaveMagics.octave.__doc__,
353 OCTAVE_PUSH_DOC = ' '*8 + OctaveMagics.octave_push.__doc__,
353 OCTAVE_PUSH_DOC = ' '*8 + OctaveMagics.octave_push.__doc__,
354 OCTAVE_PULL_DOC = ' '*8 + OctaveMagics.octave_pull.__doc__
354 OCTAVE_PULL_DOC = ' '*8 + OctaveMagics.octave_pull.__doc__
355 )
355 )
356
356
357
357
358 _loaded = False
358 _loaded = False
359 def load_ipython_extension(ip):
359 def load_ipython_extension(ip):
360 """Load the extension in IPython."""
360 """Load the extension in IPython."""
361 global _loaded
361 global _loaded
362 if not _loaded:
362 if not _loaded:
363 ip.register_magics(OctaveMagics)
363 ip.register_magics(OctaveMagics)
364 _loaded = True
364 _loaded = True
General Comments 0
You need to be logged in to leave comments. Login now