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