bzr branch
http://gegoxaren.bato24.eu/bzr/brz/remove-bazaar
| 5086.1.1
by Vincent Ladeuil Remove get_default_plugin_path() obsoleted since 2.0. | 1 | # Copyright (C) 2005-2010 Canonical Ltd
 | 
| 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 | |
| 17 | ||
| 2256.2.2
by Robert Collins Allow 'import bzrlib.plugins.NAME' to work when the plugin NAME has not | 18 | """bzr python plugin support.
 | 
| 19 | ||
| 20 | When load_plugins() is invoked, any python module in any directory in
 | |
| 21 | $BZR_PLUGIN_PATH will be imported.  The module will be imported as
 | |
| 22 | 'bzrlib.plugins.$BASENAME(PLUGIN)'.  In the plugin's main body, it should
 | |
| 3620.4.1
by Robert Collins plugin doc strings update. | 23 | update any bzrlib registries it wants to extend.
 | 
| 24 | ||
| 25 | See the plugin-api developer documentation for information about writing
 | |
| 26 | plugins.
 | |
| 2256.2.2
by Robert Collins Allow 'import bzrlib.plugins.NAME' to work when the plugin NAME has not | 27 | |
| 28 | BZR_PLUGIN_PATH is also honoured for any plugins imported via
 | |
| 3943.8.1
by Marius Kruger remove all trailing whitespace from bzr source | 29 | 'import bzrlib.plugins.PLUGINNAME', as long as set_plugins_path has been
 | 
| 2256.2.2
by Robert Collins Allow 'import bzrlib.plugins.NAME' to work when the plugin NAME has not | 30 | called.
 | 
| 1185.16.83
by mbp at sourcefrog - notes on testability of plugins | 31 | """
 | 
| 32 | ||
| 1393.2.1
by John Arbash Meinel Merged in split-storage-2 branch. Need to cleanup a little bit more still. | 33 | import os | 
| 1185.16.82
by mbp at sourcefrog - give a quieter warning if a plugin can't be loaded | 34 | import sys | 
| 1996.3.17
by John Arbash Meinel lazy_import plugin and transport/local | 35 | |
| 3794.1.1
by Martin Pool Update osutils imports to fix setup.py on Windows | 36 | from bzrlib import osutils | 
| 37 | ||
| 1996.3.17
by John Arbash Meinel lazy_import plugin and transport/local | 38 | from bzrlib.lazy_import import lazy_import | 
| 3794.1.1
by Martin Pool Update osutils imports to fix setup.py on Windows | 39 | |
| 1996.3.17
by John Arbash Meinel lazy_import plugin and transport/local | 40 | lazy_import(globals(), """ | 
| 41 | import imp
 | |
| 2256.2.3
by Robert Collins Review feedback. | 42 | import re
 | 
| 1516
by Robert Collins * bzrlib.plugin.all_plugins has been changed from an attribute to a | 43 | import types
 | 
| 1185.16.82
by mbp at sourcefrog - give a quieter warning if a plugin can't be loaded | 44 | |
| 1996.3.17
by John Arbash Meinel lazy_import plugin and transport/local | 45 | from bzrlib import (
 | 
| 3777.6.3
by Marius Kruger Use bzrlib._format_version_tuple and map as per review from John. | 46 |     _format_version_tuple,
 | 
| 3224.5.10
by Andrew Bennetts Replace some duplication with a different form of hackery. | 47 |     config,
 | 
| 3427.2.2
by James Westby Just print the exception, keeping the API of log_exception_quietly the same. | 48 |     debug,
 | 
| 3766.3.2
by Robert Collins Fix reporting of incompatible api plugin load errors, fixing bug 279451. | 49 |     errors,
 | 
| 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. | 50 |     trace,
 | 
| 1996.3.17
by John Arbash Meinel lazy_import plugin and transport/local | 51 |     )
 | 
| 2762.2.1
by Robert Collins * ``bzr plugins`` now lists the version number for each plugin in square | 52 | from bzrlib import plugins as _mod_plugins
 | 
| 1996.3.17
by John Arbash Meinel lazy_import plugin and transport/local | 53 | """) | 
| 54 | ||
| 4628.2.5
by Vincent Ladeuil Fixes prompted by review. | 55 | from bzrlib.symbol_versioning import ( | 
| 56 | deprecated_function, | |
| 57 | deprecated_in, | |
| 58 |     )
 | |
| 1996.3.17
by John Arbash Meinel lazy_import plugin and transport/local | 59 | |
| 60 | ||
| 61 | DEFAULT_PLUGIN_PATH = None | |
| 2256.2.2
by Robert Collins Allow 'import bzrlib.plugins.NAME' to work when the plugin NAME has not | 62 | _loaded = False | 
| 5017.2.2
by Martin Pool Add import tariff tests | 63 | _plugins_disabled = False | 
| 64 | ||
| 65 | ||
| 66 | def are_plugins_disabled(): | |
| 67 | return _plugins_disabled | |
| 68 | ||
| 1996.3.17
by John Arbash Meinel lazy_import plugin and transport/local | 69 | |
| 1551.3.11
by Aaron Bentley Merge from Robert | 70 | def disable_plugins(): | 
| 71 | """Disable loading plugins. | |
| 72 | ||
| 73 |     Future calls to load_plugins() will be ignored.
 | |
| 74 |     """
 | |
