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