##// END OF EJS Templates
Add message when trying to %load_ext a module that is not an IPython extension.
Thomas Kluyver -
Show More
@@ -1,181 +1,184 b''
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 26 from IPython.core.error import UsageError
27 27 from IPython.config.configurable import Configurable
28 28 from IPython.utils.traitlets import Instance
29 29 from IPython.utils.py3compat import PY3
30 30 if PY3:
31 31 from imp import reload
32 32
33 33 #-----------------------------------------------------------------------------
34 34 # Main class
35 35 #-----------------------------------------------------------------------------
36 36
37 37 class ExtensionManager(Configurable):
38 38 """A class to manage IPython extensions.
39 39
40 40 An IPython extension is an importable Python module that has
41 41 a function with the signature::
42 42
43 43 def load_ipython_extension(ipython):
44 44 # Do things with ipython
45 45
46 46 This function is called after your extension is imported and the
47 47 currently active :class:`InteractiveShell` instance is passed as
48 48 the only argument. You can do anything you want with IPython at
49 49 that point, including defining new magic and aliases, adding new
50 50 components, etc.
51 51
52 52 You can also optionaly define an :func:`unload_ipython_extension(ipython)`
53 53 function, which will be called if the user unloads or reloads the extension.
54 54 The extension manager will only call :func:`load_ipython_extension` again
55 55 if the extension is reloaded.
56 56
57 57 You can put your extension modules anywhere you want, as long as
58 58 they can be imported by Python's standard import mechanism. However,
59 59 to make it easy to write extensions, you can also put your extensions
60 60 in ``os.path.join(self.ipython_dir, 'extensions')``. This directory
61 61 is added to ``sys.path`` automatically.
62 62 """
63 63
64 64 shell = Instance('IPython.core.interactiveshell.InteractiveShellABC')
65 65
66 66 def __init__(self, shell=None, config=None):
67 67 super(ExtensionManager, self).__init__(shell=shell, config=config)
68 68 self.shell.on_trait_change(
69 69 self._on_ipython_dir_changed, 'ipython_dir'
70 70 )
71 71 self.loaded = set()
72 72
73 73 def __del__(self):
74 74 self.shell.on_trait_change(
75 75 self._on_ipython_dir_changed, 'ipython_dir', remove=True
76 76 )
77 77
78 78 @property
79 79 def ipython_extension_dir(self):
80 80 return os.path.join(self.shell.ipython_dir, u'extensions')
81 81
82 82 def _on_ipython_dir_changed(self):
83 83 if not os.path.isdir(self.ipython_extension_dir):
84 84 os.makedirs(self.ipython_extension_dir, mode = 0o777)
85 85
86 86 def load_extension(self, module_str):
87 87 """Load an IPython extension by its module name.
88 88
89 89 Returns the string "already loaded" if the extension is already loaded,
90 otherwise None.
90 "no load function" if the module doesn't have a load_ipython_extension
91 function, or None if it succeeded.
91 92 """
92 93 if module_str in self.loaded:
93 94 return "already loaded"
94 95
95 96 from IPython.utils.syspathcontext import prepended_to_syspath
96 97
97 98 if module_str not in sys.modules:
98 99 with prepended_to_syspath(self.ipython_extension_dir):
99 100 __import__(module_str)
100 101 mod = sys.modules[module_str]
101 102 if self._call_load_ipython_extension(mod):
102 103 self.loaded.add(module_str)
104 else:
105 return "no load function"
103 106
104 107 def unload_extension(self, module_str):
105 108 """Unload an IPython extension by its module name.
106 109
107 110 This function looks up the extension's name in ``sys.modules`` and
108 111 simply calls ``mod.unload_ipython_extension(self)``.
109 112
110 113 Returns the string "no unload function" if the extension doesn't define
111 114 a function to unload itself, "not loaded" if the extension isn't loaded,
112 115 otherwise None.
113 116 """
114 117 if module_str not in self.loaded:
115 118 return "not loaded"
116 119
117 120 if module_str in sys.modules:
118 121 mod = sys.modules[module_str]
119 122 if self._call_unload_ipython_extension(mod):
120 123 self.loaded.discard(module_str)
121 124 else:
122 125 return "no unload function"
123 126
124 127 def reload_extension(self, module_str):
125 128 """Reload an IPython extension by calling reload.
126 129
127 130 If the module has not been loaded before,
128 131 :meth:`InteractiveShell.load_extension` is called. Otherwise
129 132 :func:`reload` is called and then the :func:`load_ipython_extension`
130 133 function of the module, if it exists is called.
131 134 """
132 135 from IPython.utils.syspathcontext import prepended_to_syspath
133 136
134 137 if (module_str in self.loaded) and (module_str in sys.modules):
135 138 self.unload_extension(module_str)
136 139 mod = sys.modules[module_str]
137 140 with prepended_to_syspath(self.ipython_extension_dir):
138 141 reload(mod)
139 142 if self._call_load_ipython_extension(mod):
140 143 self.loaded.add(module_str)
141 144 else:
142 145 self.load_extension(module_str)
143 146
144 147 def _call_load_ipython_extension(self, mod):
145 148 if hasattr(mod, 'load_ipython_extension'):
146 149 mod.load_ipython_extension(self.shell)
147 150 return True
148 151
149 152 def _call_unload_ipython_extension(self, mod):
150 153 if hasattr(mod, 'unload_ipython_extension'):
151 154 mod.unload_ipython_extension(self.shell)
152 155 return True
153 156
154 157 def install_extension(self, url, filename=None):
155 158 """Download and install an IPython extension.
156 159
157 160 If filename is given, the file will be so named (inside the extension
158 161 directory). Otherwise, the name from the URL will be used. The file must
159 162 have a .py or .zip extension; otherwise, a ValueError will be raised.
160 163
161 164 Returns the full path to the installed file.
162 165 """
163 166 # Ensure the extension directory exists
164 167 if not os.path.isdir(self.ipython_extension_dir):
165 168 os.makedirs(self.ipython_extension_dir, mode = 0o777)
166 169
167 170 if os.path.isfile(url):
168 171 src_filename = os.path.basename(url)
169 172 copy = copyfile
170 173 else:
171 174 src_filename = urlparse(url).path.split('/')[-1]
172 175 copy = urlretrieve
173 176
174 177 if filename is None:
175 178 filename = src_filename
176 179 if os.path.splitext(filename)[1] not in ('.py', '.zip'):
177 180 raise ValueError("The file must have a .py or .zip extension", filename)
178 181
179 182 filename = os.path.join(self.ipython_extension_dir, filename)
180 183 copy(url, filename)
181 184 return filename
@@ -1,88 +1,92 b''
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 if self.shell.extension_manager.load_extension(module_str) == 'already loaded':
62 res = self.shell.extension_manager.load_extension(module_str)
63
64 if res == 'already loaded':
63 65 print "The %s extension is already loaded. To reload it, use:" % module_str
64 66 print " %reload_ext", module_str
67 elif res == 'no load function':
68 print "The %s module is not an IPython extension." % module_str
65 69
66 70 @line_magic
67 71 def unload_ext(self, module_str):
68 72 """Unload an IPython extension by its module name.
69 73
70 74 Not all extensions can be unloaded, only those which define an
71 75 ``unload_ipython_extension`` function.
72 76 """
73 77 if not module_str:
74 78 raise UsageError('Missing module name.')
75 79
76 80 res = self.shell.extension_manager.unload_extension(module_str)
77 81
78 82 if res == 'no unload function':
79 83 print "The %s extension doesn't define how to unload it." % module_str
80 84 elif res == "not loaded":
81 85 print "The %s extension is not loaded." % module_str
82 86
83 87 @line_magic
84 88 def reload_ext(self, module_str):
85 89 """Reload an IPython extension by its module name."""
86 90 if not module_str:
87 91 raise UsageError('Missing module name.')
88 92 self.shell.extension_manager.reload_extension(module_str)
@@ -1,69 +1,73 b''
1 1 import os.path
2 2
3 3 import nose.tools as nt
4 4
5 5 import IPython.testing.tools as tt
6 6 from IPython.utils.syspathcontext import prepended_to_syspath
7 7 from IPython.utils.tempdir import TemporaryDirectory
8 8
9 9 ext1_content = """
10 10 def load_ipython_extension(ip):
11 11 print("Running ext1 load")
12 12
13 13 def unload_ipython_extension(ip):
14 14 print("Running ext1 unload")
15 15 """
16 16
17 17 ext2_content = """
18 18 def load_ipython_extension(ip):
19 19 print("Running ext2 load")
20 20 """
21 21
22 22 def test_extension_loading():
23 23 em = get_ipython().extension_manager
24 24 with TemporaryDirectory() as td:
25 25 ext1 = os.path.join(td, 'ext1.py')
26 26 with open(ext1, 'w') as f:
27 27 f.write(ext1_content)
28 28
29 29 ext2 = os.path.join(td, 'ext2.py')
30 30 with open(ext2, 'w') as f:
31 31 f.write(ext2_content)
32 32
33 33 with prepended_to_syspath(td):
34 34 assert 'ext1' not in em.loaded
35 35 assert 'ext2' not in em.loaded
36 36
37 37 # Load extension
38 38 with tt.AssertPrints("Running ext1 load"):
39 39 assert em.load_extension('ext1') is None
40 40 assert 'ext1' in em.loaded
41 41
42 42 # Should refuse to load it again
43 43 with tt.AssertNotPrints("Running ext1 load"):
44 44 assert em.load_extension('ext1') == 'already loaded'
45 45
46 46 # Reload
47 47 with tt.AssertPrints("Running ext1 unload"):
48 48 with tt.AssertPrints("Running ext1 load", suppress=False):
49 49 em.reload_extension('ext1')
50 50
51 51 # Unload
52 52 with tt.AssertPrints("Running ext1 unload"):
53 53 assert em.unload_extension('ext1') is None
54 54
55 55 # Can't unload again
56 56 with tt.AssertNotPrints("Running ext1 unload"):
57 57 assert em.unload_extension('ext1') == 'not loaded'
58 58 assert em.unload_extension('ext2') == 'not loaded'
59 59
60 60 # Load extension 2
61 61 with tt.AssertPrints("Running ext2 load"):
62 62 assert em.load_extension('ext2') is None
63 63
64 64 # Can't unload this
65 65 assert em.unload_extension('ext2') == 'no unload function'
66 66
67 67 # But can reload it
68 68 with tt.AssertPrints("Running ext2 load"):
69 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")
General Comments 0
You need to be logged in to leave comments. Login now