| 5017.2.2
by Martin Pool Add import tariff tests | 75 | global _plugins_disabled | 
| 76 | _plugins_disabled = True | |
| 3835.2.5
by Aaron Bentley Simplify disable_plugins implementation | 77 | load_plugins([]) | 
| 1551.3.11
by Aaron Bentley Merge from Robert | 78 | |
| 3010.4.1
by Alexander Belchenko bzr.exe: enable to search system-wide plugins in "plugins" subdirectory of installation directory | 79 | |
| 2753.1.1
by Ian Clatworthy (Blake Winton) BZR_PLUGIN_PATH should ignore trailiing slashes | 80 | def _strip_trailing_sep(path): | 
| 2652.2.6
by Blake Winton Incorporate suggestions from Alexander Belchenko | 81 | return path.rstrip("\\/") | 
| 1551.3.11
by Aaron Bentley Merge from Robert | 82 | |
| 3010.4.1
by Alexander Belchenko bzr.exe: enable to search system-wide plugins in "plugins" subdirectory of installation directory | 83 | |
| 3835.2.2
by Aaron Bentley Allow specifying plugin paths, disable co-installed plugins. | 84 | def set_plugins_path(path=None): | 
| 85 | """Set the path for plugins to be loaded from. | |
| 86 | ||
| 87 |     :param path: The list of paths to search for plugins.  By default,
 | |
| 88 |         path will be determined using get_standard_plugins_path.
 | |
| 89 |         if path is [], no plugins can be loaded.
 | |
| 90 |     """
 | |
| 91 | if path is None: | |
| 92 | path = get_standard_plugins_path() | |
| 5086.5.5
by Vincent Ladeuil Use BZR_PLUGINS_AT and stop mucking with BZR_PLUGIN_PATH. | 93 | _mod_plugins.__path__ = path | 
| 5086.5.3
by Vincent Ladeuil First shot at loading plugins from a specific directory. | 94 | PluginImporter.reset() | 
| 5086.5.5
by Vincent Ladeuil Use BZR_PLUGINS_AT and stop mucking with BZR_PLUGIN_PATH. | 95 |     # Set up a blacklist for disabled plugins
 | 
| 5086.1.10
by Vincent Ladeuil Fixed as per review comments. | 96 | disabled_plugins = os.environ.get('BZR_DISABLE_PLUGINS', None) | 
| 97 | if disabled_plugins is not None: | |
| 98 | for name in disabled_plugins.split(os.pathsep): | |
| 5086.5.4
by Vincent Ladeuil Merge for fixes from 411413-plugin-path | 99 | PluginImporter.blacklist.add('bzrlib.plugins.' + name) | 
| 5086.5.5
by Vincent Ladeuil Use BZR_PLUGINS_AT and stop mucking with BZR_PLUGIN_PATH. | 100 |     # Set up a the specific paths for plugins
 | 
| 101 | specific_plugins = os.environ.get('BZR_PLUGINS_AT', None) | |
| 102 | if specific_plugins is not None: | |
| 103 | for spec in specific_plugins.split(os.pathsep): | |
| 104 | plugin_name, plugin_path = spec.split('@') | |
| 105 | PluginImporter.specific_paths[ | |
| 106 | 'bzrlib.plugins.%s' % plugin_name] = plugin_path | |
| 3835.2.2
by Aaron Bentley Allow specifying plugin paths, disable co-installed plugins. | 107 | return path | 
| 108 | ||
| 109 | ||
| 4628.2.1
by Vincent Ladeuil Start introducing accessors for plugin paths. | 110 | def _append_new_path(paths, new_path): | 
| 111 | """Append a new path if it set and not already known.""" | |
| 112 | if new_path is not None and new_path not in paths: | |
| 113 | paths.append(new_path) | |
| 114 | return paths | |
| 115 | ||
| 116 | ||
| 117 | def get_core_plugin_path(): | |
| 118 | core_path = None | |
| 119 | bzr_exe = bool(getattr(sys, 'frozen', None)) | |
| 120 | if bzr_exe: # expand path for bzr.exe | |
| 121 |         # We need to use relative path to system-wide plugin
 | |
| 122 |         # directory because bzrlib from standalone bzr.exe
 | |
| 123 |         # could be imported by another standalone program
 | |
| 124 |         # (e.g. bzr-config; or TortoiseBzr/Olive if/when they
 | |
| 125 |         # will become standalone exe). [bialix 20071123]
 | |
| 126 |         # __file__ typically is
 | |
| 127 |         # C:\Program Files\Bazaar\lib\library.zip\bzrlib\plugin.pyc
 | |
| 128 |         # then plugins directory is
 | |
| 129 |         # C:\Program Files\Bazaar\plugins
 | |
| 130 |         # so relative path is ../../../plugins
 | |
| 131 | core_path = osutils.abspath(osutils.pathjoin( | |
| 132 | osutils.dirname(__file__), '../../../plugins')) | |
| 133 | else: # don't look inside library.zip | |
| 134 |         # search the plugin path before the bzrlib installed dir
 | |
| 135 | core_path = os.path.dirname(_mod_plugins.__file__) | |
| 136 | return core_path | |
| 137 | ||
| 138 | ||
| 139 | def get_site_plugin_path(): | |
| 140 | """Returns the path for the site installed plugins.""" | |
| 4628.2.5
by Vincent Ladeuil Fixes prompted by review. | 141 | if sys.platform == 'win32': | 
| 142 |         # We don't have (yet) a good answer for windows since that is certainly
 | |
| 143 |         # related to the way we build the installers. -- vila20090821
 | |
