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