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