| 144 | return None | |
| 4628.2.1
by Vincent Ladeuil Start introducing accessors for plugin paths. | 145 | site_path = None | 
| 146 | try: | |
| 147 | from distutils.sysconfig import get_python_lib | |
| 148 | except ImportError: | |
| 149 |         # If distutuils is not available, we just don't know where they are
 | |
| 150 |         pass
 | |
| 151 | else: | |
| 152 | site_path = osutils.pathjoin(get_python_lib(), 'bzrlib', 'plugins') | |
| 153 | return site_path | |
| 154 | ||
| 155 | ||
| 156 | def get_user_plugin_path(): | |
| 157 | return osutils.pathjoin(config.config_dir(), 'plugins') | |
| 158 | ||
| 159 | ||
| 3835.2.2
by Aaron Bentley Allow specifying plugin paths, disable co-installed plugins. | 160 | def get_standard_plugins_path(): | 
| 161 | """Determine a plugin path suitable for general use.""" | |
| 4628.2.2
by Vincent Ladeuil Add [+-]{user|core|site} handling in BZR_PLUGIN_PATH. | 162 |     # Ad-Hoc default: core is not overriden by site but user can overrides both
 | 
| 4628.2.3
by Vincent Ladeuil Update doc and add NEWS entry. | 163 |     # The rationale is that:
 | 
| 164 |     # - 'site' comes last, because these plugins should always be available and
 | |
| 165 |     #   are supposed to be in sync with the bzr installed on site.
 | |
| 166 |     # - 'core' comes before 'site' so that running bzr from sources or a user
 | |
| 167 |     #   installed version overrides the site version.
 | |
| 168 |     # - 'user' comes first, because... user is always right.
 | |
| 169 |     # - the above rules clearly defines which plugin version will be loaded if
 | |
| 170 |     #   several exist. Yet, it is sometimes desirable to disable some directory
 | |
| 4672.1.1
by Vincent Ladeuil BZR_PLUGIN_PATH can be used to fully control the plugin directories | 171 |     #   so that a set of plugins is disabled as once. This can be done via
 | 
| 4628.2.3
by Vincent Ladeuil Update doc and add NEWS entry. | 172 |     #   -site, -core, -user.
 | 
| 173 | ||
| 174 | env_paths = os.environ.get('BZR_PLUGIN_PATH', '+user').split(os.pathsep) | |
| 175 | defaults = ['+core', '+site'] | |
| 4628.2.2
by Vincent Ladeuil Add [+-]{user|core|site} handling in BZR_PLUGIN_PATH. | 176 | |
| 177 |     # The predefined references
 | |
| 178 | refs = dict(core=get_core_plugin_path(), | |
| 179 | site=get_site_plugin_path(), | |
| 180 | user=get_user_plugin_path()) | |
| 181 | ||
| 4628.2.5
by Vincent Ladeuil Fixes prompted by review. | 182 |     # Unset paths that should be removed
 | 
| 183 | for k,v in refs.iteritems(): | |
| 184 | removed = '-%s' % k | |
| 185 |         # defaults can never mention removing paths as that will make it
 | |
| 186 |         # impossible for the user to revoke these removals.
 | |
| 187 | if removed in env_paths: | |
| 188 | env_paths.remove(removed) | |
| 189 | refs[k] = None | |
| 190 | ||
| 191 |     # Expand references
 | |
| 192 | paths = [] | |
| 193 | for p in env_paths + defaults: | |
| 194 | if p.startswith('+'): | |
| 5086.1.2
by Vincent Ladeuil Cosmetic changes. | 195 |             # Resolve references if they are known
 | 
| 4628.2.5
by Vincent Ladeuil Fixes prompted by review. | 196 | try: | 
| 197 | p = refs[p[1:]] | |
| 198 | except KeyError: | |
| 5086.1.6
by Vincent Ladeuil Crude fix for bug #411413. | 199 |                 # Leave them untouched so user can still use paths starting
 | 
| 200 |                 # with '+'
 | |
| 4628.2.5
by Vincent Ladeuil Fixes prompted by review. | 201 |                 pass
 | 
| 202 | _append_new_path(paths, p) | |
| 4628.2.2
by Vincent Ladeuil Add [+-]{user|core|site} handling in BZR_PLUGIN_PATH. | 203 | |
| 3835.2.7
by Aaron Bentley Add tests for plugins | 204 |     # Get rid of trailing slashes, since Python can't handle them when
 | 
| 205 |     # it tries to import modules.
 | |
| 4628.2.2
by Vincent Ladeuil Add [+-]{user|core|site} handling in BZR_PLUGIN_PATH. | 206 | paths = map(_strip_trailing_sep, paths) | 
| 207 | return paths | |
| 2256.2.2
by Robert Collins Allow 'import bzrlib.plugins.NAME' to work when the plugin NAME has not | 208 | |
| 209 | ||
| 3835.2.4
by Aaron Bentley Allow paths to be specified to load_plugins | 210 | def load_plugins(path=None): | 
| 1515
by Robert Collins * Plugins with the same name in different directories in the bzr plugin | 211 | """Load bzrlib plugins. | 
| 1393.2.1
by John Arbash Meinel Merged in split-storage-2 branch. Need to cleanup a little bit more still. | 212 | |
| 213 |     The environment variable BZR_PLUGIN_PATH is considered a delimited
 | |
| 214 |     set of paths to look through. Each entry is searched for *.py
 | |
| 215 |     files (and whatever other extensions are used in the platform,
 | |
| 216 |     such as *.pyd).
 | |
| 1515
by Robert Collins * Plugins with the same name in different directories in the bzr plugin | 217 | |
| 5086.1.6
by Vincent Ladeuil Crude fix for bug #411413. | 218 |     load_from_path() provides the underlying mechanism and is called with
 | 
| 1515
by Robert Collins * Plugins with the same name in different directories in the bzr plugin | 219 |     the default directory list to provide the normal behaviour.
 | 
| 3835.2.4
by Aaron Bentley Allow paths to be specified to load_plugins | 220 | |
| 221 |     :param path: The list of paths to search for plugins.  By default,
 | |
| 222 |         path will be determined using get_standard_plugins_path.
 | |
| 223 |         if path is [], no plugins can be loaded.
 | |
| 1393.2.1
by John Arbash Meinel Merged in split-storage-2 branch. Need to cleanup a little bit more still. | 224 |     """
 | 
