##// END OF EJS Templates
Extensions no longer responsible for checking if they are already loaded
Thomas Kluyver -
Show More
@@ -1,522 +1,521 b''
1 """IPython extension to reload modules before executing user code.
1 """IPython extension to reload modules before executing user code.
2
2
3 ``autoreload`` reloads modules automatically before entering the execution of
3 ``autoreload`` reloads modules automatically before entering the execution of
4 code typed at the IPython prompt.
4 code typed at the IPython prompt.
5
5
6 This makes for example the following workflow possible:
6 This makes for example the following workflow possible:
7
7
8 .. sourcecode:: ipython
8 .. sourcecode:: ipython
9
9
10 In [1]: %load_ext autoreload
10 In [1]: %load_ext autoreload
11
11
12 In [2]: %autoreload 2
12 In [2]: %autoreload 2
13
13
14 In [3]: from foo import some_function
14 In [3]: from foo import some_function
15
15
16 In [4]: some_function()
16 In [4]: some_function()
17 Out[4]: 42
17 Out[4]: 42
18
18
19 In [5]: # open foo.py in an editor and change some_function to return 43
19 In [5]: # open foo.py in an editor and change some_function to return 43
20
20
21 In [6]: some_function()
21 In [6]: some_function()
22 Out[6]: 43
22 Out[6]: 43
23
23
24 The module was reloaded without reloading it explicitly, and the object
24 The module was reloaded without reloading it explicitly, and the object
25 imported with ``from foo import ...`` was also updated.
25 imported with ``from foo import ...`` was also updated.
26
26
27 Usage
27 Usage
28 =====
28 =====
29
29
30 The following magic commands are provided:
30 The following magic commands are provided:
31
31
32 ``%autoreload``
32 ``%autoreload``
33
33
34 Reload all modules (except those excluded by ``%aimport``)
34 Reload all modules (except those excluded by ``%aimport``)
35 automatically now.
35 automatically now.
36
36
37 ``%autoreload 0``
37 ``%autoreload 0``
38
38
39 Disable automatic reloading.
39 Disable automatic reloading.
40
40
41 ``%autoreload 1``
41 ``%autoreload 1``
42
42
43 Reload all modules imported with ``%aimport`` every time before
43 Reload all modules imported with ``%aimport`` every time before
44 executing the Python code typed.
44 executing the Python code typed.
45
45
46 ``%autoreload 2``
46 ``%autoreload 2``
47
47
48 Reload all modules (except those excluded by ``%aimport``) every
48 Reload all modules (except those excluded by ``%aimport``) every
49 time before executing the Python code typed.
49 time before executing the Python code typed.
50
50
51 ``%aimport``
51 ``%aimport``
52
52
53 List modules which are to be automatically imported or not to be imported.
53 List modules which are to be automatically imported or not to be imported.
54
54
55 ``%aimport foo``
55 ``%aimport foo``
56
56
57 Import module 'foo' and mark it to be autoreloaded for ``%autoreload 1``
57 Import module 'foo' and mark it to be autoreloaded for ``%autoreload 1``
58
58
59 ``%aimport -foo``
59 ``%aimport -foo``
60
60
61 Mark module 'foo' to not be autoreloaded.
61 Mark module 'foo' to not be autoreloaded.
62
62
63 Caveats
63 Caveats
64 =======
64 =======
65
65
66 Reloading Python modules in a reliable way is in general difficult,
66 Reloading Python modules in a reliable way is in general difficult,
67 and unexpected things may occur. ``%autoreload`` tries to work around
67 and unexpected things may occur. ``%autoreload`` tries to work around
68 common pitfalls by replacing function code objects and parts of
68 common pitfalls by replacing function code objects and parts of
69 classes previously in the module with new versions. This makes the
69 classes previously in the module with new versions. This makes the
70 following things to work:
70 following things to work:
71
71
72 - Functions and classes imported via 'from xxx import foo' are upgraded
72 - Functions and classes imported via 'from xxx import foo' are upgraded
73 to new versions when 'xxx' is reloaded.
73 to new versions when 'xxx' is reloaded.
74
74
75 - Methods and properties of classes are upgraded on reload, so that
75 - Methods and properties of classes are upgraded on reload, so that
76 calling 'c.foo()' on an object 'c' created before the reload causes
76 calling 'c.foo()' on an object 'c' created before the reload causes
77 the new code for 'foo' to be executed.
77 the new code for 'foo' to be executed.
78
78
79 Some of the known remaining caveats are:
79 Some of the known remaining caveats are:
80
80
81 - Replacing code objects does not always succeed: changing a @property
81 - Replacing code objects does not always succeed: changing a @property
82 in a class to an ordinary method or a method to a member variable
82 in a class to an ordinary method or a method to a member variable
83 can cause problems (but in old objects only).
83 can cause problems (but in old objects only).
84
84
85 - Functions that are removed (eg. via monkey-patching) from a module
85 - Functions that are removed (eg. via monkey-patching) from a module
86 before it is reloaded are not upgraded.
86 before it is reloaded are not upgraded.
87
87
88 - C extension modules cannot be reloaded, and so cannot be autoreloaded.
88 - C extension modules cannot be reloaded, and so cannot be autoreloaded.
89 """
89 """
90 from __future__ import print_function
90 from __future__ import print_function
91
91
92 skip_doctest = True
92 skip_doctest = True
93
93
94 #-----------------------------------------------------------------------------
94 #-----------------------------------------------------------------------------
95 # Copyright (C) 2000 Thomas Heller
95 # Copyright (C) 2000 Thomas Heller
96 # Copyright (C) 2008 Pauli Virtanen <pav@iki.fi>
96 # Copyright (C) 2008 Pauli Virtanen <pav@iki.fi>
97 # Copyright (C) 2012 The IPython Development Team
97 # Copyright (C) 2012 The IPython Development Team
98 #
98 #
99 # Distributed under the terms of the BSD License. The full license is in
99 # Distributed under the terms of the BSD License. The full license is in
100 # the file COPYING, distributed as part of this software.
100 # the file COPYING, distributed as part of this software.
101 #-----------------------------------------------------------------------------
101 #-----------------------------------------------------------------------------
102 #
102 #
103 # This IPython module is written by Pauli Virtanen, based on the autoreload
103 # This IPython module is written by Pauli Virtanen, based on the autoreload
104 # code by Thomas Heller.
104 # code by Thomas Heller.
105
105
106 #-----------------------------------------------------------------------------
106 #-----------------------------------------------------------------------------
107 # Imports
107 # Imports
108 #-----------------------------------------------------------------------------
108 #-----------------------------------------------------------------------------
109
109
110 import imp
110 import imp
111 import os
111 import os
112 import sys
112 import sys
113 import traceback
113 import traceback
114 import types
114 import types
115 import weakref
115 import weakref
116
116
117 try:
117 try:
118 # Reload is not defined by default in Python3.
118 # Reload is not defined by default in Python3.
119 reload
119 reload
120 except NameError:
120 except NameError:
121 from imp import reload
121 from imp import reload
122
122
123 from IPython.utils import pyfile
123 from IPython.utils import pyfile
124 from IPython.utils.py3compat import PY3
124 from IPython.utils.py3compat import PY3
125
125
126 #------------------------------------------------------------------------------
126 #------------------------------------------------------------------------------
127 # Autoreload functionality
127 # Autoreload functionality
128 #------------------------------------------------------------------------------
128 #------------------------------------------------------------------------------
129
129
130 def _get_compiled_ext():
130 def _get_compiled_ext():
131 """Official way to get the extension of compiled files (.pyc or .pyo)"""
131 """Official way to get the extension of compiled files (.pyc or .pyo)"""
132 for ext, mode, typ in imp.get_suffixes():
132 for ext, mode, typ in imp.get_suffixes():
133 if typ == imp.PY_COMPILED:
133 if typ == imp.PY_COMPILED:
134 return ext
134 return ext
135
135
136
136
137 PY_COMPILED_EXT = _get_compiled_ext()
137 PY_COMPILED_EXT = _get_compiled_ext()
138
138
139
139
140 class ModuleReloader(object):
140 class ModuleReloader(object):
141 enabled = False
141 enabled = False
142 """Whether this reloader is enabled"""
142 """Whether this reloader is enabled"""
143
143
144 failed = {}
144 failed = {}
145 """Modules that failed to reload: {module: mtime-on-failed-reload, ...}"""
145 """Modules that failed to reload: {module: mtime-on-failed-reload, ...}"""
146
146
147 modules = {}
147 modules = {}
148 """Modules specially marked as autoreloadable."""
148 """Modules specially marked as autoreloadable."""
149
149
150 skip_modules = {}
150 skip_modules = {}
151 """Modules specially marked as not autoreloadable."""
151 """Modules specially marked as not autoreloadable."""
152
152
153 check_all = True
153 check_all = True
154 """Autoreload all modules, not just those listed in 'modules'"""
154 """Autoreload all modules, not just those listed in 'modules'"""
155
155
156 old_objects = {}
156 old_objects = {}
157 """(module-name, name) -> weakref, for replacing old code objects"""
157 """(module-name, name) -> weakref, for replacing old code objects"""
158
158
159 def mark_module_skipped(self, module_name):
159 def mark_module_skipped(self, module_name):
160 """Skip reloading the named module in the future"""
160 """Skip reloading the named module in the future"""
161 try:
161 try:
162 del self.modules[module_name]
162 del self.modules[module_name]
163 except KeyError:
163 except KeyError:
164 pass
164 pass
165 self.skip_modules[module_name] = True
165 self.skip_modules[module_name] = True
166
166
167 def mark_module_reloadable(self, module_name):
167 def mark_module_reloadable(self, module_name):
168 """Reload the named module in the future (if it is imported)"""
168 """Reload the named module in the future (if it is imported)"""
169 try:
169 try:
170 del self.skip_modules[module_name]
170 del self.skip_modules[module_name]
171 except KeyError:
171 except KeyError:
172 pass
172 pass
173 self.modules[module_name] = True
173 self.modules[module_name] = True
174
174
175 def aimport_module(self, module_name):
175 def aimport_module(self, module_name):
176 """Import a module, and mark it reloadable
176 """Import a module, and mark it reloadable
177
177
178 Returns
178 Returns
179 -------
179 -------
180 top_module : module
180 top_module : module
181 The imported module if it is top-level, or the top-level
181 The imported module if it is top-level, or the top-level
182 top_name : module
182 top_name : module
183 Name of top_module
183 Name of top_module
184
184
185 """
185 """
186 self.mark_module_reloadable(module_name)
186 self.mark_module_reloadable(module_name)
187
187
188 __import__(module_name)
188 __import__(module_name)
189 top_name = module_name.split('.')[0]
189 top_name = module_name.split('.')[0]
190 top_module = sys.modules[top_name]
190 top_module = sys.modules[top_name]
191 return top_module, top_name
191 return top_module, top_name
192
192
193 def check(self, check_all=False):
193 def check(self, check_all=False):
194 """Check whether some modules need to be reloaded."""
194 """Check whether some modules need to be reloaded."""
195
195
196 if not self.enabled and not check_all:
196 if not self.enabled and not check_all:
197 return
197 return
198
198
199 if check_all or self.check_all:
199 if check_all or self.check_all:
200 modules = sys.modules.keys()
200 modules = sys.modules.keys()
201 else:
201 else:
202 modules = self.modules.keys()
202 modules = self.modules.keys()
203
203
204 for modname in modules:
204 for modname in modules:
205 m = sys.modules.get(modname, None)
205 m = sys.modules.get(modname, None)
206
206
207 if modname in self.skip_modules:
207 if modname in self.skip_modules:
208 continue
208 continue
209
209
210 if not hasattr(m, '__file__'):
210 if not hasattr(m, '__file__'):
211 continue
211 continue
212
212
213 if m.__name__ == '__main__':
213 if m.__name__ == '__main__':
214 # we cannot reload(__main__)
214 # we cannot reload(__main__)
215 continue
215 continue
216
216
217 filename = m.__file__
217 filename = m.__file__
218 path, ext = os.path.splitext(filename)
218 path, ext = os.path.splitext(filename)
219
219
220 if ext.lower() == '.py':
220 if ext.lower() == '.py':
221 ext = PY_COMPILED_EXT
221 ext = PY_COMPILED_EXT
222 pyc_filename = pyfile.cache_from_source(filename)
222 pyc_filename = pyfile.cache_from_source(filename)
223 py_filename = filename
223 py_filename = filename
224 else:
224 else:
225 pyc_filename = filename
225 pyc_filename = filename
226 try:
226 try:
227 py_filename = pyfile.source_from_cache(filename)
227 py_filename = pyfile.source_from_cache(filename)
228 except ValueError:
228 except ValueError:
229 continue
229 continue
230
230
231 try:
231 try:
232 pymtime = os.stat(py_filename).st_mtime
232 pymtime = os.stat(py_filename).st_mtime
233 if pymtime <= os.stat(pyc_filename).st_mtime:
233 if pymtime <= os.stat(pyc_filename).st_mtime:
234 continue
234 continue
235 if self.failed.get(py_filename, None) == pymtime:
235 if self.failed.get(py_filename, None) == pymtime:
236 continue
236 continue
237 except OSError:
237 except OSError:
238 continue
238 continue
239
239
240 try:
240 try:
241 superreload(m, reload, self.old_objects)
241 superreload(m, reload, self.old_objects)
242 if py_filename in self.failed:
242 if py_filename in self.failed:
243 del self.failed[py_filename]
243 del self.failed[py_filename]
244 except:
244 except:
245 print("[autoreload of %s failed: %s]" % (
245 print("[autoreload of %s failed: %s]" % (
246 modname, traceback.format_exc(1)), file=sys.stderr)
246 modname, traceback.format_exc(1)), file=sys.stderr)
247 self.failed[py_filename] = pymtime
247 self.failed[py_filename] = pymtime
248
248
249 #------------------------------------------------------------------------------
249 #------------------------------------------------------------------------------
250 # superreload
250 # superreload
251 #------------------------------------------------------------------------------
251 #------------------------------------------------------------------------------
252
252
253 if PY3:
253 if PY3:
254 func_attrs = ['__code__', '__defaults__', '__doc__',
254 func_attrs = ['__code__', '__defaults__', '__doc__',
255 '__closure__', '__globals__', '__dict__']
255 '__closure__', '__globals__', '__dict__']
256 else:
256 else:
257 func_attrs = ['func_code', 'func_defaults', 'func_doc',
257 func_attrs = ['func_code', 'func_defaults', 'func_doc',
258 'func_closure', 'func_globals', 'func_dict']
258 'func_closure', 'func_globals', 'func_dict']
259
259
260
260
261 def update_function(old, new):
261 def update_function(old, new):
262 """Upgrade the code object of a function"""
262 """Upgrade the code object of a function"""
263 for name in func_attrs:
263 for name in func_attrs:
264 try:
264 try:
265 setattr(old, name, getattr(new, name))
265 setattr(old, name, getattr(new, name))
266 except (AttributeError, TypeError):
266 except (AttributeError, TypeError):
267 pass
267 pass
268
268
269
269
270 def update_class(old, new):
270 def update_class(old, new):
271 """Replace stuff in the __dict__ of a class, and upgrade
271 """Replace stuff in the __dict__ of a class, and upgrade
272 method code objects"""
272 method code objects"""
273 for key in old.__dict__.keys():
273 for key in old.__dict__.keys():
274 old_obj = getattr(old, key)
274 old_obj = getattr(old, key)
275
275
276 try:
276 try:
277 new_obj = getattr(new, key)
277 new_obj = getattr(new, key)
278 except AttributeError:
278 except AttributeError:
279 # obsolete attribute: remove it
279 # obsolete attribute: remove it
280 try:
280 try:
281 delattr(old, key)
281 delattr(old, key)
282 except (AttributeError, TypeError):
282 except (AttributeError, TypeError):
283 pass
283 pass
284 continue
284 continue
285
285
286 if update_generic(old_obj, new_obj): continue
286 if update_generic(old_obj, new_obj): continue
287
287
288 try:
288 try:
289 setattr(old, key, getattr(new, key))
289 setattr(old, key, getattr(new, key))
290 except (AttributeError, TypeError):
290 except (AttributeError, TypeError):
291 pass # skip non-writable attributes
291 pass # skip non-writable attributes
292
292
293
293
294 def update_property(old, new):
294 def update_property(old, new):
295 """Replace get/set/del functions of a property"""
295 """Replace get/set/del functions of a property"""
296 update_generic(old.fdel, new.fdel)
296 update_generic(old.fdel, new.fdel)
297 update_generic(old.fget, new.fget)
297 update_generic(old.fget, new.fget)
298 update_generic(old.fset, new.fset)
298 update_generic(old.fset, new.fset)
299
299
300
300
301 def isinstance2(a, b, typ):
301 def isinstance2(a, b, typ):
302 return isinstance(a, typ) and isinstance(b, typ)
302 return isinstance(a, typ) and isinstance(b, typ)
303
303
304
304
305 UPDATE_RULES = [
305 UPDATE_RULES = [
306 (lambda a, b: isinstance2(a, b, type),
306 (lambda a, b: isinstance2(a, b, type),
307 update_class),
307 update_class),
308 (lambda a, b: isinstance2(a, b, types.FunctionType),
308 (lambda a, b: isinstance2(a, b, types.FunctionType),
309 update_function),
309 update_function),
310 (lambda a, b: isinstance2(a, b, property),
310 (lambda a, b: isinstance2(a, b, property),
311 update_property),
311 update_property),
312 ]
312 ]
313
313
314
314
315 if PY3:
315 if PY3:
316 UPDATE_RULES.extend([(lambda a, b: isinstance2(a, b, types.MethodType),
316 UPDATE_RULES.extend([(lambda a, b: isinstance2(a, b, types.MethodType),
317 lambda a, b: update_function(a.__func__, b.__func__)),
317 lambda a, b: update_function(a.__func__, b.__func__)),
318 ])
318 ])
319 else:
319 else:
320 UPDATE_RULES.extend([(lambda a, b: isinstance2(a, b, types.ClassType),
320 UPDATE_RULES.extend([(lambda a, b: isinstance2(a, b, types.ClassType),
321 update_class),
321 update_class),
322 (lambda a, b: isinstance2(a, b, types.MethodType),
322 (lambda a, b: isinstance2(a, b, types.MethodType),
323 lambda a, b: update_function(a.im_func, b.im_func)),
323 lambda a, b: update_function(a.im_func, b.im_func)),
324 ])
324 ])
325
325
326
326
327 def update_generic(a, b):
327 def update_generic(a, b):
328 for type_check, update in UPDATE_RULES:
328 for type_check, update in UPDATE_RULES:
329 if type_check(a, b):
329 if type_check(a, b):
330 update(a, b)
330 update(a, b)
331 return True
331 return True
332 return False
332 return False
333
333
334
334
335 class StrongRef(object):
335 class StrongRef(object):
336 def __init__(self, obj):
336 def __init__(self, obj):
337 self.obj = obj
337 self.obj = obj
338 def __call__(self):
338 def __call__(self):
339 return self.obj
339 return self.obj
340
340
341
341
342 def superreload(module, reload=reload, old_objects={}):
342 def superreload(module, reload=reload, old_objects={}):
343 """Enhanced version of the builtin reload function.
343 """Enhanced version of the builtin reload function.
344
344
345 superreload remembers objects previously in the module, and
345 superreload remembers objects previously in the module, and
346
346
347 - upgrades the class dictionary of every old class in the module
347 - upgrades the class dictionary of every old class in the module
348 - upgrades the code object of every old function and method
348 - upgrades the code object of every old function and method
349 - clears the module's namespace before reloading
349 - clears the module's namespace before reloading
350
350
351 """
351 """
352
352
353 # collect old objects in the module
353 # collect old objects in the module
354 for name, obj in module.__dict__.items():
354 for name, obj in module.__dict__.items():
355 if not hasattr(obj, '__module__') or obj.__module__ != module.__name__:
355 if not hasattr(obj, '__module__') or obj.__module__ != module.__name__:
356 continue
356 continue
357 key = (module.__name__, name)
357 key = (module.__name__, name)
358 try:
358 try:
359 old_objects.setdefault(key, []).append(weakref.ref(obj))
359 old_objects.setdefault(key, []).append(weakref.ref(obj))
360 except TypeError:
360 except TypeError:
361 # weakref doesn't work for all types;
361 # weakref doesn't work for all types;
362 # create strong references for 'important' cases
362 # create strong references for 'important' cases
363 if not PY3 and isinstance(obj, types.ClassType):
363 if not PY3 and isinstance(obj, types.ClassType):
364 old_objects.setdefault(key, []).append(StrongRef(obj))
364 old_objects.setdefault(key, []).append(StrongRef(obj))
365
365
366 # reload module
366 # reload module
367 try:
367 try:
368 # clear namespace first from old cruft
368 # clear namespace first from old cruft
369 old_dict = module.__dict__.copy()
369 old_dict = module.__dict__.copy()
370 old_name = module.__name__
370 old_name = module.__name__
371 module.__dict__.clear()
371 module.__dict__.clear()
372 module.__dict__['__name__'] = old_name
372 module.__dict__['__name__'] = old_name
373 module.__dict__['__loader__'] = old_dict['__loader__']
373 module.__dict__['__loader__'] = old_dict['__loader__']
374 except (TypeError, AttributeError, KeyError):
374 except (TypeError, AttributeError, KeyError):
375 pass
375 pass
376
376
377 try:
377 try:
378 module = reload(module)
378 module = reload(module)
379 except:
379 except:
380 # restore module dictionary on failed reload
380 # restore module dictionary on failed reload
381 module.__dict__.update(old_dict)
381 module.__dict__.update(old_dict)
382 raise
382 raise
383
383
384 # iterate over all objects and update functions & classes
384 # iterate over all objects and update functions & classes
385 for name, new_obj in module.__dict__.items():
385 for name, new_obj in module.__dict__.items():
386 key = (module.__name__, name)
386 key = (module.__name__, name)
387 if key not in old_objects: continue
387 if key not in old_objects: continue
388
388
389 new_refs = []
389 new_refs = []
390 for old_ref in old_objects[key]:
390 for old_ref in old_objects[key]:
391 old_obj = old_ref()
391 old_obj = old_ref()
392 if old_obj is None: continue
392 if old_obj is None: continue
393 new_refs.append(old_ref)
393 new_refs.append(old_ref)
394 update_generic(old_obj, new_obj)
394 update_generic(old_obj, new_obj)
395
395
396 if new_refs:
396 if new_refs:
397 old_objects[key] = new_refs
397 old_objects[key] = new_refs
398 else:
398 else:
399 del old_objects[key]
399 del old_objects[key]
400
400
401 return module
401 return module
402
402
403 #------------------------------------------------------------------------------
403 #------------------------------------------------------------------------------
404 # IPython connectivity
404 # IPython connectivity
405 #------------------------------------------------------------------------------
405 #------------------------------------------------------------------------------
406
406
407 from IPython.core.hooks import TryNext
407 from IPython.core.hooks import TryNext
408 from IPython.core.magic import Magics, magics_class, line_magic
408 from IPython.core.magic import Magics, magics_class, line_magic
409
409
410 @magics_class
410 @magics_class
411 class AutoreloadMagics(Magics):
411 class AutoreloadMagics(Magics):
412 def __init__(self, *a, **kw):
412 def __init__(self, *a, **kw):
413 super(AutoreloadMagics, self).__init__(*a, **kw)
413 super(AutoreloadMagics, self).__init__(*a, **kw)
414 self._reloader = ModuleReloader()
414 self._reloader = ModuleReloader()
415 self._reloader.check_all = False
415 self._reloader.check_all = False
416
416
417 @line_magic
417 @line_magic
418 def autoreload(self, parameter_s=''):
418 def autoreload(self, parameter_s=''):
419 r"""%autoreload => Reload modules automatically
419 r"""%autoreload => Reload modules automatically
420
420
421 %autoreload
421 %autoreload
422 Reload all modules (except those excluded by %aimport) automatically
422 Reload all modules (except those excluded by %aimport) automatically
423 now.
423 now.
424
424
425 %autoreload 0
425 %autoreload 0
426 Disable automatic reloading.
426 Disable automatic reloading.
427
427
428 %autoreload 1
428 %autoreload 1
429 Reload all modules imported with %aimport every time before executing
429 Reload all modules imported with %aimport every time before executing
430 the Python code typed.
430 the Python code typed.
431
431
432 %autoreload 2
432 %autoreload 2
433 Reload all modules (except those excluded by %aimport) every time
433 Reload all modules (except those excluded by %aimport) every time
434 before executing the Python code typed.
434 before executing the Python code typed.
435
435
436 Reloading Python modules in a reliable way is in general
436 Reloading Python modules in a reliable way is in general
437 difficult, and unexpected things may occur. %autoreload tries to
437 difficult, and unexpected things may occur. %autoreload tries to
438 work around common pitfalls by replacing function code objects and
438 work around common pitfalls by replacing function code objects and
439 parts of classes previously in the module with new versions. This
439 parts of classes previously in the module with new versions. This
440 makes the following things to work:
440 makes the following things to work:
441
441
442 - Functions and classes imported via 'from xxx import foo' are upgraded
442 - Functions and classes imported via 'from xxx import foo' are upgraded
443 to new versions when 'xxx' is reloaded.
443 to new versions when 'xxx' is reloaded.
444
444
445 - Methods and properties of classes are upgraded on reload, so that
445 - Methods and properties of classes are upgraded on reload, so that
446 calling 'c.foo()' on an object 'c' created before the reload causes
446 calling 'c.foo()' on an object 'c' created before the reload causes
447 the new code for 'foo' to be executed.
447 the new code for 'foo' to be executed.
448
448
449 Some of the known remaining caveats are:
449 Some of the known remaining caveats are:
450
450
451 - Replacing code objects does not always succeed: changing a @property
451 - Replacing code objects does not always succeed: changing a @property
452 in a class to an ordinary method or a method to a member variable
452 in a class to an ordinary method or a method to a member variable
453 can cause problems (but in old objects only).
453 can cause problems (but in old objects only).
454
454
455 - Functions that are removed (eg. via monkey-patching) from a module
455 - Functions that are removed (eg. via monkey-patching) from a module
456 before it is reloaded are not upgraded.
456 before it is reloaded are not upgraded.
457
457
458 - C extension modules cannot be reloaded, and so cannot be
458 - C extension modules cannot be reloaded, and so cannot be
459 autoreloaded.
459 autoreloaded.
460
460
461 """
461 """
462 if parameter_s == '':
462 if parameter_s == '':
463 self._reloader.check(True)
463 self._reloader.check(True)
464 elif parameter_s == '0':
464 elif parameter_s == '0':
465 self._reloader.enabled = False
465 self._reloader.enabled = False
466 elif parameter_s == '1':
466 elif parameter_s == '1':
467 self._reloader.check_all = False
467 self._reloader.check_all = False
468 self._reloader.enabled = True
468 self._reloader.enabled = True
469 elif parameter_s == '2':
469 elif parameter_s == '2':
470 self._reloader.check_all = True
470 self._reloader.check_all = True
471 self._reloader.enabled = True
471 self._reloader.enabled = True
472
472
473 @line_magic
473 @line_magic
474 def aimport(self, parameter_s='', stream=None):
474 def aimport(self, parameter_s='', stream=None):
475 """%aimport => Import modules for automatic reloading.
475 """%aimport => Import modules for automatic reloading.
476
476
477 %aimport
477 %aimport
478 List modules to automatically import and not to import.
478 List modules to automatically import and not to import.
479
479
480 %aimport foo
480 %aimport foo
481 Import module 'foo' and mark it to be autoreloaded for %autoreload 1
481 Import module 'foo' and mark it to be autoreloaded for %autoreload 1
482
482
483 %aimport -foo
483 %aimport -foo
484 Mark module 'foo' to not be autoreloaded for %autoreload 1
484 Mark module 'foo' to not be autoreloaded for %autoreload 1
485 """
485 """
486 modname = parameter_s
486 modname = parameter_s
487 if not modname:
487 if not modname:
488 to_reload = self._reloader.modules.keys()
488 to_reload = self._reloader.modules.keys()
489 to_reload.sort()
489 to_reload.sort()
490 to_skip = self._reloader.skip_modules.keys()
490 to_skip = self._reloader.skip_modules.keys()
491 to_skip.sort()
491 to_skip.sort()
492 if stream is None:
492 if stream is None:
493 stream = sys.stdout
493 stream = sys.stdout
494 if self._reloader.check_all:
494 if self._reloader.check_all:
495 stream.write("Modules to reload:\nall-except-skipped\n")
495 stream.write("Modules to reload:\nall-except-skipped\n")
496 else:
496 else:
497 stream.write("Modules to reload:\n%s\n" % ' '.join(to_reload))
497 stream.write("Modules to reload:\n%s\n" % ' '.join(to_reload))
498 stream.write("\nModules to skip:\n%s\n" % ' '.join(to_skip))
498 stream.write("\nModules to skip:\n%s\n" % ' '.join(to_skip))
499 elif modname.startswith('-'):
499 elif modname.startswith('-'):
500 modname = modname[1:]
500 modname = modname[1:]
501 self._reloader.mark_module_skipped(modname)
501 self._reloader.mark_module_skipped(modname)
502 else:
502 else:
503 top_module, top_name = self._reloader.aimport_module(modname)
503 top_module, top_name = self._reloader.aimport_module(modname)
504
504
505 # Inject module to user namespace
505 # Inject module to user namespace
506 self.shell.push({top_name: top_module})
506 self.shell.push({top_name: top_module})
507
507
508 def pre_run_code_hook(self, ip):
508 def pre_run_code_hook(self, ip):
509 if not self._reloader.enabled:
509 if not self._reloader.enabled:
510 raise TryNext
510 raise TryNext
511 try:
511 try:
512 self._reloader.check()
512 self._reloader.check()
513 except:
513 except:
514 pass
514 pass
515
515
516
516
517 def load_ipython_extension(ip):
517 def load_ipython_extension(ip):
518 """Load the extension in IPython."""
518 """Load the extension in IPython."""
519 if 'autoreload' not in ip.extension_manager.loaded:
519 auto_reload = AutoreloadMagics(ip)
520 auto_reload = AutoreloadMagics(ip)
520 ip.register_magics(auto_reload)
521 ip.register_magics(auto_reload)
521 ip.set_hook('pre_run_code_hook', auto_reload.pre_run_code_hook)
522 ip.set_hook('pre_run_code_hook', auto_reload.pre_run_code_hook)
@@ -1,280 +1,279 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """
2 """
3 Cython related magics.
3 Cython related magics.
4
4
5 Author:
5 Author:
6 * Brian Granger
6 * Brian Granger
7
7
8 Parts of this code were taken from Cython.inline.
8 Parts of this code were taken from Cython.inline.
9 """
9 """
10 #-----------------------------------------------------------------------------
10 #-----------------------------------------------------------------------------
11 # Copyright (C) 2010-2011, IPython Development Team.
11 # Copyright (C) 2010-2011, IPython Development Team.
12 #
12 #
13 # Distributed under the terms of the Modified BSD License.
13 # Distributed under the terms of the Modified BSD License.
14 #
14 #
15 # The full license is in the file COPYING.txt, distributed with this software.
15 # The full license is in the file COPYING.txt, distributed with this software.
16 #-----------------------------------------------------------------------------
16 #-----------------------------------------------------------------------------
17
17
18 from __future__ import print_function
18 from __future__ import print_function
19
19
20 import imp
20 import imp
21 import io
21 import io
22 import os
22 import os
23 import re
23 import re
24 import sys
24 import sys
25 import time
25 import time
26
26
27 try:
27 try:
28 import hashlib
28 import hashlib
29 except ImportError:
29 except ImportError:
30 import md5 as hashlib
30 import md5 as hashlib
31
31
32 from distutils.core import Distribution, Extension
32 from distutils.core import Distribution, Extension
33 from distutils.command.build_ext import build_ext
33 from distutils.command.build_ext import build_ext
34
34
35 from IPython.core import display
35 from IPython.core import display
36 from IPython.core import magic_arguments
36 from IPython.core import magic_arguments
37 from IPython.core.magic import Magics, magics_class, cell_magic
37 from IPython.core.magic import Magics, magics_class, cell_magic
38 from IPython.testing.skipdoctest import skip_doctest
38 from IPython.testing.skipdoctest import skip_doctest
39 from IPython.utils import py3compat
39 from IPython.utils import py3compat
40
40
41 import Cython
41 import Cython
42 from Cython.Compiler.Errors import CompileError
42 from Cython.Compiler.Errors import CompileError
43 from Cython.Build.Dependencies import cythonize
43 from Cython.Build.Dependencies import cythonize
44
44
45
45
46 @magics_class
46 @magics_class
47 class CythonMagics(Magics):
47 class CythonMagics(Magics):
48
48
49 def __init__(self, shell):
49 def __init__(self, shell):
50 super(CythonMagics,self).__init__(shell)
50 super(CythonMagics,self).__init__(shell)
51 self._reloads = {}
51 self._reloads = {}
52 self._code_cache = {}
52 self._code_cache = {}
53
53
54 def _import_all(self, module):
54 def _import_all(self, module):
55 for k,v in module.__dict__.items():
55 for k,v in module.__dict__.items():
56 if not k.startswith('__'):
56 if not k.startswith('__'):
57 self.shell.push({k:v})
57 self.shell.push({k:v})
58
58
59 @cell_magic
59 @cell_magic
60 def cython_inline(self, line, cell):
60 def cython_inline(self, line, cell):
61 """Compile and run a Cython code cell using Cython.inline.
61 """Compile and run a Cython code cell using Cython.inline.
62
62
63 This magic simply passes the body of the cell to Cython.inline
63 This magic simply passes the body of the cell to Cython.inline
64 and returns the result. If the variables `a` and `b` are defined
64 and returns the result. If the variables `a` and `b` are defined
65 in the user's namespace, here is a simple example that returns
65 in the user's namespace, here is a simple example that returns
66 their sum::
66 their sum::
67
67
68 %%cython_inline
68 %%cython_inline
69 return a+b
69 return a+b
70
70
71 For most purposes, we recommend the usage of the `%%cython` magic.
71 For most purposes, we recommend the usage of the `%%cython` magic.
72 """
72 """
73 locs = self.shell.user_global_ns
73 locs = self.shell.user_global_ns
74 globs = self.shell.user_ns
74 globs = self.shell.user_ns
75 return Cython.inline(cell, locals=locs, globals=globs)
75 return Cython.inline(cell, locals=locs, globals=globs)
76
76
77 @cell_magic
77 @cell_magic
78 def cython_pyximport(self, line, cell):
78 def cython_pyximport(self, line, cell):
79 """Compile and import a Cython code cell using pyximport.
79 """Compile and import a Cython code cell using pyximport.
80
80
81 The contents of the cell are written to a `.pyx` file in the current
81 The contents of the cell are written to a `.pyx` file in the current
82 working directory, which is then imported using `pyximport`. This
82 working directory, which is then imported using `pyximport`. This
83 magic requires a module name to be passed::
83 magic requires a module name to be passed::
84
84
85 %%cython_pyximport modulename
85 %%cython_pyximport modulename
86 def f(x):
86 def f(x):
87 return 2.0*x
87 return 2.0*x
88
88
89 The compiled module is then imported and all of its symbols are
89 The compiled module is then imported and all of its symbols are
90 injected into the user's namespace. For most purposes, we recommend
90 injected into the user's namespace. For most purposes, we recommend
91 the usage of the `%%cython` magic.
91 the usage of the `%%cython` magic.
92 """
92 """
93 module_name = line.strip()
93 module_name = line.strip()
94 if not module_name:
94 if not module_name:
95 raise ValueError('module name must be given')
95 raise ValueError('module name must be given')
96 fname = module_name + '.pyx'
96 fname = module_name + '.pyx'
97 with io.open(fname, 'w', encoding='utf-8') as f:
97 with io.open(fname, 'w', encoding='utf-8') as f:
98 f.write(cell)
98 f.write(cell)
99 if 'pyximport' not in sys.modules:
99 if 'pyximport' not in sys.modules:
100 import pyximport
100 import pyximport
101 pyximport.install(reload_support=True)
101 pyximport.install(reload_support=True)
102 if module_name in self._reloads:
102 if module_name in self._reloads:
103 module = self._reloads[module_name]
103 module = self._reloads[module_name]
104 reload(module)
104 reload(module)
105 else:
105 else:
106 __import__(module_name)
106 __import__(module_name)
107 module = sys.modules[module_name]
107 module = sys.modules[module_name]
108 self._reloads[module_name] = module
108 self._reloads[module_name] = module
109 self._import_all(module)
109 self._import_all(module)
110
110
111 @magic_arguments.magic_arguments()
111 @magic_arguments.magic_arguments()
112 @magic_arguments.argument(
112 @magic_arguments.argument(
113 '-c', '--compile-args', action='append', default=[],
113 '-c', '--compile-args', action='append', default=[],
114 help="Extra flags to pass to compiler via the `extra_compile_args` "
114 help="Extra flags to pass to compiler via the `extra_compile_args` "
115 "Extension flag (can be specified multiple times)."
115 "Extension flag (can be specified multiple times)."
116 )
116 )
117 @magic_arguments.argument(
117 @magic_arguments.argument(
118 '-la', '--link-args', action='append', default=[],
118 '-la', '--link-args', action='append', default=[],
119 help="Extra flags to pass to linker via the `extra_link_args` "
119 help="Extra flags to pass to linker via the `extra_link_args` "
120 "Extension flag (can be specified multiple times)."
120 "Extension flag (can be specified multiple times)."
121 )
121 )
122 @magic_arguments.argument(
122 @magic_arguments.argument(
123 '-l', '--lib', action='append', default=[],
123 '-l', '--lib', action='append', default=[],
124 help="Add a library to link the extension against (can be specified "
124 help="Add a library to link the extension against (can be specified "
125 "multiple times)."
125 "multiple times)."
126 )
126 )
127 @magic_arguments.argument(
127 @magic_arguments.argument(
128 '-L', dest='library_dirs', metavar='dir', action='append', default=[],
128 '-L', dest='library_dirs', metavar='dir', action='append', default=[],
129 help="Add a path to the list of libary directories (can be specified "
129 help="Add a path to the list of libary directories (can be specified "
130 "multiple times)."
130 "multiple times)."
131 )
131 )
132 @magic_arguments.argument(
132 @magic_arguments.argument(
133 '-I', '--include', action='append', default=[],
133 '-I', '--include', action='append', default=[],
134 help="Add a path to the list of include directories (can be specified "
134 help="Add a path to the list of include directories (can be specified "
135 "multiple times)."
135 "multiple times)."
136 )
136 )
137 @magic_arguments.argument(
137 @magic_arguments.argument(
138 '-+', '--cplus', action='store_true', default=False,
138 '-+', '--cplus', action='store_true', default=False,
139 help="Output a C++ rather than C file."
139 help="Output a C++ rather than C file."
140 )
140 )
141 @magic_arguments.argument(
141 @magic_arguments.argument(
142 '-f', '--force', action='store_true', default=False,
142 '-f', '--force', action='store_true', default=False,
143 help="Force the compilation of a new module, even if the source has been "
143 help="Force the compilation of a new module, even if the source has been "
144 "previously compiled."
144 "previously compiled."
145 )
145 )
146 @magic_arguments.argument(
146 @magic_arguments.argument(
147 '-a', '--annotate', action='store_true', default=False,
147 '-a', '--annotate', action='store_true', default=False,
148 help="Produce a colorized HTML version of the source."
148 help="Produce a colorized HTML version of the source."
149 )
149 )
150 @cell_magic
150 @cell_magic
151 def cython(self, line, cell):
151 def cython(self, line, cell):
152 """Compile and import everything from a Cython code cell.
152 """Compile and import everything from a Cython code cell.
153
153
154 The contents of the cell are written to a `.pyx` file in the
154 The contents of the cell are written to a `.pyx` file in the
155 directory `IPYTHONDIR/cython` using a filename with the hash of the
155 directory `IPYTHONDIR/cython` using a filename with the hash of the
156 code. This file is then cythonized and compiled. The resulting module
156 code. This file is then cythonized and compiled. The resulting module
157 is imported and all of its symbols are injected into the user's
157 is imported and all of its symbols are injected into the user's
158 namespace. The usage is similar to that of `%%cython_pyximport` but
158 namespace. The usage is similar to that of `%%cython_pyximport` but
159 you don't have to pass a module name::
159 you don't have to pass a module name::
160
160
161 %%cython
161 %%cython
162 def f(x):
162 def f(x):
163 return 2.0*x
163 return 2.0*x
164 """
164 """
165 args = magic_arguments.parse_argstring(self.cython, line)
165 args = magic_arguments.parse_argstring(self.cython, line)
166 code = cell if cell.endswith('\n') else cell+'\n'
166 code = cell if cell.endswith('\n') else cell+'\n'
167 lib_dir = os.path.join(self.shell.ipython_dir, 'cython')
167 lib_dir = os.path.join(self.shell.ipython_dir, 'cython')
168 quiet = True
168 quiet = True
169 key = code, sys.version_info, sys.executable, Cython.__version__
169 key = code, sys.version_info, sys.executable, Cython.__version__
170
170
171 if not os.path.exists(lib_dir):
171 if not os.path.exists(lib_dir):
172 os.makedirs(lib_dir)
172 os.makedirs(lib_dir)
173
173
174 if args.force:
174 if args.force:
175 # Force a new module name by adding the current time to the
175 # Force a new module name by adding the current time to the
176 # key which is hashed to determine the module name.
176 # key which is hashed to determine the module name.
177 key += time.time(),
177 key += time.time(),
178
178
179 module_name = "_cython_magic_" + hashlib.md5(str(key).encode('utf-8')).hexdigest()
179 module_name = "_cython_magic_" + hashlib.md5(str(key).encode('utf-8')).hexdigest()
180 module_path = os.path.join(lib_dir, module_name + self.so_ext)
180 module_path = os.path.join(lib_dir, module_name + self.so_ext)
181
181
182 have_module = os.path.isfile(module_path)
182 have_module = os.path.isfile(module_path)
183 need_cythonize = not have_module
183 need_cythonize = not have_module
184
184
185 if args.annotate:
185 if args.annotate:
186 html_file = os.path.join(lib_dir, module_name + '.html')
186 html_file = os.path.join(lib_dir, module_name + '.html')
187 if not os.path.isfile(html_file):
187 if not os.path.isfile(html_file):
188 need_cythonize = True
188 need_cythonize = True
189
189
190 if need_cythonize:
190 if need_cythonize:
191 c_include_dirs = args.include
191 c_include_dirs = args.include
192 if 'numpy' in code:
192 if 'numpy' in code:
193 import numpy
193 import numpy
194 c_include_dirs.append(numpy.get_include())
194 c_include_dirs.append(numpy.get_include())
195 pyx_file = os.path.join(lib_dir, module_name + '.pyx')
195 pyx_file = os.path.join(lib_dir, module_name + '.pyx')
196 pyx_file = py3compat.cast_bytes_py2(pyx_file, encoding=sys.getfilesystemencoding())
196 pyx_file = py3compat.cast_bytes_py2(pyx_file, encoding=sys.getfilesystemencoding())
197 with io.open(pyx_file, 'w', encoding='utf-8') as f:
197 with io.open(pyx_file, 'w', encoding='utf-8') as f:
198 f.write(code)
198 f.write(code)
199 extension = Extension(
199 extension = Extension(
200 name = module_name,
200 name = module_name,
201 sources = [pyx_file],
201 sources = [pyx_file],
202 include_dirs = c_include_dirs,
202 include_dirs = c_include_dirs,
203 library_dirs = args.library_dirs,
203 library_dirs = args.library_dirs,
204 extra_compile_args = args.compile_args,
204 extra_compile_args = args.compile_args,
205 extra_link_args = args.link_args,
205 extra_link_args = args.link_args,
206 libraries = args.lib,
206 libraries = args.lib,
207 language = 'c++' if args.cplus else 'c',
207 language = 'c++' if args.cplus else 'c',
208 )
208 )
209 build_extension = self._get_build_extension()
209 build_extension = self._get_build_extension()
210 try:
210 try:
211 opts = dict(
211 opts = dict(
212 quiet=quiet,
212 quiet=quiet,
213 annotate = args.annotate,
213 annotate = args.annotate,
214 force = True,
214 force = True,
215 )
215 )
216 build_extension.extensions = cythonize([extension], **opts)
216 build_extension.extensions = cythonize([extension], **opts)
217 except CompileError:
217 except CompileError:
218 return
218 return
219
219
220 if not have_module:
220 if not have_module:
221 build_extension.build_temp = os.path.dirname(pyx_file)
221 build_extension.build_temp = os.path.dirname(pyx_file)
222 build_extension.build_lib = lib_dir
222 build_extension.build_lib = lib_dir
223 build_extension.run()
223 build_extension.run()
224 self._code_cache[key] = module_name
224 self._code_cache[key] = module_name
225
225
226 module = imp.load_dynamic(module_name, module_path)
226 module = imp.load_dynamic(module_name, module_path)
227 self._import_all(module)
227 self._import_all(module)
228
228
229 if args.annotate:
229 if args.annotate:
230 try:
230 try:
231 with io.open(html_file, encoding='utf-8') as f:
231 with io.open(html_file, encoding='utf-8') as f:
232 annotated_html = f.read()
232 annotated_html = f.read()
233 except IOError as e:
233 except IOError as e:
234 # File could not be opened. Most likely the user has a version
234 # File could not be opened. Most likely the user has a version
235 # of Cython before 0.15.1 (when `cythonize` learned the
235 # of Cython before 0.15.1 (when `cythonize` learned the
236 # `force` keyword argument) and has already compiled this
236 # `force` keyword argument) and has already compiled this
237 # exact source without annotation.
237 # exact source without annotation.
238 print('Cython completed successfully but the annotated '
238 print('Cython completed successfully but the annotated '
239 'source could not be read.', file=sys.stderr)
239 'source could not be read.', file=sys.stderr)
240 print(e, file=sys.stderr)
240 print(e, file=sys.stderr)
241 else:
241 else:
242 return display.HTML(self.clean_annotated_html(annotated_html))
242 return display.HTML(self.clean_annotated_html(annotated_html))
243
243
244 @property
244 @property
245 def so_ext(self):
245 def so_ext(self):
246 """The extension suffix for compiled modules."""
246 """The extension suffix for compiled modules."""
247 try:
247 try:
248 return self._so_ext
248 return self._so_ext
249 except AttributeError:
249 except AttributeError:
250 self._so_ext = self._get_build_extension().get_ext_filename('')
250 self._so_ext = self._get_build_extension().get_ext_filename('')
251 return self._so_ext
251 return self._so_ext
252
252
253 def _get_build_extension(self):
253 def _get_build_extension(self):
254 dist = Distribution()
254 dist = Distribution()
255 config_files = dist.find_config_files()
255 config_files = dist.find_config_files()
256 try:
256 try:
257 config_files.remove('setup.cfg')
257 config_files.remove('setup.cfg')
258 except ValueError:
258 except ValueError:
259 pass
259 pass
260 dist.parse_config_files(config_files)
260 dist.parse_config_files(config_files)
261 build_extension = build_ext(dist)
261 build_extension = build_ext(dist)
262 build_extension.finalize_options()
262 build_extension.finalize_options()
263 return build_extension
263 return build_extension
264
264
265 @staticmethod
265 @staticmethod
266 def clean_annotated_html(html):
266 def clean_annotated_html(html):
267 """Clean up the annotated HTML source.
267 """Clean up the annotated HTML source.
268
268
269 Strips the link to the generated C or C++ file, which we do not
269 Strips the link to the generated C or C++ file, which we do not
270 present to the user.
270 present to the user.
271 """
271 """
272 r = re.compile('<p>Raw output: <a href="(.*)">(.*)</a>')
272 r = re.compile('<p>Raw output: <a href="(.*)">(.*)</a>')
273 html = '\n'.join(l for l in html.splitlines() if not r.match(l))
273 html = '\n'.join(l for l in html.splitlines() if not r.match(l))
274 return html
274 return html
275
275
276
276
277 def load_ipython_extension(ip):
277 def load_ipython_extension(ip):
278 """Load the extension in IPython."""
278 """Load the extension in IPython."""
279 if 'cythonmagic' not in ip.extension_manager.loaded:
279 ip.register_magics(CythonMagics)
280 ip.register_magics(CythonMagics)
@@ -1,368 +1,367 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 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, needs_local_scope)
48 line_cell_magic, needs_local_scope)
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 @needs_local_scope
185 @needs_local_scope
186 @argument(
186 @argument(
187 'code',
187 'code',
188 nargs='*',
188 nargs='*',
189 )
189 )
190 @line_cell_magic
190 @line_cell_magic
191 def octave(self, line, cell=None, local_ns=None):
191 def octave(self, line, cell=None, local_ns=None):
192 '''
192 '''
193 Execute code in Octave, and pull some of the results back into the
193 Execute code in Octave, and pull some of the results back into the
194 Python namespace.
194 Python namespace.
195
195
196 In [9]: %octave X = [1 2; 3 4]; mean(X)
196 In [9]: %octave X = [1 2; 3 4]; mean(X)
197 Out[9]: array([[ 2., 3.]])
197 Out[9]: array([[ 2., 3.]])
198
198
199 As a cell, this will run a block of Octave code, without returning any
199 As a cell, this will run a block of Octave code, without returning any
200 value::
200 value::
201
201
202 In [10]: %%octave
202 In [10]: %%octave
203 ....: p = [-2, -1, 0, 1, 2]
203 ....: p = [-2, -1, 0, 1, 2]
204 ....: polyout(p, 'x')
204 ....: polyout(p, 'x')
205
205
206 -2*x^4 - 1*x^3 + 0*x^2 + 1*x^1 + 2
206 -2*x^4 - 1*x^3 + 0*x^2 + 1*x^1 + 2
207
207
208 In the notebook, plots are published as the output of the cell, e.g.
208 In the notebook, plots are published as the output of the cell, e.g.
209
209
210 %octave plot([1 2 3], [4 5 6])
210 %octave plot([1 2 3], [4 5 6])
211
211
212 will create a line plot.
212 will create a line plot.
213
213
214 Objects can be passed back and forth between Octave and IPython via the
214 Objects can be passed back and forth between Octave and IPython via the
215 -i and -o flags in line::
215 -i and -o flags in line::
216
216
217 In [14]: Z = np.array([1, 4, 5, 10])
217 In [14]: Z = np.array([1, 4, 5, 10])
218
218
219 In [15]: %octave -i Z mean(Z)
219 In [15]: %octave -i Z mean(Z)
220 Out[15]: array([ 5.])
220 Out[15]: array([ 5.])
221
221
222
222
223 In [16]: %octave -o W W = Z * mean(Z)
223 In [16]: %octave -o W W = Z * mean(Z)
224 Out[16]: array([ 5., 20., 25., 50.])
224 Out[16]: array([ 5., 20., 25., 50.])
225
225
226 In [17]: W
226 In [17]: W
227 Out[17]: array([ 5., 20., 25., 50.])
227 Out[17]: array([ 5., 20., 25., 50.])
228
228
229 The size and format of output plots can be specified::
229 The size and format of output plots can be specified::
230
230
231 In [18]: %%octave -s 600,800 -f svg
231 In [18]: %%octave -s 600,800 -f svg
232 ...: plot([1, 2, 3]);
232 ...: plot([1, 2, 3]);
233
233
234 '''
234 '''
235 args = parse_argstring(self.octave, line)
235 args = parse_argstring(self.octave, line)
236
236
237 # arguments 'code' in line are prepended to the cell lines
237 # arguments 'code' in line are prepended to the cell lines
238 if cell is None:
238 if cell is None:
239 code = ''
239 code = ''
240 return_output = True
240 return_output = True
241 else:
241 else:
242 code = cell
242 code = cell
243 return_output = False
243 return_output = False
244
244
245 code = ' '.join(args.code) + code
245 code = ' '.join(args.code) + code
246
246
247 # if there is no local namespace then default to an empty dict
247 # if there is no local namespace then default to an empty dict
248 if local_ns is None:
248 if local_ns is None:
249 local_ns = {}
249 local_ns = {}
250
250
251 if args.input:
251 if args.input:
252 for input in ','.join(args.input).split(','):
252 for input in ','.join(args.input).split(','):
253 input = unicode_to_str(input)
253 input = unicode_to_str(input)
254 try:
254 try:
255 val = local_ns[input]
255 val = local_ns[input]
256 except KeyError:
256 except KeyError:
257 val = self.shell.user_ns[input]
257 val = self.shell.user_ns[input]
258 self._oct.put(input, val)
258 self._oct.put(input, val)
259
259
260 # generate plots in a temporary directory
260 # generate plots in a temporary directory
261 plot_dir = tempfile.mkdtemp()
261 plot_dir = tempfile.mkdtemp()
262 if args.size is not None:
262 if args.size is not None:
263 size = args.size
263 size = args.size
264 else:
264 else:
265 size = '400,240'
265 size = '400,240'
266
266
267 if args.format is not None:
267 if args.format is not None:
268 plot_format = args.format
268 plot_format = args.format
269 else:
269 else:
270 plot_format = 'png'
270 plot_format = 'png'
271
271
272 pre_call = '''
272 pre_call = '''
273 global __ipy_figures = [];
273 global __ipy_figures = [];
274 page_screen_output(0);
274 page_screen_output(0);
275
275
276 function fig_create(src, event)
276 function fig_create(src, event)
277 global __ipy_figures;
277 global __ipy_figures;
278 __ipy_figures(size(__ipy_figures) + 1) = src;
278 __ipy_figures(size(__ipy_figures) + 1) = src;
279 set(src, "visible", "off");
279 set(src, "visible", "off");
280 end
280 end
281
281
282 set(0, 'DefaultFigureCreateFcn', @fig_create);
282 set(0, 'DefaultFigureCreateFcn', @fig_create);
283
283
284 close all;
284 close all;
285 clear ans;
285 clear ans;
286
286
287 # ___<end_pre_call>___ #
287 # ___<end_pre_call>___ #
288 '''
288 '''
289
289
290 post_call = '''
290 post_call = '''
291 # ___<start_post_call>___ #
291 # ___<start_post_call>___ #
292
292
293 # Save output of the last execution
293 # Save output of the last execution
294 if exist("ans") == 1
294 if exist("ans") == 1
295 _ = ans;
295 _ = ans;
296 else
296 else
297 _ = nan;
297 _ = nan;
298 end
298 end
299
299
300 for f = __ipy_figures
300 for f = __ipy_figures
301 outfile = sprintf('%(plot_dir)s/__ipy_oct_fig_%%03d.png', f);
301 outfile = sprintf('%(plot_dir)s/__ipy_oct_fig_%%03d.png', f);
302 try
302 try
303 print(f, outfile, '-d%(plot_format)s', '-tight', '-S%(size)s');
303 print(f, outfile, '-d%(plot_format)s', '-tight', '-S%(size)s');
304 end
304 end
305 end
305 end
306
306
307 ''' % locals()
307 ''' % locals()
308
308
309 code = ' '.join((pre_call, code, post_call))
309 code = ' '.join((pre_call, code, post_call))
310 try:
310 try:
311 text_output = self._oct.run(code, verbose=False)
311 text_output = self._oct.run(code, verbose=False)
312 except (oct2py.Oct2PyError) as exception:
312 except (oct2py.Oct2PyError) as exception:
313 msg = exception.message
313 msg = exception.message
314 msg = msg.split('# ___<end_pre_call>___ #')[1]
314 msg = msg.split('# ___<end_pre_call>___ #')[1]
315 msg = msg.split('# ___<start_post_call>___ #')[0]
315 msg = msg.split('# ___<start_post_call>___ #')[0]
316 raise OctaveMagicError('Octave could not complete execution. '
316 raise OctaveMagicError('Octave could not complete execution. '
317 'Traceback (currently broken in oct2py): %s'
317 'Traceback (currently broken in oct2py): %s'
318 % msg)
318 % msg)
319
319
320 key = 'OctaveMagic.Octave'
320 key = 'OctaveMagic.Octave'
321 display_data = []
321 display_data = []
322
322
323 # Publish text output
323 # Publish text output
324 if text_output:
324 if text_output:
325 display_data.append((key, {'text/plain': text_output}))
325 display_data.append((key, {'text/plain': text_output}))
326
326
327 # Publish images
327 # Publish images
328 images = [open(imgfile, 'rb').read() for imgfile in \
328 images = [open(imgfile, 'rb').read() for imgfile in \
329 glob("%s/*" % plot_dir)]
329 glob("%s/*" % plot_dir)]
330 rmtree(plot_dir)
330 rmtree(plot_dir)
331
331
332 plot_mime_type = _mimetypes.get(plot_format, 'image/png')
332 plot_mime_type = _mimetypes.get(plot_format, 'image/png')
333 width, height = [int(s) for s in size.split(',')]
333 width, height = [int(s) for s in size.split(',')]
334 for image in images:
334 for image in images:
335 if plot_format == 'svg':
335 if plot_format == 'svg':
336 image = self._fix_gnuplot_svg_size(image, size=(width, height))
336 image = self._fix_gnuplot_svg_size(image, size=(width, height))
337 display_data.append((key, {plot_mime_type: image}))
337 display_data.append((key, {plot_mime_type: image}))
338
338
339 if args.output:
339 if args.output:
340 for output in ','.join(args.output).split(','):
340 for output in ','.join(args.output).split(','):
341 output = unicode_to_str(output)
341 output = unicode_to_str(output)
342 self.shell.push({output: self._oct.get(output)})
342 self.shell.push({output: self._oct.get(output)})
343
343
344 for source, data in display_data:
344 for source, data in display_data:
345 self._publish_display_data(source, data)
345 self._publish_display_data(source, data)
346
346
347 if return_output:
347 if return_output:
348 ans = self._oct.get('_')
348 ans = self._oct.get('_')
349
349
350 # Unfortunately, Octave doesn't have a "None" object,
350 # Unfortunately, Octave doesn't have a "None" object,
351 # so we can't return any NaN outputs
351 # so we can't return any NaN outputs
352 if np.isscalar(ans) and np.isnan(ans):
352 if np.isscalar(ans) and np.isnan(ans):
353 ans = None
353 ans = None
354
354
355 return ans
355 return ans
356
356
357
357
358 __doc__ = __doc__.format(
358 __doc__ = __doc__.format(
359 OCTAVE_DOC = ' '*8 + OctaveMagics.octave.__doc__,
359 OCTAVE_DOC = ' '*8 + OctaveMagics.octave.__doc__,
360 OCTAVE_PUSH_DOC = ' '*8 + OctaveMagics.octave_push.__doc__,
360 OCTAVE_PUSH_DOC = ' '*8 + OctaveMagics.octave_push.__doc__,
361 OCTAVE_PULL_DOC = ' '*8 + OctaveMagics.octave_pull.__doc__
361 OCTAVE_PULL_DOC = ' '*8 + OctaveMagics.octave_pull.__doc__
362 )
362 )
363
363
364
364
365 def load_ipython_extension(ip):
365 def load_ipython_extension(ip):
366 """Load the extension in IPython."""
366 """Load the extension in IPython."""
367 if 'octavemagic' not in ip.extension_manager.loaded:
367 ip.register_magics(OctaveMagics)
368 ip.register_magics(OctaveMagics)
@@ -1,594 +1,593 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 Usage
9 Usage
10 =====
10 =====
11
11
12 ``%R``
12 ``%R``
13
13
14 {R_DOC}
14 {R_DOC}
15
15
16 ``%Rpush``
16 ``%Rpush``
17
17
18 {RPUSH_DOC}
18 {RPUSH_DOC}
19
19
20 ``%Rpull``
20 ``%Rpull``
21
21
22 {RPULL_DOC}
22 {RPULL_DOC}
23
23
24 ``%Rget``
24 ``%Rget``
25
25
26 {RGET_DOC}
26 {RGET_DOC}
27
27
28 """
28 """
29
29
30 #-----------------------------------------------------------------------------
30 #-----------------------------------------------------------------------------
31 # Copyright (C) 2012 The IPython Development Team
31 # Copyright (C) 2012 The IPython Development Team
32 #
32 #
33 # Distributed under the terms of the BSD License. The full license is in
33 # Distributed under the terms of the BSD License. The full license is in
34 # the file COPYING, distributed as part of this software.
34 # the file COPYING, distributed as part of this software.
35 #-----------------------------------------------------------------------------
35 #-----------------------------------------------------------------------------
36
36
37 import sys
37 import sys
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 from getopt import getopt
41 from getopt import getopt
42
42
43 # numpy and rpy2 imports
43 # numpy and rpy2 imports
44
44
45 import numpy as np
45 import numpy as np
46
46
47 import rpy2.rinterface as ri
47 import rpy2.rinterface as ri
48 import rpy2.robjects as ro
48 import rpy2.robjects as ro
49 from rpy2.robjects.numpy2ri import numpy2ri
49 from rpy2.robjects.numpy2ri import numpy2ri
50 ro.conversion.py2ri = numpy2ri
50 ro.conversion.py2ri = numpy2ri
51
51
52 # IPython imports
52 # IPython imports
53
53
54 from IPython.core.displaypub import publish_display_data
54 from IPython.core.displaypub import publish_display_data
55 from IPython.core.magic import (Magics, magics_class, cell_magic, line_magic,
55 from IPython.core.magic import (Magics, magics_class, cell_magic, line_magic,
56 line_cell_magic, needs_local_scope)
56 line_cell_magic, needs_local_scope)
57 from IPython.testing.skipdoctest import skip_doctest
57 from IPython.testing.skipdoctest import skip_doctest
58 from IPython.core.magic_arguments import (
58 from IPython.core.magic_arguments import (
59 argument, magic_arguments, parse_argstring
59 argument, magic_arguments, parse_argstring
60 )
60 )
61 from IPython.utils.py3compat import str_to_unicode, unicode_to_str, PY3
61 from IPython.utils.py3compat import str_to_unicode, unicode_to_str, PY3
62
62
63 class RInterpreterError(ri.RRuntimeError):
63 class RInterpreterError(ri.RRuntimeError):
64 """An error when running R code in a %%R magic cell."""
64 """An error when running R code in a %%R magic cell."""
65 def __init__(self, line, err, stdout):
65 def __init__(self, line, err, stdout):
66 self.line = line
66 self.line = line
67 self.err = err.rstrip()
67 self.err = err.rstrip()
68 self.stdout = stdout.rstrip()
68 self.stdout = stdout.rstrip()
69
69
70 def __unicode__(self):
70 def __unicode__(self):
71 s = 'Failed to parse and evaluate line %r.\nR error message: %r' % \
71 s = 'Failed to parse and evaluate line %r.\nR error message: %r' % \
72 (self.line, self.err)
72 (self.line, self.err)
73 if self.stdout and (self.stdout != self.err):
73 if self.stdout and (self.stdout != self.err):
74 s += '\nR stdout:\n' + self.stdout
74 s += '\nR stdout:\n' + self.stdout
75 return s
75 return s
76
76
77 if PY3:
77 if PY3:
78 __str__ = __unicode__
78 __str__ = __unicode__
79 else:
79 else:
80 def __str__(self):
80 def __str__(self):
81 return unicode_to_str(unicode(self), 'utf-8')
81 return unicode_to_str(unicode(self), 'utf-8')
82
82
83 def Rconverter(Robj, dataframe=False):
83 def Rconverter(Robj, dataframe=False):
84 """
84 """
85 Convert an object in R's namespace to one suitable
85 Convert an object in R's namespace to one suitable
86 for ipython's namespace.
86 for ipython's namespace.
87
87
88 For a data.frame, it tries to return a structured array.
88 For a data.frame, it tries to return a structured array.
89 It first checks for colnames, then names.
89 It first checks for colnames, then names.
90 If all are NULL, it returns np.asarray(Robj), else
90 If all are NULL, it returns np.asarray(Robj), else
91 it tries to construct a recarray
91 it tries to construct a recarray
92
92
93 Parameters
93 Parameters
94 ----------
94 ----------
95
95
96 Robj: an R object returned from rpy2
96 Robj: an R object returned from rpy2
97 """
97 """
98 is_data_frame = ro.r('is.data.frame')
98 is_data_frame = ro.r('is.data.frame')
99 colnames = ro.r('colnames')
99 colnames = ro.r('colnames')
100 rownames = ro.r('rownames') # with pandas, these could be used for the index
100 rownames = ro.r('rownames') # with pandas, these could be used for the index
101 names = ro.r('names')
101 names = ro.r('names')
102
102
103 if dataframe:
103 if dataframe:
104 as_data_frame = ro.r('as.data.frame')
104 as_data_frame = ro.r('as.data.frame')
105 cols = colnames(Robj)
105 cols = colnames(Robj)
106 _names = names(Robj)
106 _names = names(Robj)
107 if cols != ri.NULL:
107 if cols != ri.NULL:
108 Robj = as_data_frame(Robj)
108 Robj = as_data_frame(Robj)
109 names = tuple(np.array(cols))
109 names = tuple(np.array(cols))
110 elif _names != ri.NULL:
110 elif _names != ri.NULL:
111 names = tuple(np.array(_names))
111 names = tuple(np.array(_names))
112 else: # failed to find names
112 else: # failed to find names
113 return np.asarray(Robj)
113 return np.asarray(Robj)
114 Robj = np.rec.fromarrays(Robj, names = names)
114 Robj = np.rec.fromarrays(Robj, names = names)
115 return np.asarray(Robj)
115 return np.asarray(Robj)
116
116
117 @magics_class
117 @magics_class
118 class RMagics(Magics):
118 class RMagics(Magics):
119 """A set of magics useful for interactive work with R via rpy2.
119 """A set of magics useful for interactive work with R via rpy2.
120 """
120 """
121
121
122 def __init__(self, shell, Rconverter=Rconverter,
122 def __init__(self, shell, Rconverter=Rconverter,
123 pyconverter=np.asarray,
123 pyconverter=np.asarray,
124 cache_display_data=False):
124 cache_display_data=False):
125 """
125 """
126 Parameters
126 Parameters
127 ----------
127 ----------
128
128
129 shell : IPython shell
129 shell : IPython shell
130
130
131 pyconverter : callable
131 pyconverter : callable
132 To be called on values in ipython namespace before
132 To be called on values in ipython namespace before
133 assigning to variables in rpy2.
133 assigning to variables in rpy2.
134
134
135 cache_display_data : bool
135 cache_display_data : bool
136 If True, the published results of the final call to R are
136 If True, the published results of the final call to R are
137 cached in the variable 'display_cache'.
137 cached in the variable 'display_cache'.
138
138
139 """
139 """
140 super(RMagics, self).__init__(shell)
140 super(RMagics, self).__init__(shell)
141 self.cache_display_data = cache_display_data
141 self.cache_display_data = cache_display_data
142
142
143 self.r = ro.R()
143 self.r = ro.R()
144
144
145 self.Rstdout_cache = []
145 self.Rstdout_cache = []
146 self.pyconverter = pyconverter
146 self.pyconverter = pyconverter
147 self.Rconverter = Rconverter
147 self.Rconverter = Rconverter
148
148
149 def eval(self, line):
149 def eval(self, line):
150 '''
150 '''
151 Parse and evaluate a line with rpy2.
151 Parse and evaluate a line with rpy2.
152 Returns the output to R's stdout() connection
152 Returns the output to R's stdout() connection
153 and the value of eval(parse(line)).
153 and the value of eval(parse(line)).
154 '''
154 '''
155 old_writeconsole = ri.get_writeconsole()
155 old_writeconsole = ri.get_writeconsole()
156 ri.set_writeconsole(self.write_console)
156 ri.set_writeconsole(self.write_console)
157 try:
157 try:
158 value = ri.baseenv['eval'](ri.parse(line))
158 value = ri.baseenv['eval'](ri.parse(line))
159 except (ri.RRuntimeError, ValueError) as exception:
159 except (ri.RRuntimeError, ValueError) as exception:
160 warning_or_other_msg = self.flush() # otherwise next return seems to have copy of error
160 warning_or_other_msg = self.flush() # otherwise next return seems to have copy of error
161 raise RInterpreterError(line, str_to_unicode(str(exception)), warning_or_other_msg)
161 raise RInterpreterError(line, str_to_unicode(str(exception)), warning_or_other_msg)
162 text_output = self.flush()
162 text_output = self.flush()
163 ri.set_writeconsole(old_writeconsole)
163 ri.set_writeconsole(old_writeconsole)
164 return text_output, value
164 return text_output, value
165
165
166 def write_console(self, output):
166 def write_console(self, output):
167 '''
167 '''
168 A hook to capture R's stdout in a cache.
168 A hook to capture R's stdout in a cache.
169 '''
169 '''
170 self.Rstdout_cache.append(output)
170 self.Rstdout_cache.append(output)
171
171
172 def flush(self):
172 def flush(self):
173 '''
173 '''
174 Flush R's stdout cache to a string, returning the string.
174 Flush R's stdout cache to a string, returning the string.
175 '''
175 '''
176 value = ''.join([str_to_unicode(s, 'utf-8') for s in self.Rstdout_cache])
176 value = ''.join([str_to_unicode(s, 'utf-8') for s in self.Rstdout_cache])
177 self.Rstdout_cache = []
177 self.Rstdout_cache = []
178 return value
178 return value
179
179
180 @skip_doctest
180 @skip_doctest
181 @line_magic
181 @line_magic
182 def Rpush(self, line):
182 def Rpush(self, line):
183 '''
183 '''
184 A line-level magic for R that pushes
184 A line-level magic for R that pushes
185 variables from python to rpy2. The line should be made up
185 variables from python to rpy2. The line should be made up
186 of whitespace separated variable names in the IPython
186 of whitespace separated variable names in the IPython
187 namespace::
187 namespace::
188
188
189 In [7]: import numpy as np
189 In [7]: import numpy as np
190
190
191 In [8]: X = np.array([4.5,6.3,7.9])
191 In [8]: X = np.array([4.5,6.3,7.9])
192
192
193 In [9]: X.mean()
193 In [9]: X.mean()
194 Out[9]: 6.2333333333333343
194 Out[9]: 6.2333333333333343
195
195
196 In [10]: %Rpush X
196 In [10]: %Rpush X
197
197
198 In [11]: %R mean(X)
198 In [11]: %R mean(X)
199 Out[11]: array([ 6.23333333])
199 Out[11]: array([ 6.23333333])
200
200
201 '''
201 '''
202
202
203 inputs = line.split(' ')
203 inputs = line.split(' ')
204 for input in inputs:
204 for input in inputs:
205 self.r.assign(input, self.pyconverter(self.shell.user_ns[input]))
205 self.r.assign(input, self.pyconverter(self.shell.user_ns[input]))
206
206
207 @skip_doctest
207 @skip_doctest
208 @magic_arguments()
208 @magic_arguments()
209 @argument(
209 @argument(
210 '-d', '--as_dataframe', action='store_true',
210 '-d', '--as_dataframe', action='store_true',
211 default=False,
211 default=False,
212 help='Convert objects to data.frames before returning to ipython.'
212 help='Convert objects to data.frames before returning to ipython.'
213 )
213 )
214 @argument(
214 @argument(
215 'outputs',
215 'outputs',
216 nargs='*',
216 nargs='*',
217 )
217 )
218 @line_magic
218 @line_magic
219 def Rpull(self, line):
219 def Rpull(self, line):
220 '''
220 '''
221 A line-level magic for R that pulls
221 A line-level magic for R that pulls
222 variables from python to rpy2::
222 variables from python to rpy2::
223
223
224 In [18]: _ = %R x = c(3,4,6.7); y = c(4,6,7); z = c('a',3,4)
224 In [18]: _ = %R x = c(3,4,6.7); y = c(4,6,7); z = c('a',3,4)
225
225
226 In [19]: %Rpull x y z
226 In [19]: %Rpull x y z
227
227
228 In [20]: x
228 In [20]: x
229 Out[20]: array([ 3. , 4. , 6.7])
229 Out[20]: array([ 3. , 4. , 6.7])
230
230
231 In [21]: y
231 In [21]: y
232 Out[21]: array([ 4., 6., 7.])
232 Out[21]: array([ 4., 6., 7.])
233
233
234 In [22]: z
234 In [22]: z
235 Out[22]:
235 Out[22]:
236 array(['a', '3', '4'],
236 array(['a', '3', '4'],
237 dtype='|S1')
237 dtype='|S1')
238
238
239
239
240 If --as_dataframe, then each object is returned as a structured array
240 If --as_dataframe, then each object is returned as a structured array
241 after first passed through "as.data.frame" in R before
241 after first passed through "as.data.frame" in R before
242 being calling self.Rconverter.
242 being calling self.Rconverter.
243 This is useful when a structured array is desired as output, or
243 This is useful when a structured array is desired as output, or
244 when the object in R has mixed data types.
244 when the object in R has mixed data types.
245 See the %%R docstring for more examples.
245 See the %%R docstring for more examples.
246
246
247 Notes
247 Notes
248 -----
248 -----
249
249
250 Beware that R names can have '.' so this is not fool proof.
250 Beware that R names can have '.' so this is not fool proof.
251 To avoid this, don't name your R objects with '.'s...
251 To avoid this, don't name your R objects with '.'s...
252
252
253 '''
253 '''
254 args = parse_argstring(self.Rpull, line)
254 args = parse_argstring(self.Rpull, line)
255 outputs = args.outputs
255 outputs = args.outputs
256 for output in outputs:
256 for output in outputs:
257 self.shell.push({output:self.Rconverter(self.r(output),dataframe=args.as_dataframe)})
257 self.shell.push({output:self.Rconverter(self.r(output),dataframe=args.as_dataframe)})
258
258
259 @skip_doctest
259 @skip_doctest
260 @magic_arguments()
260 @magic_arguments()
261 @argument(
261 @argument(
262 '-d', '--as_dataframe', action='store_true',
262 '-d', '--as_dataframe', action='store_true',
263 default=False,
263 default=False,
264 help='Convert objects to data.frames before returning to ipython.'
264 help='Convert objects to data.frames before returning to ipython.'
265 )
265 )
266 @argument(
266 @argument(
267 'output',
267 'output',
268 nargs=1,
268 nargs=1,
269 type=str,
269 type=str,
270 )
270 )
271 @line_magic
271 @line_magic
272 def Rget(self, line):
272 def Rget(self, line):
273 '''
273 '''
274 Return an object from rpy2, possibly as a structured array (if possible).
274 Return an object from rpy2, possibly as a structured array (if possible).
275 Similar to Rpull except only one argument is accepted and the value is
275 Similar to Rpull except only one argument is accepted and the value is
276 returned rather than pushed to self.shell.user_ns::
276 returned rather than pushed to self.shell.user_ns::
277
277
278 In [3]: dtype=[('x', '<i4'), ('y', '<f8'), ('z', '|S1')]
278 In [3]: dtype=[('x', '<i4'), ('y', '<f8'), ('z', '|S1')]
279
279
280 In [4]: datapy = np.array([(1, 2.9, 'a'), (2, 3.5, 'b'), (3, 2.1, 'c'), (4, 5, 'e')], dtype=dtype)
280 In [4]: datapy = np.array([(1, 2.9, 'a'), (2, 3.5, 'b'), (3, 2.1, 'c'), (4, 5, 'e')], dtype=dtype)
281
281
282 In [5]: %R -i datapy
282 In [5]: %R -i datapy
283
283
284 In [6]: %Rget datapy
284 In [6]: %Rget datapy
285 Out[6]:
285 Out[6]:
286 array([['1', '2', '3', '4'],
286 array([['1', '2', '3', '4'],
287 ['2', '3', '2', '5'],
287 ['2', '3', '2', '5'],
288 ['a', 'b', 'c', 'e']],
288 ['a', 'b', 'c', 'e']],
289 dtype='|S1')
289 dtype='|S1')
290
290
291 In [7]: %Rget -d datapy
291 In [7]: %Rget -d datapy
292 Out[7]:
292 Out[7]:
293 array([(1, 2.9, 'a'), (2, 3.5, 'b'), (3, 2.1, 'c'), (4, 5.0, 'e')],
293 array([(1, 2.9, 'a'), (2, 3.5, 'b'), (3, 2.1, 'c'), (4, 5.0, 'e')],
294 dtype=[('x', '<i4'), ('y', '<f8'), ('z', '|S1')])
294 dtype=[('x', '<i4'), ('y', '<f8'), ('z', '|S1')])
295
295
296 '''
296 '''
297 args = parse_argstring(self.Rget, line)
297 args = parse_argstring(self.Rget, line)
298 output = args.output
298 output = args.output
299 return self.Rconverter(self.r(output[0]),dataframe=args.as_dataframe)
299 return self.Rconverter(self.r(output[0]),dataframe=args.as_dataframe)
300
300
301
301
302 @skip_doctest
302 @skip_doctest
303 @magic_arguments()
303 @magic_arguments()
304 @argument(
304 @argument(
305 '-i', '--input', action='append',
305 '-i', '--input', action='append',
306 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.'
306 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.'
307 )
307 )
308 @argument(
308 @argument(
309 '-o', '--output', action='append',
309 '-o', '--output', action='append',
310 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.'
310 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.'
311 )
311 )
312 @argument(
312 @argument(
313 '-w', '--width', type=int,
313 '-w', '--width', type=int,
314 help='Width of png plotting device sent as an argument to *png* in R.'
314 help='Width of png plotting device sent as an argument to *png* in R.'
315 )
315 )
316 @argument(
316 @argument(
317 '-h', '--height', type=int,
317 '-h', '--height', type=int,
318 help='Height of png plotting device sent as an argument to *png* in R.'
318 help='Height of png plotting device sent as an argument to *png* in R.'
319 )
319 )
320
320
321 @argument(
321 @argument(
322 '-d', '--dataframe', action='append',
322 '-d', '--dataframe', action='append',
323 help='Convert these objects to data.frames and return as structured arrays.'
323 help='Convert these objects to data.frames and return as structured arrays.'
324 )
324 )
325 @argument(
325 @argument(
326 '-u', '--units', type=int,
326 '-u', '--units', type=int,
327 help='Units of png plotting device sent as an argument to *png* in R. One of ["px", "in", "cm", "mm"].'
327 help='Units of png plotting device sent as an argument to *png* in R. One of ["px", "in", "cm", "mm"].'
328 )
328 )
329 @argument(
329 @argument(
330 '-p', '--pointsize', type=int,
330 '-p', '--pointsize', type=int,
331 help='Pointsize of png plotting device sent as an argument to *png* in R.'
331 help='Pointsize of png plotting device sent as an argument to *png* in R.'
332 )
332 )
333 @argument(
333 @argument(
334 '-b', '--bg',
334 '-b', '--bg',
335 help='Background of png plotting device sent as an argument to *png* in R.'
335 help='Background of png plotting device sent as an argument to *png* in R.'
336 )
336 )
337 @argument(
337 @argument(
338 '-n', '--noreturn',
338 '-n', '--noreturn',
339 help='Force the magic to not return anything.',
339 help='Force the magic to not return anything.',
340 action='store_true',
340 action='store_true',
341 default=False
341 default=False
342 )
342 )
343 @argument(
343 @argument(
344 'code',
344 'code',
345 nargs='*',
345 nargs='*',
346 )
346 )
347 @needs_local_scope
347 @needs_local_scope
348 @line_cell_magic
348 @line_cell_magic
349 def R(self, line, cell=None, local_ns=None):
349 def R(self, line, cell=None, local_ns=None):
350 '''
350 '''
351 Execute code in R, and pull some of the results back into the Python namespace.
351 Execute code in R, and pull some of the results back into the Python namespace.
352
352
353 In line mode, this will evaluate an expression and convert the returned value to a Python object.
353 In line mode, this will evaluate an expression and convert the returned value to a Python object.
354 The return value is determined by rpy2's behaviour of returning the result of evaluating the
354 The return value is determined by rpy2's behaviour of returning the result of evaluating the
355 final line.
355 final line.
356
356
357 Multiple R lines can be executed by joining them with semicolons::
357 Multiple R lines can be executed by joining them with semicolons::
358
358
359 In [9]: %R X=c(1,4,5,7); sd(X); mean(X)
359 In [9]: %R X=c(1,4,5,7); sd(X); mean(X)
360 Out[9]: array([ 4.25])
360 Out[9]: array([ 4.25])
361
361
362 As a cell, this will run a block of R code, without bringing anything back by default::
362 As a cell, this will run a block of R code, without bringing anything back by default::
363
363
364 In [10]: %%R
364 In [10]: %%R
365 ....: Y = c(2,4,3,9)
365 ....: Y = c(2,4,3,9)
366 ....: print(summary(lm(Y~X)))
366 ....: print(summary(lm(Y~X)))
367 ....:
367 ....:
368
368
369 Call:
369 Call:
370 lm(formula = Y ~ X)
370 lm(formula = Y ~ X)
371
371
372 Residuals:
372 Residuals:
373 1 2 3 4
373 1 2 3 4
374 0.88 -0.24 -2.28 1.64
374 0.88 -0.24 -2.28 1.64
375
375
376 Coefficients:
376 Coefficients:
377 Estimate Std. Error t value Pr(>|t|)
377 Estimate Std. Error t value Pr(>|t|)
378 (Intercept) 0.0800 2.3000 0.035 0.975
378 (Intercept) 0.0800 2.3000 0.035 0.975
379 X 1.0400 0.4822 2.157 0.164
379 X 1.0400 0.4822 2.157 0.164
380
380
381 Residual standard error: 2.088 on 2 degrees of freedom
381 Residual standard error: 2.088 on 2 degrees of freedom
382 Multiple R-squared: 0.6993,Adjusted R-squared: 0.549
382 Multiple R-squared: 0.6993,Adjusted R-squared: 0.549
383 F-statistic: 4.651 on 1 and 2 DF, p-value: 0.1638
383 F-statistic: 4.651 on 1 and 2 DF, p-value: 0.1638
384
384
385 In the notebook, plots are published as the output of the cell.
385 In the notebook, plots are published as the output of the cell.
386
386
387 %R plot(X, Y)
387 %R plot(X, Y)
388
388
389 will create a scatter plot of X bs Y.
389 will create a scatter plot of X bs Y.
390
390
391 If cell is not None and line has some R code, it is prepended to
391 If cell is not None and line has some R code, it is prepended to
392 the R code in cell.
392 the R code in cell.
393
393
394 Objects can be passed back and forth between rpy2 and python via the -i -o flags in line::
394 Objects can be passed back and forth between rpy2 and python via the -i -o flags in line::
395
395
396 In [14]: Z = np.array([1,4,5,10])
396 In [14]: Z = np.array([1,4,5,10])
397
397
398 In [15]: %R -i Z mean(Z)
398 In [15]: %R -i Z mean(Z)
399 Out[15]: array([ 5.])
399 Out[15]: array([ 5.])
400
400
401
401
402 In [16]: %R -o W W=Z*mean(Z)
402 In [16]: %R -o W W=Z*mean(Z)
403 Out[16]: array([ 5., 20., 25., 50.])
403 Out[16]: array([ 5., 20., 25., 50.])
404
404
405 In [17]: W
405 In [17]: W
406 Out[17]: array([ 5., 20., 25., 50.])
406 Out[17]: array([ 5., 20., 25., 50.])
407
407
408 The return value is determined by these rules:
408 The return value is determined by these rules:
409
409
410 * If the cell is not None, the magic returns None.
410 * If the cell is not None, the magic returns None.
411
411
412 * If the cell evaluates as False, the resulting value is returned
412 * If the cell evaluates as False, the resulting value is returned
413 unless the final line prints something to the console, in
413 unless the final line prints something to the console, in
414 which case None is returned.
414 which case None is returned.
415
415
416 * If the final line results in a NULL value when evaluated
416 * If the final line results in a NULL value when evaluated
417 by rpy2, then None is returned.
417 by rpy2, then None is returned.
418
418
419 * No attempt is made to convert the final value to a structured array.
419 * No attempt is made to convert the final value to a structured array.
420 Use the --dataframe flag or %Rget to push / return a structured array.
420 Use the --dataframe flag or %Rget to push / return a structured array.
421
421
422 * If the -n flag is present, there is no return value.
422 * If the -n flag is present, there is no return value.
423
423
424 * A trailing ';' will also result in no return value as the last
424 * A trailing ';' will also result in no return value as the last
425 value in the line is an empty string.
425 value in the line is an empty string.
426
426
427 The --dataframe argument will attempt to return structured arrays.
427 The --dataframe argument will attempt to return structured arrays.
428 This is useful for dataframes with
428 This is useful for dataframes with
429 mixed data types. Note also that for a data.frame,
429 mixed data types. Note also that for a data.frame,
430 if it is returned as an ndarray, it is transposed::
430 if it is returned as an ndarray, it is transposed::
431
431
432 In [18]: dtype=[('x', '<i4'), ('y', '<f8'), ('z', '|S1')]
432 In [18]: dtype=[('x', '<i4'), ('y', '<f8'), ('z', '|S1')]
433
433
434 In [19]: datapy = np.array([(1, 2.9, 'a'), (2, 3.5, 'b'), (3, 2.1, 'c'), (4, 5, 'e')], dtype=dtype)
434 In [19]: datapy = np.array([(1, 2.9, 'a'), (2, 3.5, 'b'), (3, 2.1, 'c'), (4, 5, 'e')], dtype=dtype)
435
435
436 In [20]: %%R -o datar
436 In [20]: %%R -o datar
437 datar = datapy
437 datar = datapy
438 ....:
438 ....:
439
439
440 In [21]: datar
440 In [21]: datar
441 Out[21]:
441 Out[21]:
442 array([['1', '2', '3', '4'],
442 array([['1', '2', '3', '4'],
443 ['2', '3', '2', '5'],
443 ['2', '3', '2', '5'],
444 ['a', 'b', 'c', 'e']],
444 ['a', 'b', 'c', 'e']],
445 dtype='|S1')
445 dtype='|S1')
446
446
447 In [22]: %%R -d datar
447 In [22]: %%R -d datar
448 datar = datapy
448 datar = datapy
449 ....:
449 ....:
450
450
451 In [23]: datar
451 In [23]: datar
452 Out[23]:
452 Out[23]:
453 array([(1, 2.9, 'a'), (2, 3.5, 'b'), (3, 2.1, 'c'), (4, 5.0, 'e')],
453 array([(1, 2.9, 'a'), (2, 3.5, 'b'), (3, 2.1, 'c'), (4, 5.0, 'e')],
454 dtype=[('x', '<i4'), ('y', '<f8'), ('z', '|S1')])
454 dtype=[('x', '<i4'), ('y', '<f8'), ('z', '|S1')])
455
455
456 The --dataframe argument first tries colnames, then names.
456 The --dataframe argument first tries colnames, then names.
457 If both are NULL, it returns an ndarray (i.e. unstructured)::
457 If both are NULL, it returns an ndarray (i.e. unstructured)::
458
458
459 In [1]: %R mydata=c(4,6,8.3); NULL
459 In [1]: %R mydata=c(4,6,8.3); NULL
460
460
461 In [2]: %R -d mydata
461 In [2]: %R -d mydata
462
462
463 In [3]: mydata
463 In [3]: mydata
464 Out[3]: array([ 4. , 6. , 8.3])
464 Out[3]: array([ 4. , 6. , 8.3])
465
465
466 In [4]: %R names(mydata) = c('a','b','c'); NULL
466 In [4]: %R names(mydata) = c('a','b','c'); NULL
467
467
468 In [5]: %R -d mydata
468 In [5]: %R -d mydata
469
469
470 In [6]: mydata
470 In [6]: mydata
471 Out[6]:
471 Out[6]:
472 array((4.0, 6.0, 8.3),
472 array((4.0, 6.0, 8.3),
473 dtype=[('a', '<f8'), ('b', '<f8'), ('c', '<f8')])
473 dtype=[('a', '<f8'), ('b', '<f8'), ('c', '<f8')])
474
474
475 In [7]: %R -o mydata
475 In [7]: %R -o mydata
476
476
477 In [8]: mydata
477 In [8]: mydata
478 Out[8]: array([ 4. , 6. , 8.3])
478 Out[8]: array([ 4. , 6. , 8.3])
479
479
480 '''
480 '''
481
481
482 args = parse_argstring(self.R, line)
482 args = parse_argstring(self.R, line)
483
483
484 # arguments 'code' in line are prepended to
484 # arguments 'code' in line are prepended to
485 # the cell lines
485 # the cell lines
486
486
487 if cell is None:
487 if cell is None:
488 code = ''
488 code = ''
489 return_output = True
489 return_output = True
490 line_mode = True
490 line_mode = True
491 else:
491 else:
492 code = cell
492 code = cell
493 return_output = False
493 return_output = False
494 line_mode = False
494 line_mode = False
495
495
496 code = ' '.join(args.code) + code
496 code = ' '.join(args.code) + code
497
497
498 # if there is no local namespace then default to an empty dict
498 # if there is no local namespace then default to an empty dict
499 if local_ns is None:
499 if local_ns is None:
500 local_ns = {}
500 local_ns = {}
501
501
502 if args.input:
502 if args.input:
503 for input in ','.join(args.input).split(','):
503 for input in ','.join(args.input).split(','):
504 try:
504 try:
505 val = local_ns[input]
505 val = local_ns[input]
506 except KeyError:
506 except KeyError:
507 val = self.shell.user_ns[input]
507 val = self.shell.user_ns[input]
508 self.r.assign(input, self.pyconverter(val))
508 self.r.assign(input, self.pyconverter(val))
509
509
510 png_argdict = dict([(n, getattr(args, n)) for n in ['units', 'height', 'width', 'bg', 'pointsize']])
510 png_argdict = dict([(n, getattr(args, n)) for n in ['units', 'height', 'width', 'bg', 'pointsize']])
511 png_args = ','.join(['%s=%s' % (o,v) for o, v in png_argdict.items() if v is not None])
511 png_args = ','.join(['%s=%s' % (o,v) for o, v in png_argdict.items() if v is not None])
512 # execute the R code in a temporary directory
512 # execute the R code in a temporary directory
513
513
514 tmpd = tempfile.mkdtemp()
514 tmpd = tempfile.mkdtemp()
515 self.r('png("%s/Rplots%%03d.png",%s)' % (tmpd, png_args))
515 self.r('png("%s/Rplots%%03d.png",%s)' % (tmpd, png_args))
516
516
517 text_output = ''
517 text_output = ''
518 if line_mode:
518 if line_mode:
519 for line in code.split(';'):
519 for line in code.split(';'):
520 text_result, result = self.eval(line)
520 text_result, result = self.eval(line)
521 text_output += text_result
521 text_output += text_result
522 if text_result:
522 if text_result:
523 # the last line printed something to the console so we won't return it
523 # the last line printed something to the console so we won't return it
524 return_output = False
524 return_output = False
525 else:
525 else:
526 text_result, result = self.eval(code)
526 text_result, result = self.eval(code)
527 text_output += text_result
527 text_output += text_result
528
528
529 self.r('dev.off()')
529 self.r('dev.off()')
530
530
531 # read out all the saved .png files
531 # read out all the saved .png files
532
532
533 images = [open(imgfile, 'rb').read() for imgfile in glob("%s/Rplots*png" % tmpd)]
533 images = [open(imgfile, 'rb').read() for imgfile in glob("%s/Rplots*png" % tmpd)]
534
534
535 # now publish the images
535 # now publish the images
536 # mimicking IPython/zmq/pylab/backend_inline.py
536 # mimicking IPython/zmq/pylab/backend_inline.py
537 fmt = 'png'
537 fmt = 'png'
538 mimetypes = { 'png' : 'image/png', 'svg' : 'image/svg+xml' }
538 mimetypes = { 'png' : 'image/png', 'svg' : 'image/svg+xml' }
539 mime = mimetypes[fmt]
539 mime = mimetypes[fmt]
540
540
541 # publish the printed R objects, if any
541 # publish the printed R objects, if any
542
542
543 display_data = []
543 display_data = []
544 if text_output:
544 if text_output:
545 display_data.append(('RMagic.R', {'text/plain':text_output}))
545 display_data.append(('RMagic.R', {'text/plain':text_output}))
546
546
547 # flush text streams before sending figures, helps a little with output
547 # flush text streams before sending figures, helps a little with output
548 for image in images:
548 for image in images:
549 # synchronization in the console (though it's a bandaid, not a real sln)
549 # synchronization in the console (though it's a bandaid, not a real sln)
550 sys.stdout.flush(); sys.stderr.flush()
550 sys.stdout.flush(); sys.stderr.flush()
551 display_data.append(('RMagic.R', {mime: image}))
551 display_data.append(('RMagic.R', {mime: image}))
552
552
553 # kill the temporary directory
553 # kill the temporary directory
554 rmtree(tmpd)
554 rmtree(tmpd)
555
555
556 # try to turn every output into a numpy array
556 # try to turn every output into a numpy array
557 # this means that output are assumed to be castable
557 # this means that output are assumed to be castable
558 # as numpy arrays
558 # as numpy arrays
559
559
560 if args.output:
560 if args.output:
561 for output in ','.join(args.output).split(','):
561 for output in ','.join(args.output).split(','):
562 self.shell.push({output:self.Rconverter(self.r(output), dataframe=False)})
562 self.shell.push({output:self.Rconverter(self.r(output), dataframe=False)})
563
563
564 if args.dataframe:
564 if args.dataframe:
565 for output in ','.join(args.dataframe).split(','):
565 for output in ','.join(args.dataframe).split(','):
566 self.shell.push({output:self.Rconverter(self.r(output), dataframe=True)})
566 self.shell.push({output:self.Rconverter(self.r(output), dataframe=True)})
567
567
568 for tag, disp_d in display_data:
568 for tag, disp_d in display_data:
569 publish_display_data(tag, disp_d)
569 publish_display_data(tag, disp_d)
570
570
571 # this will keep a reference to the display_data
571 # this will keep a reference to the display_data
572 # which might be useful to other objects who happen to use
572 # which might be useful to other objects who happen to use
573 # this method
573 # this method
574
574
575 if self.cache_display_data:
575 if self.cache_display_data:
576 self.display_cache = display_data
576 self.display_cache = display_data
577
577
578 # if in line mode and return_output, return the result as an ndarray
578 # if in line mode and return_output, return the result as an ndarray
579 if return_output and not args.noreturn:
579 if return_output and not args.noreturn:
580 if result != ri.NULL:
580 if result != ri.NULL:
581 return self.Rconverter(result, dataframe=False)
581 return self.Rconverter(result, dataframe=False)
582
582
583 __doc__ = __doc__.format(
583 __doc__ = __doc__.format(
584 R_DOC = ' '*8 + RMagics.R.__doc__,
584 R_DOC = ' '*8 + RMagics.R.__doc__,
585 RPUSH_DOC = ' '*8 + RMagics.Rpush.__doc__,
585 RPUSH_DOC = ' '*8 + RMagics.Rpush.__doc__,
586 RPULL_DOC = ' '*8 + RMagics.Rpull.__doc__,
586 RPULL_DOC = ' '*8 + RMagics.Rpull.__doc__,
587 RGET_DOC = ' '*8 + RMagics.Rget.__doc__
587 RGET_DOC = ' '*8 + RMagics.Rget.__doc__
588 )
588 )
589
589
590
590
591 def load_ipython_extension(ip):
591 def load_ipython_extension(ip):
592 """Load the extension in IPython."""
592 """Load the extension in IPython."""
593 if 'rmagic' not in ip.extension_manager.loaded:
593 ip.register_magics(RMagics)
594 ip.register_magics(RMagics)
@@ -1,215 +1,214 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """
2 """
3 %store magic for lightweight persistence.
3 %store magic for lightweight persistence.
4
4
5 Stores variables, aliases and macros in IPython's database.
5 Stores variables, aliases and macros in IPython's database.
6
6
7 To automatically restore stored variables at startup, add this to your
7 To automatically restore stored variables at startup, add this to your
8 :file:`ipython_config.py` file::
8 :file:`ipython_config.py` file::
9
9
10 c.StoreMagic.autorestore = True
10 c.StoreMagic.autorestore = True
11 """
11 """
12 #-----------------------------------------------------------------------------
12 #-----------------------------------------------------------------------------
13 # Copyright (c) 2012, The IPython Development Team.
13 # Copyright (c) 2012, The IPython Development Team.
14 #
14 #
15 # Distributed under the terms of the Modified BSD License.
15 # Distributed under the terms of the Modified BSD License.
16 #
16 #
17 # The full license is in the file COPYING.txt, distributed with this software.
17 # The full license is in the file COPYING.txt, distributed with this software.
18 #-----------------------------------------------------------------------------
18 #-----------------------------------------------------------------------------
19
19
20 #-----------------------------------------------------------------------------
20 #-----------------------------------------------------------------------------
21 # Imports
21 # Imports
22 #-----------------------------------------------------------------------------
22 #-----------------------------------------------------------------------------
23
23
24 # Stdlib
24 # Stdlib
25 import inspect, os, sys, textwrap
25 import inspect, os, sys, textwrap
26
26
27 # Our own
27 # Our own
28 from IPython.core.error import UsageError
28 from IPython.core.error import UsageError
29 from IPython.core.fakemodule import FakeModule
29 from IPython.core.fakemodule import FakeModule
30 from IPython.core.magic import Magics, magics_class, line_magic
30 from IPython.core.magic import Magics, magics_class, line_magic
31 from IPython.testing.skipdoctest import skip_doctest
31 from IPython.testing.skipdoctest import skip_doctest
32
32
33 #-----------------------------------------------------------------------------
33 #-----------------------------------------------------------------------------
34 # Functions and classes
34 # Functions and classes
35 #-----------------------------------------------------------------------------
35 #-----------------------------------------------------------------------------
36
36
37 def restore_aliases(ip):
37 def restore_aliases(ip):
38 staliases = ip.db.get('stored_aliases', {})
38 staliases = ip.db.get('stored_aliases', {})
39 for k,v in staliases.items():
39 for k,v in staliases.items():
40 #print "restore alias",k,v # dbg
40 #print "restore alias",k,v # dbg
41 #self.alias_table[k] = v
41 #self.alias_table[k] = v
42 ip.alias_manager.define_alias(k,v)
42 ip.alias_manager.define_alias(k,v)
43
43
44
44
45 def refresh_variables(ip):
45 def refresh_variables(ip):
46 db = ip.db
46 db = ip.db
47 for key in db.keys('autorestore/*'):
47 for key in db.keys('autorestore/*'):
48 # strip autorestore
48 # strip autorestore
49 justkey = os.path.basename(key)
49 justkey = os.path.basename(key)
50 try:
50 try:
51 obj = db[key]
51 obj = db[key]
52 except KeyError:
52 except KeyError:
53 print "Unable to restore variable '%s', ignoring (use %%store -d to forget!)" % justkey
53 print "Unable to restore variable '%s', ignoring (use %%store -d to forget!)" % justkey
54 print "The error was:", sys.exc_info()[0]
54 print "The error was:", sys.exc_info()[0]
55 else:
55 else:
56 #print "restored",justkey,"=",obj #dbg
56 #print "restored",justkey,"=",obj #dbg
57 ip.user_ns[justkey] = obj
57 ip.user_ns[justkey] = obj
58
58
59
59
60 def restore_dhist(ip):
60 def restore_dhist(ip):
61 ip.user_ns['_dh'] = ip.db.get('dhist',[])
61 ip.user_ns['_dh'] = ip.db.get('dhist',[])
62
62
63
63
64 def restore_data(ip):
64 def restore_data(ip):
65 refresh_variables(ip)
65 refresh_variables(ip)
66 restore_aliases(ip)
66 restore_aliases(ip)
67 restore_dhist(ip)
67 restore_dhist(ip)
68
68
69
69
70 @magics_class
70 @magics_class
71 class StoreMagics(Magics):
71 class StoreMagics(Magics):
72 """Lightweight persistence for python variables.
72 """Lightweight persistence for python variables.
73
73
74 Provides the %store magic."""
74 Provides the %store magic."""
75
75
76 @skip_doctest
76 @skip_doctest
77 @line_magic
77 @line_magic
78 def store(self, parameter_s=''):
78 def store(self, parameter_s=''):
79 """Lightweight persistence for python variables.
79 """Lightweight persistence for python variables.
80
80
81 Example::
81 Example::
82
82
83 In [1]: l = ['hello',10,'world']
83 In [1]: l = ['hello',10,'world']
84 In [2]: %store l
84 In [2]: %store l
85 In [3]: exit
85 In [3]: exit
86
86
87 (IPython session is closed and started again...)
87 (IPython session is closed and started again...)
88
88
89 ville@badger:~$ ipython
89 ville@badger:~$ ipython
90 In [1]: l
90 In [1]: l
91 Out[1]: ['hello', 10, 'world']
91 Out[1]: ['hello', 10, 'world']
92
92
93 Usage:
93 Usage:
94
94
95 * ``%store`` - Show list of all variables and their current
95 * ``%store`` - Show list of all variables and their current
96 values
96 values
97 * ``%store spam`` - Store the *current* value of the variable spam
97 * ``%store spam`` - Store the *current* value of the variable spam
98 to disk
98 to disk
99 * ``%store -d spam`` - Remove the variable and its value from storage
99 * ``%store -d spam`` - Remove the variable and its value from storage
100 * ``%store -z`` - Remove all variables from storage
100 * ``%store -z`` - Remove all variables from storage
101 * ``%store -r`` - Refresh all variables from store (delete
101 * ``%store -r`` - Refresh all variables from store (delete
102 current vals)
102 current vals)
103 * ``%store foo >a.txt`` - Store value of foo to new file a.txt
103 * ``%store foo >a.txt`` - Store value of foo to new file a.txt
104 * ``%store foo >>a.txt`` - Append value of foo to file a.txt
104 * ``%store foo >>a.txt`` - Append value of foo to file a.txt
105
105
106 It should be noted that if you change the value of a variable, you
106 It should be noted that if you change the value of a variable, you
107 need to %store it again if you want to persist the new value.
107 need to %store it again if you want to persist the new value.
108
108
109 Note also that the variables will need to be pickleable; most basic
109 Note also that the variables will need to be pickleable; most basic
110 python types can be safely %store'd.
110 python types can be safely %store'd.
111
111
112 Also aliases can be %store'd across sessions.
112 Also aliases can be %store'd across sessions.
113 """
113 """
114
114
115 opts,argsl = self.parse_options(parameter_s,'drz',mode='string')
115 opts,argsl = self.parse_options(parameter_s,'drz',mode='string')
116 args = argsl.split(None,1)
116 args = argsl.split(None,1)
117 ip = self.shell
117 ip = self.shell
118 db = ip.db
118 db = ip.db
119 # delete
119 # delete
120 if 'd' in opts:
120 if 'd' in opts:
121 try:
121 try:
122 todel = args[0]
122 todel = args[0]
123 except IndexError:
123 except IndexError:
124 raise UsageError('You must provide the variable to forget')
124 raise UsageError('You must provide the variable to forget')
125 else:
125 else:
126 try:
126 try:
127 del db['autorestore/' + todel]
127 del db['autorestore/' + todel]
128 except:
128 except:
129 raise UsageError("Can't delete variable '%s'" % todel)
129 raise UsageError("Can't delete variable '%s'" % todel)
130 # reset
130 # reset
131 elif 'z' in opts:
131 elif 'z' in opts:
132 for k in db.keys('autorestore/*'):
132 for k in db.keys('autorestore/*'):
133 del db[k]
133 del db[k]
134
134
135 elif 'r' in opts:
135 elif 'r' in opts:
136 refresh_variables(ip)
136 refresh_variables(ip)
137
137
138
138
139 # run without arguments -> list variables & values
139 # run without arguments -> list variables & values
140 elif not args:
140 elif not args:
141 vars = db.keys('autorestore/*')
141 vars = db.keys('autorestore/*')
142 vars.sort()
142 vars.sort()
143 if vars:
143 if vars:
144 size = max(map(len, vars))
144 size = max(map(len, vars))
145 else:
145 else:
146 size = 0
146 size = 0
147
147
148 print 'Stored variables and their in-db values:'
148 print 'Stored variables and their in-db values:'
149 fmt = '%-'+str(size)+'s -> %s'
149 fmt = '%-'+str(size)+'s -> %s'
150 get = db.get
150 get = db.get
151 for var in vars:
151 for var in vars:
152 justkey = os.path.basename(var)
152 justkey = os.path.basename(var)
153 # print 30 first characters from every var
153 # print 30 first characters from every var
154 print fmt % (justkey, repr(get(var, '<unavailable>'))[:50])
154 print fmt % (justkey, repr(get(var, '<unavailable>'))[:50])
155
155
156 # default action - store the variable
156 # default action - store the variable
157 else:
157 else:
158 # %store foo >file.txt or >>file.txt
158 # %store foo >file.txt or >>file.txt
159 if len(args) > 1 and args[1].startswith('>'):
159 if len(args) > 1 and args[1].startswith('>'):
160 fnam = os.path.expanduser(args[1].lstrip('>').lstrip())
160 fnam = os.path.expanduser(args[1].lstrip('>').lstrip())
161 if args[1].startswith('>>'):
161 if args[1].startswith('>>'):
162 fil = open(fnam, 'a')
162 fil = open(fnam, 'a')
163 else:
163 else:
164 fil = open(fnam, 'w')
164 fil = open(fnam, 'w')
165 obj = ip.ev(args[0])
165 obj = ip.ev(args[0])
166 print "Writing '%s' (%s) to file '%s'." % (args[0],
166 print "Writing '%s' (%s) to file '%s'." % (args[0],
167 obj.__class__.__name__, fnam)
167 obj.__class__.__name__, fnam)
168
168
169
169
170 if not isinstance (obj, basestring):
170 if not isinstance (obj, basestring):
171 from pprint import pprint
171 from pprint import pprint
172 pprint(obj, fil)
172 pprint(obj, fil)
173 else:
173 else:
174 fil.write(obj)
174 fil.write(obj)
175 if not obj.endswith('\n'):
175 if not obj.endswith('\n'):
176 fil.write('\n')
176 fil.write('\n')
177
177
178 fil.close()
178 fil.close()
179 return
179 return
180
180
181 # %store foo
181 # %store foo
182 try:
182 try:
183 obj = ip.user_ns[args[0]]
183 obj = ip.user_ns[args[0]]
184 except KeyError:
184 except KeyError:
185 # it might be an alias
185 # it might be an alias
186 # This needs to be refactored to use the new AliasManager stuff.
186 # This needs to be refactored to use the new AliasManager stuff.
187 if args[0] in ip.alias_manager:
187 if args[0] in ip.alias_manager:
188 name = args[0]
188 name = args[0]
189 nargs, cmd = ip.alias_manager.alias_table[ name ]
189 nargs, cmd = ip.alias_manager.alias_table[ name ]
190 staliases = db.get('stored_aliases',{})
190 staliases = db.get('stored_aliases',{})
191 staliases[ name ] = cmd
191 staliases[ name ] = cmd
192 db['stored_aliases'] = staliases
192 db['stored_aliases'] = staliases
193 print "Alias stored: %s (%s)" % (name, cmd)
193 print "Alias stored: %s (%s)" % (name, cmd)
194 return
194 return
195 else:
195 else:
196 raise UsageError("Unknown variable '%s'" % args[0])
196 raise UsageError("Unknown variable '%s'" % args[0])
197
197
198 else:
198 else:
199 if isinstance(inspect.getmodule(obj), FakeModule):
199 if isinstance(inspect.getmodule(obj), FakeModule):
200 print textwrap.dedent("""\
200 print textwrap.dedent("""\
201 Warning:%s is %s
201 Warning:%s is %s
202 Proper storage of interactively declared classes (or instances
202 Proper storage of interactively declared classes (or instances
203 of those classes) is not possible! Only instances
203 of those classes) is not possible! Only instances
204 of classes in real modules on file system can be %%store'd.
204 of classes in real modules on file system can be %%store'd.
205 """ % (args[0], obj) )
205 """ % (args[0], obj) )
206 return
206 return
207 #pickled = pickle.dumps(obj)
207 #pickled = pickle.dumps(obj)
208 db[ 'autorestore/' + args[0] ] = obj
208 db[ 'autorestore/' + args[0] ] = obj
209 print "Stored '%s' (%s)" % (args[0], obj.__class__.__name__)
209 print "Stored '%s' (%s)" % (args[0], obj.__class__.__name__)
210
210
211
211
212 def load_ipython_extension(ip):
212 def load_ipython_extension(ip):
213 """Load the extension in IPython."""
213 """Load the extension in IPython."""
214 if 'storemagic' not in ip.extension_manager.loaded:
214 ip.register_magics(StoreMagics)
215 ip.register_magics(StoreMagics)
General Comments 0
You need to be logged in to leave comments. Login now