##// END OF EJS Templates
Extension manager refuses to load extension more than once
Thomas Kluyver -
Show More
@@ -1,164 +1,178 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 from IPython.core.error import UsageError
26 27 from IPython.config.configurable import Configurable
27 28 from IPython.utils.traitlets import Instance
28 29
29 30 #-----------------------------------------------------------------------------
30 31 # Main class
31 32 #-----------------------------------------------------------------------------
32 33
33 34 class ExtensionManager(Configurable):
34 35 """A class to manage IPython extensions.
35 36
36 37 An IPython extension is an importable Python module that has
37 38 a function with the signature::
38 39
39 40 def load_ipython_extension(ipython):
40 41 # Do things with ipython
41 42
42 43 This function is called after your extension is imported and the
43 44 currently active :class:`InteractiveShell` instance is passed as
44 45 the only argument. You can do anything you want with IPython at
45 46 that point, including defining new magic and aliases, adding new
46 47 components, etc.
47
48 The :func:`load_ipython_extension` will be called again is you
49 load or reload the extension again. It is up to the extension
50 author to add code to manage that.
48
49 You can also optionaly define an :func:`unload_ipython_extension(ipython)`
50 function, which will be called if the user unloads or reloads the extension.
51 The extension manager will only call :func:`load_ipython_extension` again
52 if the extension is reloaded.
51 53
52 54 You can put your extension modules anywhere you want, as long as
53 55 they can be imported by Python's standard import mechanism. However,
54 56 to make it easy to write extensions, you can also put your extensions
55 57 in ``os.path.join(self.ipython_dir, 'extensions')``. This directory
56 58 is added to ``sys.path`` automatically.
57 59 """
58 60
59 61 shell = Instance('IPython.core.interactiveshell.InteractiveShellABC')
60 62
61 63 def __init__(self, shell=None, config=None):
62 64 super(ExtensionManager, self).__init__(shell=shell, config=config)
63 65 self.shell.on_trait_change(
64 66 self._on_ipython_dir_changed, 'ipython_dir'
65 67 )
66 68 self.loaded = set()
67 69
68 70 def __del__(self):
69 71 self.shell.on_trait_change(
70 72 self._on_ipython_dir_changed, 'ipython_dir', remove=True
71 73 )
72 74
73 75 @property
74 76 def ipython_extension_dir(self):
75 77 return os.path.join(self.shell.ipython_dir, u'extensions')
76 78
77 79 def _on_ipython_dir_changed(self):
78 80 if not os.path.isdir(self.ipython_extension_dir):
79 81 os.makedirs(self.ipython_extension_dir, mode = 0o777)
80 82
81 83 def load_extension(self, module_str):
82 84 """Load an IPython extension by its module name.
83 85
84 If :func:`load_ipython_extension` returns anything, this function
85 will return that object.
86 Returns the string "already loaded" if the extension is already loaded,
87 otherwise None.
86 88 """
89 if module_str in self.loaded:
90 return "already loaded"
91
87 92 from IPython.utils.syspathcontext import prepended_to_syspath
88 93
89 94 if module_str not in sys.modules:
90 95 with prepended_to_syspath(self.ipython_extension_dir):
91 96 __import__(module_str)
92 97 mod = sys.modules[module_str]
93 98 if self._call_load_ipython_extension(mod):
94 99 self.loaded.add(module_str)
95 100
96 101 def unload_extension(self, module_str):
97 102 """Unload an IPython extension by its module name.
98 103
99 104 This function looks up the extension's name in ``sys.modules`` and
100 105 simply calls ``mod.unload_ipython_extension(self)``.
106
107 Returns the string "no unload function" if the extension doesn't define
108 a function to unload itself, "not loaded" if the extension isn't loaded,
109 otherwise None.
101 110 """
111 if module_str not in self.loaded:
112 return "not loaded"
113
102 114 if module_str in sys.modules:
103 115 mod = sys.modules[module_str]
104 116 if self._call_unload_ipython_extension(mod):
105 117 self.loaded.discard(module_str)
118 else:
119 return "no unload function"
106 120
107 121 def reload_extension(self, module_str):
108 122 """Reload an IPython extension by calling reload.
109 123
110 124 If the module has not been loaded before,
111 125 :meth:`InteractiveShell.load_extension` is called. Otherwise
112 126 :func:`reload` is called and then the :func:`load_ipython_extension`
113 127 function of the module, if it exists is called.
114 128 """
115 129 from IPython.utils.syspathcontext import prepended_to_syspath
116 130
117 131 if (module_str in self.loaded) and (module_str in sys.modules):
118 132 self.unload_extension(module_str)
119 133 mod = sys.modules[module_str]
120 134 with prepended_to_syspath(self.ipython_extension_dir):
121 135 reload(mod)
122 136 if self._call_load_ipython_extension(mod):
123 137 self.loaded.add(module_str)
124 138 else:
125 139 self.load_extension(module_str)
126 140
127 141 def _call_load_ipython_extension(self, mod):
128 142 if hasattr(mod, 'load_ipython_extension'):
129 143 mod.load_ipython_extension(self.shell)
130 144 return True
131 145
132 146 def _call_unload_ipython_extension(self, mod):
133 147 if hasattr(mod, 'unload_ipython_extension'):
134 148 mod.unload_ipython_extension(self.shell)
135 149 return True
136 150
137 151 def install_extension(self, url, filename=None):
138 152 """Download and install an IPython extension.
139 153
140 154 If filename is given, the file will be so named (inside the extension
141 155 directory). Otherwise, the name from the URL will be used. The file must
142 156 have a .py or .zip extension; otherwise, a ValueError will be raised.
143 157
144 158 Returns the full path to the installed file.
145 159 """
146 160 # Ensure the extension directory exists
147 161 if not os.path.isdir(self.ipython_extension_dir):
148 162 os.makedirs(self.ipython_extension_dir, mode = 0o777)
149 163
150 164 if os.path.isfile(url):
151 165 src_filename = os.path.basename(url)
152 166 copy = copyfile
153 167 else:
154 168 src_filename = urlparse(url).path.split('/')[-1]
155 169 copy = urlretrieve
156 170
157 171 if filename is None:
158 172 filename = src_filename
159 173 if os.path.splitext(filename)[1] not in ('.py', '.zip'):
160 174 raise ValueError("The file must have a .py or .zip extension", filename)
161 175
162 176 filename = os.path.join(self.ipython_extension_dir, filename)
163 177 copy(url, filename)
164 178 return filename
@@ -1,76 +1,88 b''
1 1 """Implementation of magic functions for the extension machinery.
2 2 """
3 3 #-----------------------------------------------------------------------------
4 4 # Copyright (c) 2012 The IPython Development Team.
5 5 #
6 6 # Distributed under the terms of the Modified BSD License.
7 7 #
8 8 # The full license is in the file COPYING.txt, distributed with this software.
9 9 #-----------------------------------------------------------------------------
10 10
11 11 #-----------------------------------------------------------------------------
12 12 # Imports
13 13 #-----------------------------------------------------------------------------
14 14
15 15 # Stdlib
16 16 import os
17 17
18 18 # Our own packages
19 19 from IPython.core.error import UsageError
20 20 from IPython.core.magic import Magics, magics_class, line_magic
21 21
22 22 #-----------------------------------------------------------------------------
23 23 # Magic implementation classes
24 24 #-----------------------------------------------------------------------------
25 25
26 26 @magics_class
27 27 class ExtensionMagics(Magics):
28 28 """Magics to manage the IPython extensions system."""
29 29
30 30 @line_magic
31 31 def install_ext(self, parameter_s=''):
32 32 """Download and install an extension from a URL, e.g.::
33 33
34 34 %install_ext https://bitbucket.org/birkenfeld/ipython-physics/raw/d1310a2ab15d/physics.py
35 35
36 36 The URL should point to an importable Python module - either a .py file
37 37 or a .zip file.
38 38
39 39 Parameters:
40 40
41 41 -n filename : Specify a name for the file, rather than taking it from
42 42 the URL.
43 43 """
44 44 opts, args = self.parse_options(parameter_s, 'n:')
45 45 try:
46 46 filename = self.shell.extension_manager.install_extension(args,
47 47 opts.get('n'))
48 48 except ValueError as e:
49 49 print e
50 50 return
51 51
52 52 filename = os.path.basename(filename)
53 53 print "Installed %s. To use it, type:" % filename
54 54 print " %%load_ext %s" % os.path.splitext(filename)[0]
55 55
56 56
57 57 @line_magic
58 58 def load_ext(self, module_str):
59 59 """Load an IPython extension by its module name."""
60 60 if not module_str:
61 61 raise UsageError('Missing module name.')
62 self.shell.extension_manager.load_extension(module_str)
62 if self.shell.extension_manager.load_extension(module_str) == 'already loaded':
63 print "The %s extension is already loaded. To reload it, use:" % module_str
64 print " %reload_ext", module_str
63 65
64 66 @line_magic
65 67 def unload_ext(self, module_str):
66 """Unload an IPython extension by its module name."""
68 """Unload an IPython extension by its module name.
69
70 Not all extensions can be unloaded, only those which define an
71 ``unload_ipython_extension`` function.
72 """
67 73 if not module_str:
68 74 raise UsageError('Missing module name.')
69 self.shell.extension_manager.unload_extension(module_str)
75
76 res = self.shell.extension_manager.unload_extension(module_str)
77
78 if res == 'no unload function':
79 print "The %s extension doesn't define how to unload it." % module_str
80 elif res == "not loaded":
81 print "The %s extension is not loaded." % module_str
70 82
71 83 @line_magic
72 84 def reload_ext(self, module_str):
73 85 """Reload an IPython extension by its module name."""
74 86 if not module_str:
75 87 raise UsageError('Missing module name.')
76 88 self.shell.extension_manager.reload_extension(module_str)
General Comments 0
You need to be logged in to leave comments. Login now