##// END OF EJS Templates
remove unregister trait-change callback in ExtensionManager.__del__...
Min RK -
Show More
@@ -1,179 +1,174 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 # Copyright (c) IPython Development Team.
4 # Copyright (c) IPython Development Team.
5 # Distributed under the terms of the Modified BSD License.
5 # Distributed under the terms of the Modified BSD License.
6
6
7 import os
7 import os
8 from shutil import copyfile
8 from shutil import copyfile
9 import sys
9 import sys
10
10
11 from traitlets.config.configurable import Configurable
11 from traitlets.config.configurable import Configurable
12 from IPython.utils.path import ensure_dir_exists
12 from IPython.utils.path import ensure_dir_exists
13 from traitlets import Instance
13 from traitlets import Instance
14
14
15 try:
15 try:
16 from importlib import reload
16 from importlib import reload
17 except ImportError :
17 except ImportError :
18 ## deprecated since 3.4
18 ## deprecated since 3.4
19 from imp import reload
19 from imp import reload
20
20
21 #-----------------------------------------------------------------------------
21 #-----------------------------------------------------------------------------
22 # Main class
22 # Main class
23 #-----------------------------------------------------------------------------
23 #-----------------------------------------------------------------------------
24
24
25 class ExtensionManager(Configurable):
25 class ExtensionManager(Configurable):
26 """A class to manage IPython extensions.
26 """A class to manage IPython extensions.
27
27
28 An IPython extension is an importable Python module that has
28 An IPython extension is an importable Python module that has
29 a function with the signature::
29 a function with the signature::
30
30
31 def load_ipython_extension(ipython):
31 def load_ipython_extension(ipython):
32 # Do things with ipython
32 # Do things with ipython
33
33
34 This function is called after your extension is imported and the
34 This function is called after your extension is imported and the
35 currently active :class:`InteractiveShell` instance is passed as
35 currently active :class:`InteractiveShell` instance is passed as
36 the only argument. You can do anything you want with IPython at
36 the only argument. You can do anything you want with IPython at
37 that point, including defining new magic and aliases, adding new
37 that point, including defining new magic and aliases, adding new
38 components, etc.
38 components, etc.
39
39
40 You can also optionally define an :func:`unload_ipython_extension(ipython)`
40 You can also optionally define an :func:`unload_ipython_extension(ipython)`
41 function, which will be called if the user unloads or reloads the extension.
41 function, which will be called if the user unloads or reloads the extension.
42 The extension manager will only call :func:`load_ipython_extension` again
42 The extension manager will only call :func:`load_ipython_extension` again
43 if the extension is reloaded.
43 if the extension is reloaded.
44
44
45 You can put your extension modules anywhere you want, as long as
45 You can put your extension modules anywhere you want, as long as
46 they can be imported by Python's standard import mechanism. However,
46 they can be imported by Python's standard import mechanism. However,
47 to make it easy to write extensions, you can also put your extensions
47 to make it easy to write extensions, you can also put your extensions
48 in ``os.path.join(self.ipython_dir, 'extensions')``. This directory
48 in ``os.path.join(self.ipython_dir, 'extensions')``. This directory
49 is added to ``sys.path`` automatically.
49 is added to ``sys.path`` automatically.
50 """
50 """
51
51
52 shell = Instance('IPython.core.interactiveshell.InteractiveShellABC',
52 shell = Instance('IPython.core.interactiveshell.InteractiveShellABC',
53 allow_none=True)
53 allow_none=True)
54
54
55 def __init__(self, shell=None, **kwargs):
55 def __init__(self, shell=None, **kwargs):
56 super(ExtensionManager, self).__init__(shell=shell, **kwargs)
56 super(ExtensionManager, self).__init__(shell=shell, **kwargs)
57 self.shell.on_trait_change(
57 self.shell.on_trait_change(
58 self._on_ipython_dir_changed, 'ipython_dir'
58 self._on_ipython_dir_changed, 'ipython_dir'
59 )
59 )
60 self.loaded = set()
60 self.loaded = set()
61
61
62 def __del__(self):
63 self.shell.on_trait_change(
64 self._on_ipython_dir_changed, 'ipython_dir', remove=True
65 )
66
67 @property
62 @property
68 def ipython_extension_dir(self):
63 def ipython_extension_dir(self):
69 return os.path.join(self.shell.ipython_dir, u'extensions')
64 return os.path.join(self.shell.ipython_dir, u'extensions')
70
65
71 def _on_ipython_dir_changed(self):
66 def _on_ipython_dir_changed(self):
72 ensure_dir_exists(self.ipython_extension_dir)
67 ensure_dir_exists(self.ipython_extension_dir)
73
68
74 def load_extension(self, module_str):
69 def load_extension(self, module_str):
75 """Load an IPython extension by its module name.
70 """Load an IPython extension by its module name.
76
71
77 Returns the string "already loaded" if the extension is already loaded,
72 Returns the string "already loaded" if the extension is already loaded,
78 "no load function" if the module doesn't have a load_ipython_extension
73 "no load function" if the module doesn't have a load_ipython_extension
79 function, or None if it succeeded.
74 function, or None if it succeeded.
80 """
75 """
81 if module_str in self.loaded:
76 if module_str in self.loaded:
82 return "already loaded"
77 return "already loaded"
83
78
84 from IPython.utils.syspathcontext import prepended_to_syspath
79 from IPython.utils.syspathcontext import prepended_to_syspath
85
80
86 with self.shell.builtin_trap:
81 with self.shell.builtin_trap:
87 if module_str not in sys.modules:
82 if module_str not in sys.modules:
88 with prepended_to_syspath(self.ipython_extension_dir):
83 with prepended_to_syspath(self.ipython_extension_dir):
89 __import__(module_str)
84 __import__(module_str)
90 mod = sys.modules[module_str]
85 mod = sys.modules[module_str]
91 if self._call_load_ipython_extension(mod):
86 if self._call_load_ipython_extension(mod):
92 self.loaded.add(module_str)
87 self.loaded.add(module_str)
93 else:
88 else:
94 return "no load function"
89 return "no load function"
95
90
96 def unload_extension(self, module_str):
91 def unload_extension(self, module_str):
97 """Unload an IPython extension by its module name.
92 """Unload an IPython extension by its module name.
98
93
99 This function looks up the extension's name in ``sys.modules`` and
94 This function looks up the extension's name in ``sys.modules`` and
100 simply calls ``mod.unload_ipython_extension(self)``.
95 simply calls ``mod.unload_ipython_extension(self)``.
101
96
102 Returns the string "no unload function" if the extension doesn't define
97 Returns the string "no unload function" if the extension doesn't define
103 a function to unload itself, "not loaded" if the extension isn't loaded,
98 a function to unload itself, "not loaded" if the extension isn't loaded,
104 otherwise None.
99 otherwise None.
105 """
100 """
106 if module_str not in self.loaded:
101 if module_str not in self.loaded:
107 return "not loaded"
102 return "not loaded"
108
103
109 if module_str in sys.modules:
104 if module_str in sys.modules:
110 mod = sys.modules[module_str]
105 mod = sys.modules[module_str]
111 if self._call_unload_ipython_extension(mod):
106 if self._call_unload_ipython_extension(mod):
112 self.loaded.discard(module_str)
107 self.loaded.discard(module_str)
113 else:
108 else:
114 return "no unload function"
109 return "no unload function"
115
110
116 def reload_extension(self, module_str):
111 def reload_extension(self, module_str):
117 """Reload an IPython extension by calling reload.
112 """Reload an IPython extension by calling reload.
118
113
119 If the module has not been loaded before,
114 If the module has not been loaded before,
120 :meth:`InteractiveShell.load_extension` is called. Otherwise
115 :meth:`InteractiveShell.load_extension` is called. Otherwise
121 :func:`reload` is called and then the :func:`load_ipython_extension`
116 :func:`reload` is called and then the :func:`load_ipython_extension`
122 function of the module, if it exists is called.
117 function of the module, if it exists is called.
123 """
118 """
124 from IPython.utils.syspathcontext import prepended_to_syspath
119 from IPython.utils.syspathcontext import prepended_to_syspath
125
120
126 if (module_str in self.loaded) and (module_str in sys.modules):
121 if (module_str in self.loaded) and (module_str in sys.modules):
127 self.unload_extension(module_str)
122 self.unload_extension(module_str)
128 mod = sys.modules[module_str]
123 mod = sys.modules[module_str]
129 with prepended_to_syspath(self.ipython_extension_dir):
124 with prepended_to_syspath(self.ipython_extension_dir):
130 reload(mod)
125 reload(mod)
131 if self._call_load_ipython_extension(mod):
126 if self._call_load_ipython_extension(mod):
132 self.loaded.add(module_str)
127 self.loaded.add(module_str)
133 else:
128 else:
134 self.load_extension(module_str)
129 self.load_extension(module_str)
135
130
136 def _call_load_ipython_extension(self, mod):
131 def _call_load_ipython_extension(self, mod):
137 if hasattr(mod, 'load_ipython_extension'):
132 if hasattr(mod, 'load_ipython_extension'):
138 mod.load_ipython_extension(self.shell)
133 mod.load_ipython_extension(self.shell)
139 return True
134 return True
140
135
141 def _call_unload_ipython_extension(self, mod):
136 def _call_unload_ipython_extension(self, mod):
142 if hasattr(mod, 'unload_ipython_extension'):
137 if hasattr(mod, 'unload_ipython_extension'):
143 mod.unload_ipython_extension(self.shell)
138 mod.unload_ipython_extension(self.shell)
144 return True
139 return True
145
140
146 def install_extension(self, url, filename=None):
141 def install_extension(self, url, filename=None):
147 """Download and install an IPython extension.
142 """Download and install an IPython extension.
148
143
149 If filename is given, the file will be so named (inside the extension
144 If filename is given, the file will be so named (inside the extension
150 directory). Otherwise, the name from the URL will be used. The file must
145 directory). Otherwise, the name from the URL will be used. The file must
151 have a .py or .zip extension; otherwise, a ValueError will be raised.
146 have a .py or .zip extension; otherwise, a ValueError will be raised.
152
147
153 Returns the full path to the installed file.
148 Returns the full path to the installed file.
154 """
149 """
155 # Ensure the extension directory exists
150 # Ensure the extension directory exists
156 ensure_dir_exists(self.ipython_extension_dir)
151 ensure_dir_exists(self.ipython_extension_dir)
157
152
158 if os.path.isfile(url):
153 if os.path.isfile(url):
159 src_filename = os.path.basename(url)
154 src_filename = os.path.basename(url)
160 copy = copyfile
155 copy = copyfile
161 else:
156 else:
162 # Deferred imports
157 # Deferred imports
163 try:
158 try:
164 from urllib.parse import urlparse # Py3
159 from urllib.parse import urlparse # Py3
165 from urllib.request import urlretrieve
160 from urllib.request import urlretrieve
166 except ImportError:
161 except ImportError:
167 from urlparse import urlparse
162 from urlparse import urlparse
168 from urllib import urlretrieve
163 from urllib import urlretrieve
169 src_filename = urlparse(url).path.split('/')[-1]
164 src_filename = urlparse(url).path.split('/')[-1]
170 copy = urlretrieve
165 copy = urlretrieve
171
166
172 if filename is None:
167 if filename is None:
173 filename = src_filename
168 filename = src_filename
174 if os.path.splitext(filename)[1] not in ('.py', '.zip'):
169 if os.path.splitext(filename)[1] not in ('.py', '.zip'):
175 raise ValueError("The file must have a .py or .zip extension", filename)
170 raise ValueError("The file must have a .py or .zip extension", filename)
176
171
177 filename = os.path.join(self.ipython_extension_dir, filename)
172 filename = os.path.join(self.ipython_extension_dir, filename)
178 copy(url, filename)
173 copy(url, filename)
179 return filename
174 return filename
General Comments 0
You need to be logged in to leave comments. Login now