| 1515
by Robert Collins * Plugins with the same name in different directories in the bzr plugin | 225 | global _loaded | 
| 1393.2.1
by John Arbash Meinel Merged in split-storage-2 branch. Need to cleanup a little bit more still. | 226 | if _loaded: | 
| 227 |         # People can make sure plugins are loaded, they just won't be twice
 | |
| 228 |         return
 | |
| 229 | _loaded = True | |
| 230 | ||
| 2256.2.2
by Robert Collins Allow 'import bzrlib.plugins.NAME' to work when the plugin NAME has not | 231 |     # scan for all plugins in the path.
 | 
| 3835.2.4
by Aaron Bentley Allow paths to be specified to load_plugins | 232 | load_from_path(set_plugins_path(path)) | 
| 2256.2.2
by Robert Collins Allow 'import bzrlib.plugins.NAME' to work when the plugin NAME has not | 233 | |
| 234 | ||
| 235 | def load_from_path(dirs): | |
| 1515
by Robert Collins * Plugins with the same name in different directories in the bzr plugin | 236 | """Load bzrlib plugins found in each dir in dirs. | 
| 237 | ||
| 238 |     Loading a plugin means importing it into the python interpreter.
 | |
| 239 |     The plugin is expected to make calls to register commands when
 | |
| 240 |     it's loaded (or perhaps access other hooks in future.)
 | |
| 241 | ||
| 242 |     Plugins are loaded into bzrlib.plugins.NAME, and can be found there
 | |
| 243 |     for future reference.
 | |
| 2256.2.2
by Robert Collins Allow 'import bzrlib.plugins.NAME' to work when the plugin NAME has not | 244 | |
| 245 |     The python module path for bzrlib.plugins will be modified to be 'dirs'.
 | |
| 2652.2.5
by Blake Winton Get rid of CRs. | 246 |     """
 | 
| 5086.5.10
by Vincent Ladeuil Cleanup docs. | 247 |     # Explicitly load the plugins with a specific path
 | 
| 5086.5.8
by Vincent Ladeuil Make sure we can load from a non-standard directory name. | 248 | for fullname, path in PluginImporter.specific_paths.iteritems(): | 
| 249 | name = fullname[len('bzrlib.plugins.'):] | |
| 5086.5.10
by Vincent Ladeuil Cleanup docs. | 250 | _load_plugin_module(name, path) | 
| 5086.5.8
by Vincent Ladeuil Make sure we can load from a non-standard directory name. | 251 | |
| 2652.2.5
by Blake Winton Get rid of CRs. | 252 |     # We need to strip the trailing separators here as well as in the
 | 
| 253 |     # set_plugins_path function because calling code can pass anything in to
 | |
| 254 |     # this function, and since it sets plugins.__path__, it should set it to
 | |
| 255 |     # something that will be valid for Python to use (in case people try to
 | |
| 2652.2.4
by Blake Winton Add a note explaining why I strip the slashes twice. | 256 |     # run "import bzrlib.plugins.PLUGINNAME" after calling this function).
 | 
| 2762.2.1
by Robert Collins * ``bzr plugins`` now lists the version number for each plugin in square | 257 | _mod_plugins.__path__ = map(_strip_trailing_sep, dirs) | 
| 2256.2.2
by Robert Collins Allow 'import bzrlib.plugins.NAME' to work when the plugin NAME has not | 258 | for d in dirs: | 
| 259 | if not d: | |
| 260 |             continue
 | |
| 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. | 261 | trace.mutter('looking for plugins in %s', d) | 
| 2256.2.2
by Robert Collins Allow 'import bzrlib.plugins.NAME' to work when the plugin NAME has not | 262 | if os.path.isdir(d): | 
| 263 | load_from_dir(d) | |
| 264 | ||
| 265 | ||
| 266 | # backwards compatability: load_from_dirs was the old name
 | |
| 267 | # This was changed in 0.15
 | |
| 268 | load_from_dirs = load_from_path | |
| 269 | ||
| 270 | ||
| 5086.5.3
by Vincent Ladeuil First shot at loading plugins from a specific directory. | 271 | def _find_plugin_module(dir, name): | 
| 5086.5.1
by Vincent Ladeuil Slight refactoring preparing fix for bug #82693. | 272 | """Check if there is a valid python module that can be loaded as a plugin. | 
| 273 | ||
| 5086.5.10
by Vincent Ladeuil Cleanup docs. | 274 |     :param dir: The directory where the search is performed.
 | 
| 5086.5.1
by Vincent Ladeuil Slight refactoring preparing fix for bug #82693. | 275 |     :param path: An existing file path, either a python file or a package
 | 
| 276 |         directory.
 | |
| 277 | ||
| 278 |     :return: (name, path, description) name is the module name, path is the
 | |
| 279 |         file to load and description is the tuple returned by
 | |
