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