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