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