| 280 |         imp.get_suffixes().
 | |
| 281 |     """
 | |
| 282 | path = osutils.pathjoin(dir, name) | |
| 283 | if os.path.isdir(path): | |
| 284 |         # Check for a valid __init__.py file, valid suffixes depends on -O and
 | |
| 285 |         # can be .py, .pyc and .pyo
 | |
| 286 | for suffix, mode, kind in imp.get_suffixes(): | |
| 5086.5.3
by Vincent Ladeuil First shot at loading plugins from a specific directory. | 287 | if kind not in (imp.PY_SOURCE, imp.PY_COMPILED): | 
| 5086.5.1
by Vincent Ladeuil Slight refactoring preparing fix for bug #82693. | 288 |                 # We don't recognize compiled modules (.so, .dll, etc)
 | 
| 289 |                 continue
 | |
| 5086.5.3
by Vincent Ladeuil First shot at loading plugins from a specific directory. | 290 | init_path = osutils.pathjoin(path, '__init__' + suffix) | 
| 291 | if os.path.isfile(init_path): | |
| 292 | return name, init_path, (suffix, mode, kind) | |
| 5086.5.1
by Vincent Ladeuil Slight refactoring preparing fix for bug #82693. | 293 | else: | 
| 294 | for suffix, mode, kind in imp.get_suffixes(): | |
| 295 | if name.endswith(suffix): | |
| 296 |                 # Clean up the module name
 | |
| 297 | name = name[:-len(suffix)] | |
| 298 | if kind == imp.C_EXTENSION and name.endswith('module'): | |
| 299 | name = name[:-len('module')] | |
| 300 | return name, path, (suffix, mode, kind) | |
| 301 |     # There is no python module here
 | |
| 302 | return None, None, (None, None, None) | |
| 303 | ||
| 304 | ||
| 5086.5.8
by Vincent Ladeuil Make sure we can load from a non-standard directory name. | 305 | def _load_plugin_module(name, dir): | 
| 5086.5.14
by Vincent Ladeuil Fix bug #552922 by controlling which files can be used to load a plugin. | 306 | """Load plugin name from dir. | 
| 5086.5.10
by Vincent Ladeuil Cleanup docs. | 307 | |
| 308 |     :param name: The plugin name in the bzrlib.plugins namespace.
 | |
| 309 |     :param dir: The directory the plugin is loaded from for error messages.
 | |
| 310 |     """
 | |
| 5086.5.8
by Vincent Ladeuil Make sure we can load from a non-standard directory name. | 311 | if ('bzrlib.plugins.%s' % name) in PluginImporter.blacklist: | 
| 312 |         return
 | |
| 313 | try: | |
| 314 | exec "import bzrlib.plugins.%s" % name in {} | |
| 315 | except KeyboardInterrupt: | |
| 316 |         raise
 | |
| 317 | except errors.IncompatibleAPI, e: | |
| 318 | trace.warning("Unable to load plugin %r. It requested API version " | |
| 319 | "%s of module %s but the minimum exported version is %s, and " | |
| 320 | "the maximum is %s" % | |
| 321 | (name, e.wanted, e.api, e.minimum, e.current)) | |
| 322 | except Exception, e: | |
| 323 | trace.warning("%s" % e) | |
| 324 | if re.search('\.|-| ', name): | |
| 325 | sanitised_name = re.sub('[-. ]', '_', name) | |
| 326 | if sanitised_name.startswith('bzr_'): | |
| 327 | sanitised_name = sanitised_name[len('bzr_'):] | |
| 328 | trace.warning("Unable to load %r in %r as a plugin because the " | |
| 329 |                     "file path isn't a valid module name; try renaming "
 | |
| 330 | "it to %r." % (name, dir, sanitised_name)) | |
| 331 | else: | |
| 332 | trace.warning('Unable to load plugin %r from %r' % (name, dir)) | |
| 333 | trace.log_exception_quietly() | |
| 334 | if 'error' in debug.debug_flags: | |
| 335 | trace.print_exception(sys.exc_info(), sys.stderr) | |
| 336 | ||
| 337 | ||
| 2256.2.2
by Robert Collins Allow 'import bzrlib.plugins.NAME' to work when the plugin NAME has not | 338 | def load_from_dir(d): | 
| 3766.3.2
by Robert Collins Fix reporting of incompatible api plugin load errors, fixing bug 279451. | 339 | """Load the plugins in directory d. | 
| 3943.8.1
by Marius Kruger remove all trailing whitespace from bzr source | 340 | |
| 3766.3.2
by Robert Collins Fix reporting of incompatible api plugin load errors, fixing bug 279451. | 341 |     d must be in the plugins module path already.
 | 
| 5086.5.1
by Vincent Ladeuil Slight refactoring preparing fix for bug #82693. | 342 |     This function is called once for each directory in the module path.
 | 
