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