Show More
@@ -0,0 +1,73 b'' | |||||
|
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") |
@@ -23,8 +23,12 b' import sys' | |||||
23 | from urllib import urlretrieve |
|
23 | from urllib import urlretrieve | |
24 | from urlparse import urlparse |
|
24 | from urlparse import urlparse | |
25 |
|
25 | |||
|
26 | from IPython.core.error import UsageError | |||
26 | from IPython.config.configurable import Configurable |
|
27 | from IPython.config.configurable import Configurable | |
27 | from IPython.utils.traitlets import Instance |
|
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 | # Main class |
|
34 | # Main class | |
@@ -44,10 +48,11 b' class ExtensionManager(Configurable):' | |||||
44 | the only argument. You can do anything you want with IPython at |
|
48 | the only argument. You can do anything you want with IPython at | |
45 | that point, including defining new magic and aliases, adding new |
|
49 | that point, including defining new magic and aliases, adding new | |
46 | components, etc. |
|
50 | components, etc. | |
47 |
|
51 | |||
48 | The :func:`load_ipython_extension` will be called again is you |
|
52 | You can also optionaly define an :func:`unload_ipython_extension(ipython)` | |
49 | load or reload the extension again. It is up to the extension |
|
53 | function, which will be called if the user unloads or reloads the extension. | |
50 | author to add code to manage that. |
|
54 | The extension manager will only call :func:`load_ipython_extension` again | |
|
55 | if the extension is reloaded. | |||
51 |
|
56 | |||
52 | You can put your extension modules anywhere you want, as long as |
|
57 | You can put your extension modules anywhere you want, as long as | |
53 | they can be imported by Python's standard import mechanism. However, |
|
58 | they can be imported by Python's standard import mechanism. However, | |
@@ -63,6 +68,7 b' class ExtensionManager(Configurable):' | |||||
63 | self.shell.on_trait_change( |
|
68 | self.shell.on_trait_change( | |
64 | self._on_ipython_dir_changed, 'ipython_dir' |
|
69 | self._on_ipython_dir_changed, 'ipython_dir' | |
65 | ) |
|
70 | ) | |
|
71 | self.loaded = set() | |||
66 |
|
72 | |||
67 | def __del__(self): |
|
73 | def __del__(self): | |
68 | self.shell.on_trait_change( |
|
74 | self.shell.on_trait_change( | |
@@ -80,26 +86,43 b' class ExtensionManager(Configurable):' | |||||
80 | def load_extension(self, module_str): |
|
86 | def load_extension(self, module_str): | |
81 | """Load an IPython extension by its module name. |
|
87 | """Load an IPython extension by its module name. | |
82 |
|
88 | |||
83 | If :func:`load_ipython_extension` returns anything, this function |
|
89 | Returns the string "already loaded" if the extension is already loaded, | |
84 | will return that object. |
|
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 | from IPython.utils.syspathcontext import prepended_to_syspath |
|
96 | from IPython.utils.syspathcontext import prepended_to_syspath | |
87 |
|
97 | |||
88 | if module_str not in sys.modules: |
|
98 | if module_str not in sys.modules: | |
89 | with prepended_to_syspath(self.ipython_extension_dir): |
|
99 | with prepended_to_syspath(self.ipython_extension_dir): | |
90 | __import__(module_str) |
|
100 | __import__(module_str) | |
91 | mod = sys.modules[module_str] |
|
101 | mod = sys.modules[module_str] | |
92 |
|
|
102 | if self._call_load_ipython_extension(mod): | |
|
103 | self.loaded.add(module_str) | |||
|
104 | else: | |||
|
105 | return "no load function" | |||
93 |
|
106 | |||
94 | def unload_extension(self, module_str): |
|
107 | def unload_extension(self, module_str): | |
95 | """Unload an IPython extension by its module name. |
|
108 | """Unload an IPython extension by its module name. | |
96 |
|
109 | |||
97 | This function looks up the extension's name in ``sys.modules`` and |
|
110 | This function looks up the extension's name in ``sys.modules`` and | |
98 | simply calls ``mod.unload_ipython_extension(self)``. |
|
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 | if module_str in sys.modules: |
|
120 | if module_str in sys.modules: | |
101 | mod = sys.modules[module_str] |
|
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 | def reload_extension(self, module_str): |
|
127 | def reload_extension(self, module_str): | |
105 | """Reload an IPython extension by calling reload. |
|
128 | """Reload an IPython extension by calling reload. | |
@@ -111,21 +134,25 b' class ExtensionManager(Configurable):' | |||||
111 | """ |
|
134 | """ | |
112 | from IPython.utils.syspathcontext import prepended_to_syspath |
|
135 | from IPython.utils.syspathcontext import prepended_to_syspath | |
113 |
|
136 | |||
114 | with prepended_to_syspath(self.ipython_extension_dir): |
|
137 | if (module_str in self.loaded) and (module_str in sys.modules): | |
115 | if module_str in sys.modules: |
|
138 | self.unload_extension(module_str) | |
116 |
|
|
139 | mod = sys.modules[module_str] | |
|
140 | with prepended_to_syspath(self.ipython_extension_dir): | |||
117 | reload(mod) |
|
141 | reload(mod) | |
118 |
|
|
142 | if self._call_load_ipython_extension(mod): | |
119 | else: |
|
143 | self.loaded.add(module_str) | |
120 | self.load_extension(module_str) |
|
144 | else: | |
|
145 | self.load_extension(module_str) | |||
121 |
|
146 | |||
122 | def _call_load_ipython_extension(self, mod): |
|
147 | def _call_load_ipython_extension(self, mod): | |
123 | if hasattr(mod, 'load_ipython_extension'): |
|
148 | if hasattr(mod, 'load_ipython_extension'): | |
124 |
|
|
149 | mod.load_ipython_extension(self.shell) | |
|
150 | return True | |||
125 |
|
151 | |||
126 | def _call_unload_ipython_extension(self, mod): |
|
152 | def _call_unload_ipython_extension(self, mod): | |
127 | if hasattr(mod, 'unload_ipython_extension'): |
|
153 | if hasattr(mod, 'unload_ipython_extension'): | |
128 |
|
|
154 | mod.unload_ipython_extension(self.shell) | |
|
155 | return True | |||
129 |
|
156 | |||
130 | def install_extension(self, url, filename=None): |
|
157 | def install_extension(self, url, filename=None): | |
131 | """Download and install an IPython extension. |
|
158 | """Download and install an IPython extension. |
@@ -59,14 +59,30 b' class ExtensionMagics(Magics):' | |||||
59 | """Load an IPython extension by its module name.""" |
|
59 | """Load an IPython extension by its module name.""" | |
60 | if not module_str: |
|
60 | if not module_str: | |
61 | raise UsageError('Missing module name.') |
|
61 | raise UsageError('Missing module name.') | |
62 |
re |
|
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 | @line_magic |
|
70 | @line_magic | |
65 | def unload_ext(self, module_str): |
|
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 | if not module_str: |
|
77 | if not module_str: | |
68 | raise UsageError('Missing module name.') |
|
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 | @line_magic |
|
87 | @line_magic | |
72 | def reload_ext(self, module_str): |
|
88 | def reload_ext(self, module_str): |
@@ -514,14 +514,8 b' class AutoreloadMagics(Magics):' | |||||
514 | pass |
|
514 | pass | |
515 |
|
515 | |||
516 |
|
516 | |||
517 | _loaded = False |
|
|||
518 |
|
||||
519 |
|
||||
520 | def load_ipython_extension(ip): |
|
517 | def load_ipython_extension(ip): | |
521 | """Load the extension in IPython.""" |
|
518 | """Load the extension in IPython.""" | |
522 | global _loaded |
|
519 | auto_reload = AutoreloadMagics(ip) | |
523 | if not _loaded: |
|
520 | ip.register_magics(auto_reload) | |
524 | auto_reload = AutoreloadMagics(ip) |
|
521 | ip.set_hook('pre_run_code_hook', auto_reload.pre_run_code_hook) | |
525 | ip.register_magics(auto_reload) |
|
|||
526 | ip.set_hook('pre_run_code_hook', auto_reload.pre_run_code_hook) |
|
|||
527 | _loaded = True |
|
@@ -273,11 +273,7 b' class CythonMagics(Magics):' | |||||
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 | _loaded = False |
|
|||
277 |
|
276 | |||
278 | def load_ipython_extension(ip): |
|
277 | def load_ipython_extension(ip): | |
279 | """Load the extension in IPython.""" |
|
278 | """Load the extension in IPython.""" | |
280 | global _loaded |
|
279 | ip.register_magics(CythonMagics) | |
281 | if not _loaded: |
|
|||
282 | ip.register_magics(CythonMagics) |
|
|||
283 | _loaded = True |
|
@@ -362,10 +362,6 b' __doc__ = __doc__.format(' | |||||
362 | ) |
|
362 | ) | |
363 |
|
363 | |||
364 |
|
364 | |||
365 | _loaded = False |
|
|||
366 | def load_ipython_extension(ip): |
|
365 | def load_ipython_extension(ip): | |
367 | """Load the extension in IPython.""" |
|
366 | """Load the extension in IPython.""" | |
368 | global _loaded |
|
367 | ip.register_magics(OctaveMagics) | |
369 | if not _loaded: |
|
|||
370 | ip.register_magics(OctaveMagics) |
|
|||
371 | _loaded = True |
|
@@ -588,10 +588,6 b' __doc__ = __doc__.format(' | |||||
588 | ) |
|
588 | ) | |
589 |
|
589 | |||
590 |
|
590 | |||
591 | _loaded = False |
|
|||
592 | def load_ipython_extension(ip): |
|
591 | def load_ipython_extension(ip): | |
593 | """Load the extension in IPython.""" |
|
592 | """Load the extension in IPython.""" | |
594 | global _loaded |
|
593 | ip.register_magics(RMagics) | |
595 | if not _loaded: |
|
|||
596 | ip.register_magics(RMagics) |
|
|||
597 | _loaded = True |
|
@@ -209,12 +209,6 b' class StoreMagics(Magics):' | |||||
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 | _loaded = False |
|
|||
213 |
|
||||
214 |
|
||||
215 | def load_ipython_extension(ip): |
|
212 | def load_ipython_extension(ip): | |
216 | """Load the extension in IPython.""" |
|
213 | """Load the extension in IPython.""" | |
217 | global _loaded |
|
214 | ip.register_magics(StoreMagics) | |
218 | if not _loaded: |
|
|||
219 | ip.register_magics(StoreMagics) |
|
|||
220 | _loaded = True |
|
General Comments 0
You need to be logged in to leave comments.
Login now