| 3766.3.2
by Robert Collins Fix reporting of incompatible api plugin load errors, fixing bug 279451. | 343 |     """
 | 
| 2256.2.2
by Robert Collins Allow 'import bzrlib.plugins.NAME' to work when the plugin NAME has not | 344 | plugin_names = set() | 
| 5086.5.1
by Vincent Ladeuil Slight refactoring preparing fix for bug #82693. | 345 | for p in os.listdir(d): | 
| 5086.5.3
by Vincent Ladeuil First shot at loading plugins from a specific directory. | 346 | name, path, desc = _find_plugin_module(d, p) | 
| 5086.5.1
by Vincent Ladeuil Slight refactoring preparing fix for bug #82693. | 347 | if name is not None: | 
| 348 | if name == '__init__': | |
| 349 |                 # We do nothing with the __init__.py file in directories from
 | |
| 350 |                 # the bzrlib.plugins module path, we may want to, one day
 | |
| 351 |                 # -- vila 20100316.
 | |
| 352 | continue # We don't load __init__.py in the plugins dirs | |
| 353 | elif getattr(_mod_plugins, name, None) is not None: | |
| 354 |                 # The module has already been loaded from another directory
 | |
| 355 |                 # during a previous call.
 | |
| 356 |                 # FIXME: There should be a better way to report masked plugins
 | |
| 357 |                 # -- vila 20100316
 | |
| 358 | trace.mutter('Plugin name %s already loaded', name) | |
| 2256.2.2
by Robert Collins Allow 'import bzrlib.plugins.NAME' to work when the plugin NAME has not | 359 | else: | 
| 5086.5.1
by Vincent Ladeuil Slight refactoring preparing fix for bug #82693. | 360 | plugin_names.add(name) | 
| 3943.8.1
by Marius Kruger remove all trailing whitespace from bzr source | 361 | |
| 2256.2.2
by Robert Collins Allow 'import bzrlib.plugins.NAME' to work when the plugin NAME has not | 362 | for name in plugin_names: | 
| 5086.5.8
by Vincent Ladeuil Make sure we can load from a non-standard directory name. | 363 | _load_plugin_module(name, d) | 
| 2256.2.2
by Robert Collins Allow 'import bzrlib.plugins.NAME' to work when the plugin NAME has not | 364 | |
| 365 | ||
| 2762.2.1
by Robert Collins * ``bzr plugins`` now lists the version number for each plugin in square | 366 | def plugins(): | 
| 367 | """Return a dictionary of the plugins. | |
| 3943.8.1
by Marius Kruger remove all trailing whitespace from bzr source | 368 | |
| 2762.2.1
by Robert Collins * ``bzr plugins`` now lists the version number for each plugin in square | 369 |     Each item in the dictionary is a PlugIn object.
 | 
| 370 |     """
 | |
| 371 | result = {} | |
| 372 | for name, plugin in _mod_plugins.__dict__.items(): | |
| 373 | if isinstance(plugin, types.ModuleType): | |
| 374 | result[name] = PlugIn(name, plugin) | |
| 375 | return result | |
| 376 | ||
| 377 | ||
| 2432.1.24
by Robert Collins Add plugins as a help index. | 378 | class PluginsHelpIndex(object): | 
| 379 | """A help index that returns help topics for plugins.""" | |
| 380 | ||
| 381 | def __init__(self): | |
| 382 | self.prefix = 'plugins/' | |
| 383 | ||
| 384 | def get_topics(self, topic): | |
| 2432.1.25
by Robert Collins Return plugin module docstrings for 'bzr help plugin'. | 385 | """Search for topic in the loaded plugins. | 
| 386 | ||
| 387 |         This will not trigger loading of new plugins.
 | |
| 388 | ||
| 389 |         :param topic: A topic to search for.
 | |
| 390 |         :return: A list which is either empty or contains a single
 | |
| 391 |             RegisteredTopic entry.
 | |
| 392 |         """
 | |
| 393 | if not topic: | |
| 394 | return [] | |
| 395 | if topic.startswith(self.prefix): | |
| 396 | topic = topic[len(self.prefix):] | |
| 397 | plugin_module_name = 'bzrlib.plugins.%s' % topic | |
| 398 | try: | |
| 399 | module = sys.modules[plugin_module_name] | |
| 400 | except KeyError: | |
| 401 | return [] | |
| 402 | else: | |
| 403 | return [ModuleHelpTopic(module)] | |
| 404 | ||
| 405 | ||
| 406 | class ModuleHelpTopic(object): | |
| 407 | """A help topic which returns the docstring for a module.""" | |
| 408 | ||
| 409 | def __init__(self, module): | |
| 410 | """Constructor. | |
| 411 | ||
| 412 |         :param module: The module for which help should be generated.
 | |
| 413 |         """
 | |
| 414 | self.module = module | |
| 415 | ||
| 3984.4.5
by Ian Clatworthy help xxx is full help; xxx -h is concise help | 416 | 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'. | 417 | """Return a string with the help for this topic. | 
| 418 | ||
| 419 |         :param additional_see_also: Additional help topics to be
 | |
| 420 |             cross-referenced.
 | |
| 421 |         """
 | |
| 422 | if not self.module.__doc__: | |
| 423 | result = "Plugin '%s' has no docstring.\n" % self.module.__name__ | |
| 424 | else: | |
| 425 | result = self.module.__doc__ | |
| 426 | if result[-1] != '\n': | |
| 427 | result += '\n' | |
| 3943.8.1
by Marius Kruger remove all trailing whitespace from bzr source | 428 |         # there is code duplicated here and in bzrlib/help_topic.py's
 | 
| 2432.1.25
by Robert Collins Return plugin module docstrings for 'bzr help plugin'. | 429 |         # matching Topic code. This should probably be factored in
 | 
| 430 |         # to a helper function and a common base class.
 | |
| 431 | if additional_see_also is not None: | |
| 432 | see_also = sorted(set(additional_see_also)) | |
| 433 | else: | |
| 434 | see_also = None | |
| 435 | if see_also: | |
| 436 | result += 'See also: ' | |
| 437 | result += ', '.join(see_also) | |
| 438 | result += '\n' | |
| 439 | return result | |
| 2432.1.29
by Robert Collins Add get_help_topic to ModuleHelpTopic. | 440 | |
| 441 | def get_help_topic(self): | |
| 2432.1.30
by Robert Collins Fix the ModuleHelpTopic get_help_topic to be tested with closer to real world data and strip the bzrlib.plugins. prefix from the name. | 442 | """Return the modules help topic - its __name__ after bzrlib.plugins..""" | 
| 443 | return self.module.__name__[len('bzrlib.plugins.'):] | |
| 2762.2.1
by Robert Collins * ``bzr plugins`` now lists the version number for each plugin in square | 444 | |
| 445 | ||
| 446 | class PlugIn(object): | |
| 447 | """The bzrlib representation of a plugin. | |
| 448 | ||
| 449 |     The PlugIn object provides a way to manipulate a given plugin module.
 | |
