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") |
@@ -23,8 +23,12 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 |
@@ -45,9 +49,10 class ExtensionManager(Configurable): | |||
|
45 | 49 | that point, including defining new magic and aliases, adding new |
|
46 | 50 | components, etc. |
|
47 | 51 | |
|
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. | |
|
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, |
@@ -63,6 +68,7 class ExtensionManager(Configurable): | |||
|
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( |
@@ -80,26 +86,43 class ExtensionManager(Configurable): | |||
|
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 |
|
|
|
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. |
@@ -111,21 +134,25 class ExtensionManager(Configurable): | |||
|
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: | |
|
137 | if (module_str in self.loaded) and (module_str in sys.modules): | |
|
138 | self.unload_extension(module_str) | |
|
116 | 139 |
|
|
140 | with prepended_to_syspath(self.ipython_extension_dir): | |
|
117 | 141 | reload(mod) |
|
118 |
|
|
|
142 | if self._call_load_ipython_extension(mod): | |
|
143 | self.loaded.add(module_str) | |
|
119 | 144 |
|
|
120 | 145 |
|
|
121 | 146 | |
|
122 | 147 | def _call_load_ipython_extension(self, mod): |
|
123 | 148 | if hasattr(mod, 'load_ipython_extension'): |
|
124 |
|
|
|
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 |
|
|
|
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. |
@@ -59,14 +59,30 class ExtensionMagics(Magics): | |||
|
59 | 59 | """Load an IPython extension by its module name.""" |
|
60 | 60 | if not module_str: |
|
61 | 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 | 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): |
@@ -514,14 +514,8 class AutoreloadMagics(Magics): | |||
|
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 | 519 |
|
|
525 | 520 |
|
|
526 | 521 |
|
|
527 | _loaded = True |
@@ -273,11 +273,7 class CythonMagics(Magics): | |||
|
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 | 279 |
|
|
283 | _loaded = True |
@@ -362,10 +362,6 __doc__ = __doc__.format( | |||
|
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 | 367 |
|
|
371 | _loaded = True |
@@ -588,10 +588,6 __doc__ = __doc__.format( | |||
|
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 | 593 |
|
|
597 | _loaded = True |
@@ -209,12 +209,6 class StoreMagics(Magics): | |||
|
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 | 214 |
|
|
220 | _loaded = True |
General Comments 0
You need to be logged in to leave comments.
Login now