bzr branch
http://gegoxaren.bato24.eu/bzr/brz/remove-bazaar
|
6651.4.1
by Martin
Rewrite of the plugin module for Python 3 compat and general sanity |
1 |
# Copyright (C) 2005-2011 Canonical Ltd, 2017 Breezy developers
|
|
1887.1.1
by Adeodato Simó
Do not separate paragraphs in the copyright statement with blank lines, |
2 |
#
|
|
1393.2.1
by John Arbash Meinel
Merged in split-storage-2 branch. Need to cleanup a little bit more still. |
3 |
# This program is free software; you can redistribute it and/or modify
|
4 |
# it under the terms of the GNU General Public License as published by
|
|
5 |
# the Free Software Foundation; either version 2 of the License, or
|
|
6 |
# (at your option) any later version.
|
|
|
1887.1.1
by Adeodato Simó
Do not separate paragraphs in the copyright statement with blank lines, |
7 |
#
|
|
1393.2.1
by John Arbash Meinel
Merged in split-storage-2 branch. Need to cleanup a little bit more still. |
8 |
# This program is distributed in the hope that it will be useful,
|
9 |
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
10 |
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
11 |
# GNU General Public License for more details.
|
|
|
1887.1.1
by Adeodato Simó
Do not separate paragraphs in the copyright statement with blank lines, |
12 |
#
|
|
1393.2.1
by John Arbash Meinel
Merged in split-storage-2 branch. Need to cleanup a little bit more still. |
13 |
# You should have received a copy of the GNU General Public License
|
14 |
# along with this program; if not, write to the Free Software
|
|
|
4183.7.1
by Sabin Iacob
update FSF mailing address |
15 |
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
|
1393.2.1
by John Arbash Meinel
Merged in split-storage-2 branch. Need to cleanup a little bit more still. |
16 |
|
|
6651.4.1
by Martin
Rewrite of the plugin module for Python 3 compat and general sanity |
17 |
"""Breezy plugin support.
|
18 |
||
19 |
Which plugins to load can be configured by setting these environment variables:
|
|
20 |
||
21 |
- BRZ_PLUGIN_PATH: Paths to look for plugins in.
|
|
22 |
- BRZ_DISABLE_PLUGINS: Plugin names to block from being loaded.
|
|
23 |
- BRZ_PLUGINS_AT: Name and paths for plugins to load from specific locations.
|
|
24 |
||
25 |
The interfaces this module exports include:
|
|
26 |
||
27 |
- disable_plugins: Load no plugins and stop future automatic loading.
|
|
28 |
- load_plugins: Load all plugins that can be found in configuration.
|
|
29 |
- describe_plugins: Generate text for each loaded (or failed) plugin.
|
|
30 |
- extend_path: Mechanism by which the plugins package path is set.
|
|
|
6651.4.2
by Martin
Move plugin_name logic from commands to plugin to fix test |
31 |
- plugin_name: Gives unprefixed name of a plugin module.
|
|
3620.4.1
by Robert Collins
plugin doc strings update. |
32 |
|
33 |
See the plugin-api developer documentation for information about writing
|
|
34 |
plugins.
|
|
|
1185.16.83
by mbp at sourcefrog
- notes on testability of plugins |
35 |
"""
|
36 |
||
|
6379.6.7
by Jelmer Vernooij
Move importing from future until after doc string, otherwise the doc string will disappear. |
37 |
from __future__ import absolute_import |
38 |
||
|
1393.2.1
by John Arbash Meinel
Merged in split-storage-2 branch. Need to cleanup a little bit more still. |
39 |
import os |
|
6651.4.1
by Martin
Rewrite of the plugin module for Python 3 compat and general sanity |
40 |
import re |
|
1185.16.82
by mbp at sourcefrog
- give a quieter warning if a plugin can't be loaded |
41 |
import sys |
|
1996.3.17
by John Arbash Meinel
lazy_import plugin and transport/local |
42 |
|
|
6651.4.1
by Martin
Rewrite of the plugin module for Python 3 compat and general sanity |
43 |
import breezy |
|
6624
by Jelmer Vernooij
Merge Python3 porting work ('py3 pokes') |
44 |
from . import osutils |
|
3794.1.1
by Martin Pool
Update osutils imports to fix setup.py on Windows |
45 |
|
|
6624
by Jelmer Vernooij
Merge Python3 porting work ('py3 pokes') |
46 |
from .lazy_import import lazy_import |
|
1996.3.17
by John Arbash Meinel
lazy_import plugin and transport/local |
47 |
lazy_import(globals(), """ |
48 |
import imp
|
|
|
6651.4.1
by Martin
Rewrite of the plugin module for Python 3 compat and general sanity |
49 |
from importlib import util as importlib_util
|
|
1185.16.82
by mbp at sourcefrog
- give a quieter warning if a plugin can't be loaded |
50 |
|
|
6622.1.34
by Jelmer Vernooij
Rename brzlib => breezy. |
51 |
from breezy import (
|
|
7336.2.1
by Martin
Split non-ini config methods to bedding |
52 |
bedding,
|
|
3427.2.2
by James Westby
Just print the exception, keeping the API of log_exception_quietly the same. |
53 |
debug,
|
|
6651.4.1
by Martin
Rewrite of the plugin module for Python 3 compat and general sanity |
54 |
help_topics,
|
|
3224.5.1
by Andrew Bennetts
Lots of assorted hackery to reduce the number of imports for common operations. Improves 'rocks', 'st' and 'help' times by ~50ms on my laptop. |
55 |
trace,
|
|
1996.3.17
by John Arbash Meinel
lazy_import plugin and transport/local |
56 |
)
|
57 |
""") |
|
58 |
||
|
7413.8.11
by Jelmer Vernooij
Don't lazy-import errors. |
59 |
from . import ( |
60 |
errors, |
|
61 |
)
|
|
62 |
||
|
1996.3.17
by John Arbash Meinel
lazy_import plugin and transport/local |
63 |
|
|
6651.4.1
by Martin
Rewrite of the plugin module for Python 3 compat and general sanity |
64 |
_MODULE_PREFIX = "breezy.plugins." |
65 |
||
66 |
if __debug__ or sys.version_info > (3,): |
|
67 |
COMPILED_EXT = ".pyc" |
|
68 |
else: |
|
69 |
COMPILED_EXT = ".pyo" |
|
70 |
||
71 |
||
72 |
def disable_plugins(state=None): |
|
|
1551.3.11
by Aaron Bentley
Merge from Robert |
73 |
"""Disable loading plugins. |
74 |
||
75 |
Future calls to load_plugins() will be ignored.
|
|
|
6651.4.1
by Martin
Rewrite of the plugin module for Python 3 compat and general sanity |
76 |
|
77 |
:param state: The library state object that records loaded plugins.
|
|
78 |
"""
|
|
79 |
if state is None: |
|
|
6759.4.2
by Jelmer Vernooij
Use get_global_state> |
80 |
state = breezy.get_global_state() |
|
6651.4.1
by Martin
Rewrite of the plugin module for Python 3 compat and general sanity |
81 |
state.plugins = {} |
82 |
||
83 |
||
|
7490.98.2
by Jelmer Vernooij
Don't hide problems from plugins by default. |
84 |
def load_plugins(path=None, state=None, warn_load_problems=True): |
|
6651.4.1
by Martin
Rewrite of the plugin module for Python 3 compat and general sanity |
85 |
"""Load breezy plugins. |
86 |
||
87 |
The environment variable BRZ_PLUGIN_PATH is considered a delimited
|
|
88 |
set of paths to look through. Each entry is searched for `*.py`
|
|
89 |
files (and whatever other extensions are used in the platform,
|
|
90 |
such as `*.pyd`).
|
|
91 |
||
92 |
:param path: The list of paths to search for plugins. By default,
|
|
93 |
it is populated from the __path__ of the breezy.plugins package.
|
|
94 |
:param state: The library state object that records loaded plugins.
|
|
95 |
"""
|
|
96 |
if state is None: |
|
|
6759.4.2
by Jelmer Vernooij
Use get_global_state> |
97 |
state = breezy.get_global_state() |
|
6651.4.1
by Martin
Rewrite of the plugin module for Python 3 compat and general sanity |
98 |
if getattr(state, 'plugins', None) is not None: |
99 |
# People can make sure plugins are loaded, they just won't be twice
|
|
100 |
return
|
|
101 |
||
102 |
if path is None: |
|
103 |
# Calls back into extend_path() here
|
|
104 |
from breezy.plugins import __path__ as path |
|
105 |
||
106 |
state.plugin_warnings = {} |
|
|
7236.3.1
by Jelmer Vernooij
Add basic support for registering plugins as entrypoints in pkg_resources. |
107 |
_load_plugins_from_path(state, path) |
|
7236.3.2
by Jelmer Vernooij
Fix tests. |
108 |
if (None, 'entrypoints') in _env_plugin_path(): |
109 |
_load_plugins_from_entrypoints(state) |
|
|
6651.4.1
by Martin
Rewrite of the plugin module for Python 3 compat and general sanity |
110 |
state.plugins = plugins() |
|
7490.98.2
by Jelmer Vernooij
Don't hide problems from plugins by default. |
111 |
if warn_load_problems: |
112 |
for plugin, errors in state.plugin_warnings.items(): |
|
113 |
for error in errors: |
|
114 |
trace.warning('%s', error) |
|
|
6651.4.1
by Martin
Rewrite of the plugin module for Python 3 compat and general sanity |
115 |
|
116 |
||
|
7236.3.1
by Jelmer Vernooij
Add basic support for registering plugins as entrypoints in pkg_resources. |
117 |
def _load_plugins_from_entrypoints(state): |
118 |
try: |
|
119 |
import pkg_resources |
|
120 |
except ImportError: |
|
121 |
# No pkg_resources, no entrypoints.
|
|
122 |
pass
|
|
123 |
else: |
|
124 |
for ep in pkg_resources.iter_entry_points('breezy.plugin'): |
|
|
7236.3.6
by Jelmer Vernooij
Fix tests. |
125 |
fullname = _MODULE_PREFIX + ep.name |
126 |
if fullname in sys.modules: |
|
127 |
continue
|
|
128 |
sys.modules[fullname] = ep.load() |
|
|
7236.3.1
by Jelmer Vernooij
Add basic support for registering plugins as entrypoints in pkg_resources. |
129 |
|
130 |
||
|
6651.4.2
by Martin
Move plugin_name logic from commands to plugin to fix test |
131 |
def plugin_name(module_name): |
132 |
"""Gives unprefixed name from module_name or None.""" |
|
133 |
if module_name.startswith(_MODULE_PREFIX): |
|
134 |
parts = module_name.split(".") |
|
135 |
if len(parts) > 2: |
|
136 |
return parts[2] |
|
137 |
return None |
|
138 |
||
139 |
||
|
6651.4.1
by Martin
Rewrite of the plugin module for Python 3 compat and general sanity |
140 |
def extend_path(path, name): |
141 |
"""Helper so breezy.plugins can be a sort of namespace package. |
|
142 |
||
143 |
To be used in similar fashion to pkgutil.extend_path:
|
|
144 |
||
145 |
from breezy.plugins import extend_path
|
|
146 |
__path__ = extend_path(__path__, __name__)
|
|
147 |
||
148 |
Inspects the BRZ_PLUGIN* envvars, sys.path, and the filesystem to find
|
|
149 |
plugins. May mutate sys.modules in order to block plugin loading, and may
|
|
150 |
append a new meta path finder to sys.meta_path for plugins@ loading.
|
|
151 |
||
152 |
Returns a list of paths to import from, as an enhanced object that also
|
|
153 |
contains details of the other configuration used.
|
|
154 |
"""
|
|
155 |
blocks = _env_disable_plugins() |
|
156 |
_block_plugins(blocks) |
|
157 |
||
158 |
extra_details = _env_plugins_at() |
|
159 |
_install_importer_if_needed(extra_details) |
|
160 |
||
161 |
paths = _iter_plugin_paths(_env_plugin_path(), path) |
|
162 |
||
163 |
return _Path(name, blocks, extra_details, paths) |
|
164 |
||
165 |
||
166 |
class _Path(list): |
|
167 |
"""List type to use as __path__ but containing additional details. |
|
168 |
||
169 |
Python 3 allows any iterable for __path__ but Python 2 is more fussy.
|
|
170 |
"""
|
|
171 |
||
172 |
def __init__(self, package_name, blocked, extra, paths): |
|
173 |
super(_Path, self).__init__(paths) |
|
174 |
self.package_name = package_name |
|
175 |
self.blocked_names = blocked |
|
176 |
self.extra_details = extra |
|
177 |
||
178 |
def __repr__(self): |
|
179 |
return "%s(%r, %r, %r, %s)" % ( |
|
180 |
self.__class__.__name__, self.package_name, self.blocked_names, |
|
181 |
self.extra_details, list.__repr__(self)) |
|
182 |
||
183 |
||
184 |
def _expect_identifier(name, env_key, env_value): |
|
185 |
"""Validate given name from envvar is usable as a Python identifier. |
|
186 |
||
187 |
Returns the name as a native str, or None if it was invalid.
|
|
188 |
||
189 |
Per PEP 3131 this is no longer strictly correct for Python 3, but as MvL
|
|
190 |
didn't include a neat way to check except eval, this enforces ascii.
|
|
191 |
"""
|
|
192 |
if re.match(r"^[A-Za-z_][A-Za-z0-9_]*$", name) is None: |
|
193 |
trace.warning("Invalid name '%s' in %s='%s'", name, env_key, env_value) |
|
194 |
return None |
|
195 |
return str(name) |
|
196 |
||
197 |
||
198 |
def _env_disable_plugins(key='BRZ_DISABLE_PLUGINS'): |
|
199 |
"""Gives list of names for plugins to disable from environ key.""" |
|
200 |
disabled_names = [] |
|
201 |
env = osutils.path_from_environ(key) |
|
202 |
if env: |
|
203 |
for name in env.split(os.pathsep): |
|
204 |
name = _expect_identifier(name, key, env) |
|
205 |
if name is not None: |
|
206 |
disabled_names.append(name) |
|
207 |
return disabled_names |
|
208 |
||
209 |
||
210 |
def _env_plugins_at(key='BRZ_PLUGINS_AT'): |
|
211 |
"""Gives list of names and paths of specific plugins from environ key.""" |
|
212 |
plugin_details = [] |
|
213 |
env = osutils.path_from_environ(key) |
|
214 |
if env: |
|
215 |
for pair in env.split(os.pathsep): |
|
216 |
if '@' in pair: |
|
217 |
name, path = pair.split('@', 1) |
|
218 |
else: |
|
219 |
path = pair |
|
220 |
name = osutils.basename(path).split('.', 1)[0] |
|
221 |
name = _expect_identifier(name, key, env) |
|
222 |
if name is not None: |
|
223 |
plugin_details.append((name, path)) |
|
224 |
return plugin_details |
|
225 |
||
226 |
||
227 |
def _env_plugin_path(key='BRZ_PLUGIN_PATH'): |
|
228 |
"""Gives list of paths and contexts for plugins from environ key. |
|
229 |
||
230 |
Each entry is either a specific path to load plugins from and the value
|
|
|
7236.3.2
by Jelmer Vernooij
Fix tests. |
231 |
'path', or None and one of the values 'user', 'core', 'entrypoints', 'site'.
|
|
6651.4.1
by Martin
Rewrite of the plugin module for Python 3 compat and general sanity |
232 |
"""
|
233 |
path_details = [] |
|
234 |
env = osutils.path_from_environ(key) |
|
|
7290.33.1
by Jelmer Vernooij
Disable entrypoints by default. |
235 |
defaults = { |
236 |
"user": not env, |
|
237 |
"core": True, |
|
238 |
"site": True, |
|
239 |
'entrypoints': False, |
|
240 |
}
|
|
|
6651.4.1
by Martin
Rewrite of the plugin module for Python 3 compat and general sanity |
241 |
if env: |
242 |
# Add paths specified by user in order
|
|
243 |
for p in env.split(os.pathsep): |
|
244 |
flag, name = p[:1], p[1:] |
|
245 |
if flag in ("+", "-") and name in defaults: |
|
246 |
if flag == "+" and defaults[name] is not None: |
|
247 |
path_details.append((None, name)) |
|
248 |
defaults[name] = None |
|
249 |
else: |
|
250 |
path_details.append((p, 'path')) |
|
251 |
||
252 |
# Add any remaining default paths
|
|
|
7236.3.2
by Jelmer Vernooij
Fix tests. |
253 |
for name in ('user', 'core', 'entrypoints', 'site'): |
|
6651.4.1
by Martin
Rewrite of the plugin module for Python 3 compat and general sanity |
254 |
if defaults[name]: |
255 |
path_details.append((None, name)) |
|
256 |
||
257 |
return path_details |
|
258 |
||
259 |
||
260 |
def _iter_plugin_paths(paths_from_env, core_paths): |
|
261 |
"""Generate paths using paths_from_env and core_paths.""" |
|
262 |
# GZ 2017-06-02: This is kinda horrid, should make better.
|
|
263 |
for path, context in paths_from_env: |
|
264 |
if context == 'path': |
|
265 |
yield path |
|
266 |
elif context == 'user': |
|
267 |
path = get_user_plugin_path() |
|
268 |
if os.path.isdir(path): |
|
269 |
yield path |
|
270 |
elif context == 'core': |
|
271 |
for path in _get_core_plugin_paths(core_paths): |
|
272 |
yield path |
|
273 |
elif context == 'site': |
|
274 |
for path in _get_site_plugin_paths(sys.path): |
|
275 |
if os.path.isdir(path): |
|
276 |
yield path |
|
277 |
||
278 |
||
279 |
def _install_importer_if_needed(plugin_details): |
|
280 |
"""Install a meta path finder to handle plugin_details if any.""" |
|
281 |
if plugin_details: |
|
282 |
finder = _PluginsAtFinder(_MODULE_PREFIX, plugin_details) |
|
283 |
# For Python 3, must insert before default PathFinder to override.
|
|
284 |
sys.meta_path.insert(2, finder) |
|
285 |
||
286 |
||
|
7236.3.1
by Jelmer Vernooij
Add basic support for registering plugins as entrypoints in pkg_resources. |
287 |
def _load_plugins_from_path(state, paths): |
|
6651.4.1
by Martin
Rewrite of the plugin module for Python 3 compat and general sanity |
288 |
"""Do the importing all plugins from paths.""" |
289 |
imported_names = set() |
|
290 |
for name, path in _iter_possible_plugins(paths): |
|
291 |
if name not in imported_names: |
|
292 |
msg = _load_plugin_module(name, path) |
|
293 |
if msg is not None: |
|
294 |
state.plugin_warnings.setdefault(name, []).append(msg) |
|
295 |
imported_names.add(name) |
|
296 |
||
297 |
||
298 |
def _block_plugins(names): |
|
299 |
"""Add names to sys.modules to block future imports.""" |
|
300 |
for name in names: |
|
301 |
package_name = _MODULE_PREFIX + name |
|
302 |
if sys.modules.get(package_name) is not None: |
|
303 |
trace.mutter("Blocked plugin %s already loaded.", name) |
|
304 |
sys.modules[package_name] = None |
|
305 |
||
306 |
||
307 |
def _get_package_init(package_path): |
|
308 |
"""Get path of __init__ file from package_path or None if not a package.""" |
|
309 |
init_path = osutils.pathjoin(package_path, "__init__.py") |
|
310 |
if os.path.exists(init_path): |
|
311 |
return init_path |
|
312 |
init_path = init_path[:-3] + COMPILED_EXT |
|
313 |
if os.path.exists(init_path): |
|
314 |
return init_path |
|
315 |
return None |
|
316 |
||
317 |
||
318 |
def _iter_possible_plugins(plugin_paths): |
|
319 |
"""Generate names and paths of possible plugins from plugin_paths.""" |
|
320 |
# Inspect any from BRZ_PLUGINS_AT first.
|
|
321 |
for name, path in getattr(plugin_paths, "extra_details", ()): |
|
322 |
yield name, path |
|
323 |
# Then walk over files and directories in the paths from the package.
|
|
324 |
for path in plugin_paths: |
|
325 |
if os.path.isfile(path): |
|
326 |
if path.endswith(".zip"): |
|
327 |
trace.mutter("Don't yet support loading plugins from zip.") |
|
328 |
else: |
|
329 |
for name, path in _walk_modules(path): |
|
330 |
yield name, path |
|
331 |
||
332 |
||
333 |
def _walk_modules(path): |
|
334 |
"""Generate name and path of modules and packages on path.""" |
|
335 |
for root, dirs, files in os.walk(path): |
|
336 |
files.sort() |
|
337 |
for f in files: |
|
338 |
if f[:2] != "__": |
|
339 |
if f.endswith((".py", COMPILED_EXT)): |
|
340 |
yield f.rsplit(".", 1)[0], root |
|
341 |
dirs.sort() |
|
342 |
for d in dirs: |
|
343 |
if d[:2] != "__": |
|
344 |
package_dir = osutils.pathjoin(root, d) |
|
345 |
fullpath = _get_package_init(package_dir) |
|
346 |
if fullpath is not None: |
|
347 |
yield d, package_dir |
|
348 |
# Don't descend into subdirectories
|
|
349 |
del dirs[:] |
|
350 |
||
351 |
||
352 |
def describe_plugins(show_paths=False, state=None): |
|
|
5616.7.10
by Martin Pool
Clean up describe_plugins to sort loaded and unloaded plugins together. |
353 |
"""Generate text description of plugins. |
354 |
||
|
6651.4.1
by Martin
Rewrite of the plugin module for Python 3 compat and general sanity |
355 |
Includes both those that have loaded, and those that failed to load.
|
|
5616.7.5
by Martin Pool
Factor out describe_loaded_plugins |
356 |
|
|
6651.4.1
by Martin
Rewrite of the plugin module for Python 3 compat and general sanity |
357 |
:param show_paths: If true, include the plugin path.
|
358 |
:param state: The library state object to inspect.
|
|
|
5616.7.5
by Martin Pool
Factor out describe_loaded_plugins |
359 |
:returns: Iterator of text lines (including newlines.)
|
360 |
"""
|
|
|
6651.4.1
by Martin
Rewrite of the plugin module for Python 3 compat and general sanity |
361 |
if state is None: |
|
6759.4.2
by Jelmer Vernooij
Use get_global_state> |
362 |
state = breezy.get_global_state() |
|
6759.4.7
by Jelmer Vernooij
Fix last test. |
363 |
loaded_plugins = getattr(state, 'plugins', {}) |
|
6759.4.5
by Jelmer Vernooij
Fix some tests. |
364 |
plugin_warnings = set(getattr(state, 'plugin_warnings', [])) |
|
6759.4.7
by Jelmer Vernooij
Fix last test. |
365 |
all_names = sorted(set(loaded_plugins.keys()).union(plugin_warnings)) |
|
5616.7.10
by Martin Pool
Clean up describe_plugins to sort loaded and unloaded plugins together. |
366 |
for name in all_names: |
|
6759.4.4
by Jelmer Vernooij
Avoid accessing global state. |
367 |
if name in loaded_plugins: |
368 |
plugin = loaded_plugins[name] |
|
|
5616.7.10
by Martin Pool
Clean up describe_plugins to sort loaded and unloaded plugins together. |
369 |
version = plugin.__version__ |
370 |
if version == 'unknown': |
|
371 |
version = '' |
|
372 |
yield '%s %s\n' % (name, version) |
|
|
6651.4.1
by Martin
Rewrite of the plugin module for Python 3 compat and general sanity |
373 |
d = plugin.module.__doc__ |
|
5616.7.10
by Martin Pool
Clean up describe_plugins to sort loaded and unloaded plugins together. |
374 |
if d: |
375 |
doc = d.split('\n')[0] |
|
376 |
else: |
|
377 |
doc = '(no description)' |
|
|
5616.7.11
by Martin Pool
Additional tests and fixes for refactored describe_plugins. |
378 |
yield (" %s\n" % doc) |
|
5616.7.10
by Martin Pool
Clean up describe_plugins to sort loaded and unloaded plugins together. |
379 |
if show_paths: |
380 |
yield (" %s\n" % plugin.path()) |
|
|
5616.7.5
by Martin Pool
Factor out describe_loaded_plugins |
381 |
else: |
|
5616.7.10
by Martin Pool
Clean up describe_plugins to sort loaded and unloaded plugins together. |
382 |
yield "%s (failed to load)\n" % name |
|
6651.4.1
by Martin
Rewrite of the plugin module for Python 3 compat and general sanity |
383 |
if name in state.plugin_warnings: |
384 |
for line in state.plugin_warnings[name]: |
|
|
5616.7.6
by Martin Pool
Use standard plugin list formatting in crash reports |
385 |
yield " ** " + line + '\n' |
386 |
yield '\n' |
|
|
5616.7.5
by Martin Pool
Factor out describe_loaded_plugins |
387 |
|
388 |
||
|
6651.4.1
by Martin
Rewrite of the plugin module for Python 3 compat and general sanity |
389 |
def _get_core_plugin_paths(existing_paths): |
390 |
"""Generate possible locations for plugins based on existing_paths.""" |
|
391 |
if getattr(sys, 'frozen', False): |
|
|
4628.2.1
by Vincent Ladeuil
Start introducing accessors for plugin paths. |
392 |
# We need to use relative path to system-wide plugin
|
|
6622.1.35
by Jelmer Vernooij
Fix last tests. |
393 |
# directory because breezy from standalone brz.exe
|
|
4628.2.1
by Vincent Ladeuil
Start introducing accessors for plugin paths. |
394 |
# could be imported by another standalone program
|
|
6681.2.4
by Jelmer Vernooij
More renames. |
395 |
# (e.g. brz-config; or TortoiseBzr/Olive if/when they
|
|
4628.2.1
by Vincent Ladeuil
Start introducing accessors for plugin paths. |
396 |
# will become standalone exe). [bialix 20071123]
|
397 |
# __file__ typically is
|
|
|
6622.1.34
by Jelmer Vernooij
Rename brzlib => breezy. |
398 |
# C:\Program Files\Bazaar\lib\library.zip\breezy\plugin.pyc
|
|
4628.2.1
by Vincent Ladeuil
Start introducing accessors for plugin paths. |
399 |
# then plugins directory is
|
400 |
# C:\Program Files\Bazaar\plugins
|
|
401 |
# so relative path is ../../../plugins
|
|
|
6651.4.1
by Martin
Rewrite of the plugin module for Python 3 compat and general sanity |
402 |
yield osutils.abspath(osutils.pathjoin( |
403 |
osutils.dirname(__file__), '../../../plugins')) |
|
|
4628.2.1
by Vincent Ladeuil
Start introducing accessors for plugin paths. |
404 |
else: # don't look inside library.zip |
|
6651.4.1
by Martin
Rewrite of the plugin module for Python 3 compat and general sanity |
405 |
for path in existing_paths: |
406 |
yield path |
|
407 |
||
408 |
||
409 |
def _get_site_plugin_paths(sys_paths): |
|
410 |
"""Generate possible locations for plugins from given sys_paths.""" |
|
411 |
for path in sys_paths: |
|
412 |
if os.path.basename(path) in ('dist-packages', 'site-packages'): |
|
413 |
yield osutils.pathjoin(path, 'breezy', 'plugins') |
|
|
4628.2.1
by Vincent Ladeuil
Start introducing accessors for plugin paths. |
414 |
|
415 |
||
416 |
def get_user_plugin_path(): |
|
|
7336.2.1
by Martin
Split non-ini config methods to bedding |
417 |
return osutils.pathjoin(bedding.config_dir(), 'plugins') |
|
4628.2.1
by Vincent Ladeuil
Start introducing accessors for plugin paths. |
418 |
|
419 |
||
|
6651.4.1
by Martin
Rewrite of the plugin module for Python 3 compat and general sanity |
420 |
def record_plugin_warning(warning_message): |
|
5616.7.4
by Martin Pool
Also use quiet warnings for other failures to load plugins |
421 |
trace.mutter(warning_message) |
|
6651.4.1
by Martin
Rewrite of the plugin module for Python 3 compat and general sanity |
422 |
return warning_message |
|
5616.7.4
by Martin Pool
Also use quiet warnings for other failures to load plugins |
423 |
|
424 |
||
|
5086.5.8
by Vincent Ladeuil
Make sure we can load from a non-standard directory name. |
425 |
def _load_plugin_module(name, dir): |
|
6651.4.1
by Martin
Rewrite of the plugin module for Python 3 compat and general sanity |
426 |
"""Load plugin by name. |
|
5086.5.10
by Vincent Ladeuil
Cleanup docs. |
427 |
|
|
6622.1.34
by Jelmer Vernooij
Rename brzlib => breezy. |
428 |
:param name: The plugin name in the breezy.plugins namespace.
|
|
5086.5.10
by Vincent Ladeuil
Cleanup docs. |
429 |
:param dir: The directory the plugin is loaded from for error messages.
|
430 |
"""
|
|
|
6651.4.1
by Martin
Rewrite of the plugin module for Python 3 compat and general sanity |
431 |
if _MODULE_PREFIX + name in sys.modules: |
|
5086.5.8
by Vincent Ladeuil
Make sure we can load from a non-standard directory name. |
432 |
return
|
433 |
try: |
|
|
6651.4.1
by Martin
Rewrite of the plugin module for Python 3 compat and general sanity |
434 |
__import__(_MODULE_PREFIX + name) |
|
6672.1.2
by Jelmer Vernooij
Remove breezy.api. |
435 |
except errors.IncompatibleVersion as e: |
|
5616.7.1
by Martin Pool
Record but don't show warnings about updated plugins |
436 |
warning_message = ( |
|
6672.1.2
by Jelmer Vernooij
Remove breezy.api. |
437 |
"Unable to load plugin %r. It supports %s " |
438 |
"versions %r but the current version is %s" % |
|
439 |
(name, e.api.__name__, e.wanted, e.current)) |
|
|
6651.4.1
by Martin
Rewrite of the plugin module for Python 3 compat and general sanity |
440 |
return record_plugin_warning(warning_message) |
|
6619.3.2
by Jelmer Vernooij
Apply 2to3 except fix. |
441 |
except Exception as e: |
|
6651.4.1
by Martin
Rewrite of the plugin module for Python 3 compat and general sanity |
442 |
trace.log_exception_quietly() |
443 |
if 'error' in debug.debug_flags: |
|
444 |
trace.print_exception(sys.exc_info(), sys.stderr) |
|
445 |
# GZ 2017-06-02: Move this name checking up a level, no point trying
|
|
446 |
# to import things with bad names.
|
|
|
6797
by Jelmer Vernooij
Merge lp:~jelmer/brz/fix-imports. |
447 |
if re.search('\\.|-| ', name): |
|
5086.5.8
by Vincent Ladeuil
Make sure we can load from a non-standard directory name. |
448 |
sanitised_name = re.sub('[-. ]', '_', name) |
|
6622.1.35
by Jelmer Vernooij
Fix last tests. |
449 |
if sanitised_name.startswith('brz_'): |
450 |
sanitised_name = sanitised_name[len('brz_'):] |
|
|
5086.5.8
by Vincent Ladeuil
Make sure we can load from a non-standard directory name. |
451 |
trace.warning("Unable to load %r in %r as a plugin because the " |
|
7143.15.2
by Jelmer Vernooij
Run autopep8. |
452 |
"file path isn't a valid module name; try renaming "
|
453 |
"it to %r." % (name, dir, sanitised_name)) |
|
|
5086.5.8
by Vincent Ladeuil
Make sure we can load from a non-standard directory name. |
454 |
else: |
|
6651.4.1
by Martin
Rewrite of the plugin module for Python 3 compat and general sanity |
455 |
return record_plugin_warning( |
456 |
'Unable to load plugin %r from %r: %s' % (name, dir, e)) |
|
|
2256.2.2
by Robert Collins
Allow 'import bzrlib.plugins.NAME' to work when the plugin NAME has not |
457 |
|
458 |
||
|
2762.2.1
by Robert Collins
* ``bzr plugins`` now lists the version number for each plugin in square |
459 |
def plugins(): |
460 |
"""Return a dictionary of the plugins. |
|
|
3943.8.1
by Marius Kruger
remove all trailing whitespace from bzr source |
461 |
|
|
2762.2.1
by Robert Collins
* ``bzr plugins`` now lists the version number for each plugin in square |
462 |
Each item in the dictionary is a PlugIn object.
|
463 |
"""
|
|
464 |
result = {} |
|
|
6651.4.1
by Martin
Rewrite of the plugin module for Python 3 compat and general sanity |
465 |
for fullname in sys.modules: |
466 |
if fullname.startswith(_MODULE_PREFIX): |
|
467 |
name = fullname[len(_MODULE_PREFIX):] |
|
|
7143.15.5
by Jelmer Vernooij
More PEP8 fixes. |
468 |
if "." not in name and sys.modules[fullname] is not None: |
|
6651.4.1
by Martin
Rewrite of the plugin module for Python 3 compat and general sanity |
469 |
result[name] = PlugIn(name, sys.modules[fullname]) |
|
2762.2.1
by Robert Collins
* ``bzr plugins`` now lists the version number for each plugin in square |
470 |
return result |
471 |
||
472 |
||
|
6759.4.3
by Jelmer Vernooij
Avoid accessing global state. |
473 |
def get_loaded_plugin(name): |
474 |
"""Retrieve an already loaded plugin. |
|
475 |
||
476 |
Returns None if there is no such plugin loaded
|
|
477 |
"""
|
|
478 |
try: |
|
479 |
module = sys.modules[_MODULE_PREFIX + name] |
|
480 |
except KeyError: |
|
481 |
return None |
|
|
6780.1.1
by Jelmer Vernooij
Check for plugin existing in sys.modules but being None. |
482 |
if module is None: |
483 |
return None |
|
|
6759.4.3
by Jelmer Vernooij
Avoid accessing global state. |
484 |
return PlugIn(name, module) |
485 |
||
486 |
||
|
6677.1.5
by Martin
Make crash debug and trace modules pass on Python 3 |
487 |
def format_concise_plugin_list(state=None): |
|
5609.23.6
by Martin Pool
Show concise list of plugins in non-apport crash; add test for this |
488 |
"""Return a string holding a concise list of plugins and their version. |
489 |
"""
|
|
|
6677.1.5
by Martin
Make crash debug and trace modules pass on Python 3 |
490 |
if state is None: |
|
6759.4.2
by Jelmer Vernooij
Use get_global_state> |
491 |
state = breezy.get_global_state() |
|
5609.23.6
by Martin Pool
Show concise list of plugins in non-apport crash; add test for this |
492 |
items = [] |
|
6954.1.5
by Jelmer Vernooij
Support error reporting in a state without plugins. |
493 |
for name, a_plugin in sorted(getattr(state, 'plugins', {}).items()): |
|
5609.23.6
by Martin Pool
Show concise list of plugins in non-apport crash; add test for this |
494 |
items.append("%s[%s]" % |
|
7143.15.2
by Jelmer Vernooij
Run autopep8. |
495 |
(name, a_plugin.__version__)) |
|
5609.23.6
by Martin Pool
Show concise list of plugins in non-apport crash; add test for this |
496 |
return ', '.join(items) |
497 |
||
498 |
||
|
2432.1.24
by Robert Collins
Add plugins as a help index. |
499 |
class PluginsHelpIndex(object): |
500 |
"""A help index that returns help topics for plugins.""" |
|
501 |
||
502 |
def __init__(self): |
|
503 |
self.prefix = 'plugins/' |
|
504 |
||
505 |
def get_topics(self, topic): |
|
|
2432.1.25
by Robert Collins
Return plugin module docstrings for 'bzr help plugin'. |
506 |
"""Search for topic in the loaded plugins. |
507 |
||
508 |
This will not trigger loading of new plugins.
|
|
509 |
||
510 |
:param topic: A topic to search for.
|
|
511 |
:return: A list which is either empty or contains a single
|
|
512 |
RegisteredTopic entry.
|
|
513 |
"""
|
|
514 |
if not topic: |
|
515 |
return [] |
|
516 |
if topic.startswith(self.prefix): |
|
517 |
topic = topic[len(self.prefix):] |
|
|
6651.4.1
by Martin
Rewrite of the plugin module for Python 3 compat and general sanity |
518 |
plugin_module_name = _MODULE_PREFIX + topic |
|
2432.1.25
by Robert Collins
Return plugin module docstrings for 'bzr help plugin'. |
519 |
try: |
520 |
module = sys.modules[plugin_module_name] |
|
521 |
except KeyError: |
|
522 |
return [] |
|
523 |
else: |
|
524 |
return [ModuleHelpTopic(module)] |
|
525 |
||
526 |
||
527 |
class ModuleHelpTopic(object): |
|
528 |
"""A help topic which returns the docstring for a module.""" |
|
529 |
||
530 |
def __init__(self, module): |
|
531 |
"""Constructor. |
|
532 |
||
533 |
:param module: The module for which help should be generated.
|
|
534 |
"""
|
|
535 |
self.module = module |
|
536 |
||
|
3984.4.5
by Ian Clatworthy
help xxx is full help; xxx -h is concise help |
537 |
def get_help_text(self, additional_see_also=None, verbose=True): |
|
2432.1.25
by Robert Collins
Return plugin module docstrings for 'bzr help plugin'. |
538 |
"""Return a string with the help for this topic. |
539 |
||
540 |
:param additional_see_also: Additional help topics to be
|
|
541 |
cross-referenced.
|
|
542 |
"""
|
|
543 |
if not self.module.__doc__: |
|
544 |
result = "Plugin '%s' has no docstring.\n" % self.module.__name__ |
|
545 |
else: |
|
546 |
result = self.module.__doc__ |
|
547 |
if result[-1] != '\n': |
|
548 |
result += '\n' |
|
|
6059.3.4
by Vincent Ladeuil
Fix forgotten renaming. |
549 |
result += help_topics._format_see_also(additional_see_also) |
|
2432.1.25
by Robert Collins
Return plugin module docstrings for 'bzr help plugin'. |
550 |
return result |
|
2432.1.29
by Robert Collins
Add get_help_topic to ModuleHelpTopic. |
551 |
|
552 |
def get_help_topic(self): |
|
|
6059.3.4
by Vincent Ladeuil
Fix forgotten renaming. |
553 |
"""Return the module help topic: its basename.""" |
|
6651.4.1
by Martin
Rewrite of the plugin module for Python 3 compat and general sanity |
554 |
return self.module.__name__[len(_MODULE_PREFIX):] |
|
2762.2.1
by Robert Collins
* ``bzr plugins`` now lists the version number for each plugin in square |
555 |
|
556 |
||
557 |
class PlugIn(object): |
|
|
6622.1.34
by Jelmer Vernooij
Rename brzlib => breezy. |
558 |
"""The breezy representation of a plugin. |
|
2762.2.1
by Robert Collins
* ``bzr plugins`` now lists the version number for each plugin in square |
559 |
|
560 |
The PlugIn object provides a way to manipulate a given plugin module.
|
|
561 |
"""
|
|
562 |
||
563 |
def __init__(self, name, module): |
|
564 |
"""Construct a plugin for module.""" |
|
565 |
self.name = name |
|
566 |
self.module = module |
|
567 |
||
|
5939.3.2
by Andrew Bennetts
Take a slightly more direct approach by largely preserving BZR_DISABLE_PLUGINS/BZR_PLUGINS_AT. |
568 |
def path(self): |
569 |
"""Get the path that this plugin was loaded from.""" |
|
|
2762.2.1
by Robert Collins
* ``bzr plugins`` now lists the version number for each plugin in square |
570 |
if getattr(self.module, '__path__', None) is not None: |
571 |
return os.path.abspath(self.module.__path__[0]) |
|
572 |
elif getattr(self.module, '__file__', None) is not None: |
|
|
3193.2.1
by Alexander Belchenko
show path to plugin module as *.py instead of *.pyc if python source available |
573 |
path = os.path.abspath(self.module.__file__) |
|
6651.4.1
by Martin
Rewrite of the plugin module for Python 3 compat and general sanity |
574 |
if path[-4:] == COMPILED_EXT: |
|
3193.2.1
by Alexander Belchenko
show path to plugin module as *.py instead of *.pyc if python source available |
575 |
pypath = path[:-4] + '.py' |
576 |
if os.path.isfile(pypath): |
|
577 |
path = pypath |
|
578 |
return path |
|
|
2762.2.1
by Robert Collins
* ``bzr plugins`` now lists the version number for each plugin in square |
579 |
else: |
|
5939.3.2
by Andrew Bennetts
Take a slightly more direct approach by largely preserving BZR_DISABLE_PLUGINS/BZR_PLUGINS_AT. |
580 |
return repr(self.module) |
|
2762.2.1
by Robert Collins
* ``bzr plugins`` now lists the version number for each plugin in square |
581 |
|
|
6651.4.1
by Martin
Rewrite of the plugin module for Python 3 compat and general sanity |
582 |
def __repr__(self): |
583 |
return "<%s.%s name=%s, module=%s>" % ( |
|
584 |
self.__class__.__module__, self.__class__.__name__, |
|
|
2762.2.1
by Robert Collins
* ``bzr plugins`` now lists the version number for each plugin in square |
585 |
self.name, self.module) |
586 |
||
587 |
def test_suite(self): |
|
588 |
"""Return the plugin's test suite.""" |
|
589 |
if getattr(self.module, 'test_suite', None) is not None: |
|
590 |
return self.module.test_suite() |
|
591 |
else: |
|
592 |
return None |
|
593 |
||
|
3302.8.21
by Vincent Ladeuil
Fixed as per Robert's review. |
594 |
def load_plugin_tests(self, loader): |
|
3302.8.10
by Vincent Ladeuil
Prepare bzrlib.plugin to use the new test loader. |
595 |
"""Return the adapted plugin's test suite. |
596 |
||
597 |
:param loader: The custom loader that should be used to load additional
|
|
598 |
tests.
|
|
599 |
"""
|
|
600 |
if getattr(self.module, 'load_tests', None) is not None: |
|
|
3302.8.11
by Vincent Ladeuil
Simplify plugin.load_tests. |
601 |
return loader.loadTestsFromModule(self.module) |
|
3302.8.10
by Vincent Ladeuil
Prepare bzrlib.plugin to use the new test loader. |
602 |
else: |
603 |
return None |
|
604 |
||
|
2762.2.1
by Robert Collins
* ``bzr plugins`` now lists the version number for each plugin in square |
605 |
def version_info(self): |
606 |
"""Return the plugin's version_tuple or None if unknown.""" |
|
607 |
version_info = getattr(self.module, 'version_info', None) |
|
|
3777.6.7
by Marius Kruger
* Can now also handle non-iteratable and string plugin versions. |
608 |
if version_info is not None: |
609 |
try: |
|
|
6651.4.1
by Martin
Rewrite of the plugin module for Python 3 compat and general sanity |
610 |
if isinstance(version_info, str): |
|
3777.6.7
by Marius Kruger
* Can now also handle non-iteratable and string plugin versions. |
611 |
version_info = version_info.split('.') |
612 |
elif len(version_info) == 3: |
|
613 |
version_info = tuple(version_info) + ('final', 0) |
|
|
6651.4.1
by Martin
Rewrite of the plugin module for Python 3 compat and general sanity |
614 |
except TypeError: |
|
3777.6.7
by Marius Kruger
* Can now also handle non-iteratable and string plugin versions. |
615 |
# The given version_info isn't even iteratible
|
616 |
trace.log_exception_quietly() |
|
617 |
version_info = (version_info,) |
|
|
2762.2.1
by Robert Collins
* ``bzr plugins`` now lists the version number for each plugin in square |
618 |
return version_info |
|
3302.8.10
by Vincent Ladeuil
Prepare bzrlib.plugin to use the new test loader. |
619 |
|
|
6651.4.1
by Martin
Rewrite of the plugin module for Python 3 compat and general sanity |
620 |
@property
|
621 |
def __version__(self): |
|
|
2762.2.1
by Robert Collins
* ``bzr plugins`` now lists the version number for each plugin in square |
622 |
version_info = self.version_info() |
|
3777.6.1
by Marius Kruger
Try to return something usefull for plugins with bad version numbers, |
623 |
if version_info is None or len(version_info) == 0: |
|
2762.2.1
by Robert Collins
* ``bzr plugins`` now lists the version number for each plugin in square |
624 |
return "unknown" |
|
3777.6.1
by Marius Kruger
Try to return something usefull for plugins with bad version numbers, |
625 |
try: |
|
6651.4.1
by Martin
Rewrite of the plugin module for Python 3 compat and general sanity |
626 |
version_string = breezy._format_version_tuple(version_info) |
627 |
except (ValueError, TypeError, IndexError): |
|
|
3777.6.6
by Marius Kruger
catch only ValueError, TypeError, IndexError as per feedback from John |
628 |
trace.log_exception_quietly() |
|
6651.4.1
by Martin
Rewrite of the plugin module for Python 3 compat and general sanity |
629 |
# Try to show something for the version anyway
|
|
3777.6.3
by Marius Kruger
Use bzrlib._format_version_tuple and map as per review from John. |
630 |
version_string = '.'.join(map(str, version_info)) |
|
2762.2.1
by Robert Collins
* ``bzr plugins`` now lists the version number for each plugin in square |
631 |
return version_string |
632 |
||
|
6651.4.1
by Martin
Rewrite of the plugin module for Python 3 compat and general sanity |
633 |
|
634 |
class _PluginsAtFinder(object): |
|
635 |
"""Meta path finder to support BRZ_PLUGINS_AT configuration.""" |
|
636 |
||
637 |
def __init__(self, prefix, names_and_paths): |
|
638 |
self.prefix = prefix |
|
639 |
self.names_to_path = dict((prefix + n, p) for n, p in names_and_paths) |
|
640 |
||
641 |
def __repr__(self): |
|
642 |
return "<%s %r>" % (self.__class__.__name__, self.prefix) |
|
643 |
||
644 |
def find_spec(self, fullname, paths, target=None): |
|
645 |
"""New module spec returning find method.""" |
|
646 |
if fullname not in self.names_to_path: |
|
647 |
return None |
|
648 |
path = self.names_to_path[fullname] |
|
649 |
if os.path.isdir(path): |
|
650 |
path = _get_package_init(path) |
|
651 |
if path is None: |
|
652 |
# GZ 2017-06-02: Any reason to block loading of the name from
|
|
653 |
# further down the path like this?
|
|
654 |
raise ImportError("Not loading namespace package %s as %s" % ( |
|
655 |
path, fullname)) |
|
656 |
return importlib_util.spec_from_file_location(fullname, path) |
|
657 |
||
658 |
def find_module(self, fullname, path): |
|
659 |
"""Old PEP 302 import hook find_module method.""" |
|
660 |
if fullname not in self.names_to_path: |
|
661 |
return None |
|
662 |
return _LegacyLoader(self.names_to_path[fullname]) |
|
663 |
||
664 |
||
665 |
class _LegacyLoader(object): |
|
666 |
"""Source loader implementation for Python versions without importlib.""" |
|
667 |
||
668 |
def __init__(self, filepath): |
|
669 |
self.filepath = filepath |
|
670 |
||
671 |
def __repr__(self): |
|
672 |
return "<%s %r>" % (self.__class__.__name__, self.filepath) |
|
|
5086.1.7
by Vincent Ladeuil
Cleaner fix for bug #411413. |
673 |
|
|
5086.5.3
by Vincent Ladeuil
First shot at loading plugins from a specific directory. |
674 |
def load_module(self, fullname): |
|
6325.1.1
by Vincent Ladeuil
Fix various typos |
675 |
"""Load a plugin from a specific directory (or file).""" |
|
6651.4.1
by Martin
Rewrite of the plugin module for Python 3 compat and general sanity |
676 |
plugin_path = self.filepath |
|
5086.5.14
by Vincent Ladeuil
Fix bug #552922 by controlling which files can be used to load a plugin. |
677 |
loading_path = None |
678 |
if os.path.isdir(plugin_path): |
|
|
6651.4.1
by Martin
Rewrite of the plugin module for Python 3 compat and general sanity |
679 |
init_path = _get_package_init(plugin_path) |
680 |
if init_path is not None: |
|
681 |
loading_path = plugin_path |
|
682 |
suffix = '' |
|
683 |
mode = '' |
|
684 |
kind = imp.PKG_DIRECTORY |
|
|
5086.5.14
by Vincent Ladeuil
Fix bug #552922 by controlling which files can be used to load a plugin. |
685 |
else: |
686 |
for suffix, mode, kind in imp.get_suffixes(): |
|
687 |
if plugin_path.endswith(suffix): |
|
688 |
loading_path = plugin_path |
|
689 |
break
|
|
690 |
if loading_path is None: |
|
|
5086.5.3
by Vincent Ladeuil
First shot at loading plugins from a specific directory. |
691 |
raise ImportError('%s cannot be loaded from %s' |
|
5086.5.14
by Vincent Ladeuil
Fix bug #552922 by controlling which files can be used to load a plugin. |
692 |
% (fullname, plugin_path)) |
|
5268.6.3
by Vincent Ladeuil
BZR_PLUGINS_AT should use packages properly to handle relative imports. |
693 |
if kind is imp.PKG_DIRECTORY: |
694 |
f = None |
|
695 |
else: |
|
696 |
f = open(loading_path, mode) |
|
|
5086.5.3
by Vincent Ladeuil
First shot at loading plugins from a specific directory. |
697 |
try: |
|
5086.5.14
by Vincent Ladeuil
Fix bug #552922 by controlling which files can be used to load a plugin. |
698 |
mod = imp.load_module(fullname, f, loading_path, |
699 |
(suffix, mode, kind)) |
|
|
5086.5.12
by Vincent Ladeuil
Force __package__ to fix pqm failure. |
700 |
mod.__package__ = fullname |
|
5086.5.3
by Vincent Ladeuil
First shot at loading plugins from a specific directory. |
701 |
return mod |
702 |
finally: |
|
|
5268.6.3
by Vincent Ladeuil
BZR_PLUGINS_AT should use packages properly to handle relative imports. |
703 |
if f is not None: |
704 |
f.close() |