| 450 |     """
 | |
| 451 | ||
| 452 | def __init__(self, name, module): | |
| 453 | """Construct a plugin for module.""" | |
| 454 | self.name = name | |
| 455 | self.module = module | |
| 456 | ||
| 457 | def path(self): | |
| 458 | """Get the path that this plugin was loaded from.""" | |
| 459 | if getattr(self.module, '__path__', None) is not None: | |
| 460 | return os.path.abspath(self.module.__path__[0]) | |
| 461 | 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 | 462 | path = os.path.abspath(self.module.__file__) | 
| 463 | if path[-4:] in ('.pyc', '.pyo'): | |
| 464 | pypath = path[:-4] + '.py' | |
| 465 | if os.path.isfile(pypath): | |
| 466 | path = pypath | |
| 467 | return path | |
| 2762.2.1
by Robert Collins * ``bzr plugins`` now lists the version number for each plugin in square | 468 | else: | 
| 469 | return repr(self.module) | |
| 470 | ||
| 471 | def __str__(self): | |
| 472 | return "<%s.%s object at %s, name=%s, module=%s>" % ( | |
| 473 | self.__class__.__module__, self.__class__.__name__, id(self), | |
| 474 | self.name, self.module) | |
| 475 | ||
| 476 | __repr__ = __str__ | |
| 477 | ||
| 478 | def test_suite(self): | |
| 479 | """Return the plugin's test suite.""" | |
| 480 | if getattr(self.module, 'test_suite', None) is not None: | |
| 481 | return self.module.test_suite() | |
| 482 | else: | |
| 483 | return None | |
| 484 | ||
| 3302.8.21
by Vincent Ladeuil Fixed as per Robert's review. | 485 | def load_plugin_tests(self, loader): | 
| 3302.8.10
by Vincent Ladeuil Prepare bzrlib.plugin to use the new test loader. | 486 | """Return the adapted plugin's test suite. | 
| 487 | ||
| 488 |         :param loader: The custom loader that should be used to load additional
 | |
| 489 |             tests.
 | |
| 490 | ||
| 491 |         """
 | |
| 492 | if getattr(self.module, 'load_tests', None) is not None: | |
| 3302.8.11
by Vincent Ladeuil Simplify plugin.load_tests. | 493 | return loader.loadTestsFromModule(self.module) | 
| 3302.8.10
by Vincent Ladeuil Prepare bzrlib.plugin to use the new test loader. | 494 | else: | 
| 495 | return None | |
| 496 | ||
| 2762.2.1
by Robert Collins * ``bzr plugins`` now lists the version number for each plugin in square | 497 | def version_info(self): | 
| 498 | """Return the plugin's version_tuple or None if unknown.""" | |
| 499 | version_info = getattr(self.module, 'version_info', None) | |
| 3777.6.7
by Marius Kruger * Can now also handle non-iteratable and string plugin versions. | 500 | if version_info is not None: | 
| 501 | try: | |
| 502 | if isinstance(version_info, types.StringType): | |
| 503 | version_info = version_info.split('.') | |
| 504 | elif len(version_info) == 3: | |
| 505 | version_info = tuple(version_info) + ('final', 0) | |
| 506 | except TypeError, e: | |
| 507 |                 # The given version_info isn't even iteratible
 | |
| 508 | trace.log_exception_quietly() | |
| 509 | version_info = (version_info,) | |
| 2762.2.1
by Robert Collins * ``bzr plugins`` now lists the version number for each plugin in square | 510 | return version_info | 
| 3302.8.10
by Vincent Ladeuil Prepare bzrlib.plugin to use the new test loader. | 511 | |
| 2762.2.1
by Robert Collins * ``bzr plugins`` now lists the version number for each plugin in square | 512 | def _get__version__(self): | 
| 513 | version_info = self.version_info() | |
| 3777.6.1
by Marius Kruger Try to return something usefull for plugins with bad version numbers, | 514 | 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 | 515 | return "unknown" | 
| 3777.6.1
by Marius Kruger Try to return something usefull for plugins with bad version numbers, | 516 | try: | 
| 3777.6.3
by Marius Kruger Use bzrlib._format_version_tuple and map as per review from John. | 517 | version_string = _format_version_tuple(version_info) | 
| 3777.6.6
by Marius Kruger catch only ValueError, TypeError, IndexError as per feedback from John | 518 | except (ValueError, TypeError, IndexError), e: | 
| 519 | trace.log_exception_quietly() | |
| 3777.6.1
by Marius Kruger Try to return something usefull for plugins with bad version numbers, | 520 |             # try to return something usefull for bad plugins, in stead of
 | 
| 521 |             # stack tracing.
 | |
