##// 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")
@@ -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 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.
@@ -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 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)
142 if self._call_load_ipython_extension(mod):
143 self.loaded.add(module_str)
119 144 else:
120 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.
@@ -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 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):
@@ -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 auto_reload = AutoreloadMagics(ip)
525 520 ip.register_magics(auto_reload)
526 521 ip.set_hook('pre_run_code_hook', auto_reload.pre_run_code_hook)
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 ip.register_magics(CythonMagics)
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 ip.register_magics(OctaveMagics)
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 ip.register_magics(RMagics)
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 ip.register_magics(StoreMagics)
220 _loaded = True
General Comments 0
You need to be logged in to leave comments. Login now