| 3777.6.3
by Marius Kruger Use bzrlib._format_version_tuple and map as per review from John. | 522 | 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 | 523 | return version_string | 
| 524 | ||
| 525 | __version__ = property(_get__version__) | |
| 5086.1.6
by Vincent Ladeuil Crude fix for bug #411413. | 526 | |
| 527 | ||
| 5086.5.3
by Vincent Ladeuil First shot at loading plugins from a specific directory. | 528 | class _PluginImporter(object): | 
| 5086.5.10
by Vincent Ladeuil Cleanup docs. | 529 | """An importer tailored to bzr specific needs. | 
| 530 | ||
| 531 |     This is a singleton that takes care of:
 | |
| 532 |     - disabled plugins specified in 'blacklist',
 | |
| 533 |     - plugins that needs to be loaded from specific directories.
 | |
| 534 |     """
 | |
| 5086.1.7
by Vincent Ladeuil Cleaner fix for bug #411413. | 535 | |
| 536 | def __init__(self): | |
| 5086.5.3
by Vincent Ladeuil First shot at loading plugins from a specific directory. | 537 | self.reset() | 
| 538 | ||
| 539 | def reset(self): | |
| 540 | self.blacklist = set() | |
| 541 | self.specific_paths = {} | |
| 5086.1.6
by Vincent Ladeuil Crude fix for bug #411413. | 542 | |
| 543 | def find_module(self, fullname, parent_path=None): | |
| 5086.5.10
by Vincent Ladeuil Cleanup docs. | 544 | """Search a plugin module. | 
| 545 | ||
| 546 |         Disabled plugins raise an import error, plugins with specific paths
 | |
| 547 |         returns a specific loader.
 | |
| 548 | ||
| 549 |         :return: None if the plugin doesn't need special handling, self
 | |
| 550 |             otherwise.
 | |
| 551 |         """
 | |
| 5086.5.3
by Vincent Ladeuil First shot at loading plugins from a specific directory. | 552 | if not fullname.startswith('bzrlib.plugins.'): | 
| 553 | return None | |
| 5086.1.7
by Vincent Ladeuil Cleaner fix for bug #411413. | 554 | if fullname in self.blacklist: | 
| 5086.1.6
by Vincent Ladeuil Crude fix for bug #411413. | 555 | raise ImportError('%s is disabled' % fullname) | 
| 5086.5.3
by Vincent Ladeuil First shot at loading plugins from a specific directory. | 556 | if fullname in self.specific_paths: | 
| 557 | return self | |
| 5086.1.6
by Vincent Ladeuil Crude fix for bug #411413. | 558 | return None | 
| 5086.1.7
by Vincent Ladeuil Cleaner fix for bug #411413. | 559 | |
| 5086.5.3
by Vincent Ladeuil First shot at loading plugins from a specific directory. | 560 | def load_module(self, fullname): | 
| 5086.5.10
by Vincent Ladeuil Cleanup docs. | 561 | """Load a plugin from a specific directory.""" | 
| 5086.5.3
by Vincent Ladeuil First shot at loading plugins from a specific directory. | 562 |         # We are called only for specific paths
 | 
| 5086.5.14
by Vincent Ladeuil Fix bug #552922 by controlling which files can be used to load a plugin. | 563 | plugin_path = self.specific_paths[fullname] | 
| 564 | loading_path = None | |
| 565 | package = False | |
| 566 | if os.path.isdir(plugin_path): | |
| 567 | for suffix, mode, kind in imp.get_suffixes(): | |
| 568 | if kind not in (imp.PY_SOURCE, imp.PY_COMPILED): | |
| 569 |                     # We don't recognize compiled modules (.so, .dll, etc)
 | |
| 570 |                     continue
 | |
| 571 | init_path = osutils.pathjoin(plugin_path, '__init__' + suffix) | |
| 572 | if os.path.isfile(init_path): | |
| 573 | loading_path = init_path | |
| 574 | package = True | |
| 575 |                     break
 | |
| 576 | else: | |
| 577 | for suffix, mode, kind in imp.get_suffixes(): | |
| 578 | if plugin_path.endswith(suffix): | |
| 579 | loading_path = plugin_path | |
| 580 |                     break
 | |
| 581 | if loading_path is None: | |
| 5086.5.3
by Vincent Ladeuil First shot at loading plugins from a specific directory. | 582 | 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. | 583 | % (fullname, plugin_path)) | 
| 584 | f = open(loading_path, mode) | |
| 5086.5.3
by Vincent Ladeuil First shot at loading plugins from a specific directory. | 585 | try: | 
| 5086.5.14
by Vincent Ladeuil Fix bug #552922 by controlling which files can be used to load a plugin. | 586 | mod = imp.load_module(fullname, f, loading_path, | 
| 587 | (suffix, mode, kind)) | |
| 588 | if package: | |
| 589 |                 # The plugin can contain modules, so be ready
 | |
| 590 | mod.__path__ = [plugin_path] | |
| 5086.5.12
by Vincent Ladeuil Force __package__ to fix pqm failure. | 591 | mod.__package__ = fullname | 
| 5086.5.3
by Vincent Ladeuil First shot at loading plugins from a specific directory. | 592 | return mod | 
| 593 | finally: | |
| 594 | f.close() | |
| 595 | ||
| 596 | ||
| 5086.5.10
by Vincent Ladeuil Cleanup docs. | 597 | # Install a dedicated importer for plugins requiring special handling
 | 
| 5086.5.3
by Vincent Ladeuil First shot at loading plugins from a specific directory. | 598 | PluginImporter = _PluginImporter() | 
| 599 | sys.meta_path.append(PluginImporter) |