Rev 4673: (vila) BZR_PLUGIN_PATH can be used to fully control the plugin in file:///home/pqm/archives/thelove/bzr/%2Btrunk/
Canonical.com Patch Queue Manager
pqm at pqm.ubuntu.com
Fri Sep 4 17:14:56 BST 2009
At file:///home/pqm/archives/thelove/bzr/%2Btrunk/
------------------------------------------------------------
revno: 4673 [merge]
revision-id: pqm at pqm.ubuntu.com-20090904161455-0urk36t3btfjpy6r
parent: pqm at pqm.ubuntu.com-20090904024310-7a3uqlf6iruxvz6m
parent: v.ladeuil+lp at free.fr-20090904153648-f4ajhttkhs92mgjz
committer: Canonical.com Patch Queue Manager <pqm at pqm.ubuntu.com>
branch nick: +trunk
timestamp: Fri 2009-09-04 17:14:55 +0100
message:
(vila) BZR_PLUGIN_PATH can be used to fully control the plugin
directories
modified:
NEWS NEWS-20050323055033-4e00b5db738777ff
bzrlib/help_topics/en/configuration.txt configuration.txt-20060314161707-868350809502af01
bzrlib/plugin.py plugin.py-20050622060424-829b654519533d69
bzrlib/tests/test_plugins.py plugins.py-20050622075746-32002b55e5e943e9
doc/en/user-guide/plugins.txt plugins.txt-20060314145616-525099a747f3ffdd
doc/en/user-guide/writing_a_plugin.txt writing_a_plugin.txt-20071114035000-q36a9h57ps06uvnl-7
=== modified file 'NEWS'
--- a/NEWS 2009-09-04 00:49:55 +0000
+++ b/NEWS 2009-09-04 15:36:48 +0000
@@ -280,6 +280,11 @@
New Features
************
+* Give more control on BZR_PLUGIN_PATH by providing a way to refer to or
+ disable the user, site and core plugin directories.
+ (Vincent Ladeuil, #412930, #316192, #145612)
+
+
Bug Fixes
*********
=== modified file 'bzrlib/help_topics/en/configuration.txt'
--- a/bzrlib/help_topics/en/configuration.txt 2009-06-26 18:13:41 +0000
+++ b/bzrlib/help_topics/en/configuration.txt 2009-08-21 09:19:11 +0000
@@ -63,6 +63,60 @@
~~~~~~~~~~~~~~~
The path to the plugins directory that Bazaar should use.
+If not set, Bazaar will search for plugins in:
+
+* the user specific plugin directory (containing the ``user`` plugins),
+
+* the bzrlib directory (containing the ``core`` plugins),
+
+* the site specific plugin directory if applicable (containing
+ the ``site`` plugins).
+
+If ``BZR_PLUGIN_PATH`` is set in any fashion, it will change the
+the way the plugin are searched.
+
+As for the ``PATH`` variables, if multiple directories are
+specified in ``BZR_PLUGIN_PATH`` they should be separated by the
+platform specific appropriate character (':' on Unix/Linux/etc,
+';' on windows)
+
+By default if ``BZR_PLUGIN_PATH`` is set, it replaces searching
+in ``user``. However it will continue to search in ``core`` and
+``site`` unless they are explicitly removed.
+
+If you need to change the order or remove one of these
+directories, you should use special values:
+
+* ``-user``, ``-core``, ``-site`` will remove the corresponding
+ path from the default values,
+
+* ``+user``, ``+core``, ``+site`` will add the corresponding path
+ before the remaining default values (and also remove it from
+ the default values).
+
+Note that the special values 'user', 'core' and 'site' should be
+used literally, they will be substituted by the corresponding,
+platform specific, values.
+
+Examples:
+^^^^^^^^^
+
+The examples below uses ':' as the separator, windows users
+should use ';'.
+
+Overriding the default user plugin directory:
+``BZR_PLUGIN_PATH='/path/to/my/other/plugins'``
+
+Disabling the site directory while retaining the user directory:
+``BZR_PLUGIN_PATH='-site:+user'``
+
+Disabling all plugins (better achieved with --no-plugins):
+``BZR_PLUGIN_PATH='-user:-core:-site'``
+
+Overriding the default site plugin directory:
+``BZR_PLUGIN_PATH='/path/to/my/site/plugins:-site':+user``
+
+
BZRPATH
~~~~~~~
=== modified file 'bzrlib/plugin.py'
--- a/bzrlib/plugin.py 2009-03-24 01:53:42 +0000
+++ b/bzrlib/plugin.py 2009-09-04 15:36:48 +0000
@@ -52,12 +52,16 @@
from bzrlib import plugins as _mod_plugins
""")
-from bzrlib.symbol_versioning import deprecated_function
+from bzrlib.symbol_versioning import (
+ deprecated_function,
+ deprecated_in,
+ )
DEFAULT_PLUGIN_PATH = None
_loaded = False
+ at deprecated_function(deprecated_in((2, 0, 0)))
def get_default_plugin_path():
"""Get the DEFAULT_PLUGIN_PATH"""
global DEFAULT_PLUGIN_PATH
@@ -91,13 +95,15 @@
return path
-def get_standard_plugins_path():
- """Determine a plugin path suitable for general use."""
- path = os.environ.get('BZR_PLUGIN_PATH',
- get_default_plugin_path()).split(os.pathsep)
- # Get rid of trailing slashes, since Python can't handle them when
- # it tries to import modules.
- path = map(_strip_trailing_sep, path)
+def _append_new_path(paths, new_path):
+ """Append a new path if it set and not already known."""
+ if new_path is not None and new_path not in paths:
+ paths.append(new_path)
+ return paths
+
+
+def get_core_plugin_path():
+ core_path = None
bzr_exe = bool(getattr(sys, 'frozen', None))
if bzr_exe: # expand path for bzr.exe
# We need to use relative path to system-wide plugin
@@ -110,25 +116,83 @@
# then plugins directory is
# C:\Program Files\Bazaar\plugins
# so relative path is ../../../plugins
- path.append(osutils.abspath(osutils.pathjoin(
- osutils.dirname(__file__), '../../../plugins')))
- if not bzr_exe: # don't look inside library.zip
+ core_path = osutils.abspath(osutils.pathjoin(
+ osutils.dirname(__file__), '../../../plugins'))
+ else: # don't look inside library.zip
# search the plugin path before the bzrlib installed dir
- path.append(os.path.dirname(_mod_plugins.__file__))
- # search the arch independent path if we can determine that and
- # the plugin is found nowhere else
- if sys.platform != 'win32':
- try:
- from distutils.sysconfig import get_python_lib
- except ImportError:
- # If distutuils is not available, we just won't add that path
- pass
- else:
- archless_path = osutils.pathjoin(get_python_lib(), 'bzrlib',
- 'plugins')
- if archless_path not in path:
- path.append(archless_path)
- return path
+ core_path = os.path.dirname(_mod_plugins.__file__)
+ return core_path
+
+
+def get_site_plugin_path():
+ """Returns the path for the site installed plugins."""
+ if sys.platform == 'win32':
+ # We don't have (yet) a good answer for windows since that is certainly
+ # related to the way we build the installers. -- vila20090821
+ return None
+ site_path = None
+ try:
+ from distutils.sysconfig import get_python_lib
+ except ImportError:
+ # If distutuils is not available, we just don't know where they are
+ pass
+ else:
+ site_path = osutils.pathjoin(get_python_lib(), 'bzrlib', 'plugins')
+ return site_path
+
+
+def get_user_plugin_path():
+ return osutils.pathjoin(config.config_dir(), 'plugins')
+
+
+def get_standard_plugins_path():
+ """Determine a plugin path suitable for general use."""
+ # Ad-Hoc default: core is not overriden by site but user can overrides both
+ # The rationale is that:
+ # - 'site' comes last, because these plugins should always be available and
+ # are supposed to be in sync with the bzr installed on site.
+ # - 'core' comes before 'site' so that running bzr from sources or a user
+ # installed version overrides the site version.
+ # - 'user' comes first, because... user is always right.
+ # - the above rules clearly defines which plugin version will be loaded if
+ # several exist. Yet, it is sometimes desirable to disable some directory
+ # so that a set of plugins is disabled as once. This can be done via
+ # -site, -core, -user.
+
+ env_paths = os.environ.get('BZR_PLUGIN_PATH', '+user').split(os.pathsep)
+ defaults = ['+core', '+site']
+
+ # The predefined references
+ refs = dict(core=get_core_plugin_path(),
+ site=get_site_plugin_path(),
+ user=get_user_plugin_path())
+
+ # Unset paths that should be removed
+ for k,v in refs.iteritems():
+ removed = '-%s' % k
+ # defaults can never mention removing paths as that will make it
+ # impossible for the user to revoke these removals.
+ if removed in env_paths:
+ env_paths.remove(removed)
+ refs[k] = None
+
+ # Expand references
+ paths = []
+ for p in env_paths + defaults:
+ if p.startswith('+'):
+ # Resolve reference if they are known
+ try:
+ p = refs[p[1:]]
+ except KeyError:
+ # Leave them untouched otherwise, user may have paths starting
+ # with '+'...
+ pass
+ _append_new_path(paths, p)
+
+ # Get rid of trailing slashes, since Python can't handle them when
+ # it tries to import modules.
+ paths = map(_strip_trailing_sep, paths)
+ return paths
def load_plugins(path=None):
=== modified file 'bzrlib/tests/test_plugins.py'
--- a/bzrlib/tests/test_plugins.py 2009-06-10 03:56:49 +0000
+++ b/bzrlib/tests/test_plugins.py 2009-08-21 09:19:11 +0000
@@ -26,7 +26,11 @@
import sys
import zipfile
-from bzrlib import plugin, tests
+from bzrlib import (
+ osutils,
+ plugin,
+ tests,
+ )
import bzrlib.plugin
import bzrlib.plugins
import bzrlib.commands
@@ -454,41 +458,6 @@
delattr(bzrlib.plugins, 'myplug')
-class TestSetPluginsPath(TestCase):
-
- def test_set_plugins_path(self):
- """set_plugins_path should set the module __path__ correctly."""
- old_path = bzrlib.plugins.__path__
- try:
- bzrlib.plugins.__path__ = []
- expected_path = bzrlib.plugin.set_plugins_path()
- self.assertEqual(expected_path, bzrlib.plugins.__path__)
- finally:
- bzrlib.plugins.__path__ = old_path
-
- def test_set_plugins_path_with_trailing_slashes(self):
- """set_plugins_path should set the module __path__ based on
- BZR_PLUGIN_PATH after removing all trailing slashes."""
- old_path = bzrlib.plugins.__path__
- old_env = os.environ.get('BZR_PLUGIN_PATH')
- try:
- bzrlib.plugins.__path__ = []
- os.environ['BZR_PLUGIN_PATH'] = "first\\//\\" + os.pathsep + \
- "second/\\/\\/"
- bzrlib.plugin.set_plugins_path()
- # We expect our nominated paths to have all path-seps removed,
- # and this is testing only that.
- expected_path = ['first', 'second']
- self.assertEqual(expected_path,
- bzrlib.plugins.__path__[:len(expected_path)])
- finally:
- bzrlib.plugins.__path__ = old_path
- if old_env is not None:
- os.environ['BZR_PLUGIN_PATH'] = old_env
- else:
- del os.environ['BZR_PLUGIN_PATH']
-
-
class TestHelpIndex(tests.TestCase):
"""Tests for the PluginsHelpIndex class."""
@@ -597,41 +566,42 @@
self.assertEqual('foo_bar', topic.get_help_topic())
-def clear_plugins(test_case):
- # Save the attributes that we're about to monkey-patch.
- old_plugins_path = bzrlib.plugins.__path__
- old_loaded = plugin._loaded
- old_load_from_path = plugin.load_from_path
- # Change bzrlib.plugin to think no plugins have been loaded yet.
- bzrlib.plugins.__path__ = []
- plugin._loaded = False
- # Monkey-patch load_from_path to stop it from actually loading anything.
- def load_from_path(dirs):
- pass
- plugin.load_from_path = load_from_path
- def restore_plugins():
- bzrlib.plugins.__path__ = old_plugins_path
- plugin._loaded = old_loaded
- plugin.load_from_path = old_load_from_path
- test_case.addCleanup(restore_plugins)
-
-
-class TestPluginPaths(tests.TestCase):
+class TestLoadFromPath(tests.TestCaseInTempDir):
+
+ def setUp(self):
+ super(TestLoadFromPath, self).setUp()
+ # Save the attributes that we're about to monkey-patch.
+ old_plugins_path = bzrlib.plugins.__path__
+ old_loaded = plugin._loaded
+ old_load_from_path = plugin.load_from_path
+
+ def restore():
+ bzrlib.plugins.__path__ = old_plugins_path
+ plugin._loaded = old_loaded
+ plugin.load_from_path = old_load_from_path
+
+ self.addCleanup(restore)
+
+ # Change bzrlib.plugin to think no plugins have been loaded yet.
+ bzrlib.plugins.__path__ = []
+ plugin._loaded = False
+
+ # Monkey-patch load_from_path to stop it from actually loading anything.
+ def load_from_path(dirs):
+ pass
+ plugin.load_from_path = load_from_path
def test_set_plugins_path_with_args(self):
- clear_plugins(self)
plugin.set_plugins_path(['a', 'b'])
self.assertEqual(['a', 'b'], bzrlib.plugins.__path__)
def test_set_plugins_path_defaults(self):
- clear_plugins(self)
plugin.set_plugins_path()
self.assertEqual(plugin.get_standard_plugins_path(),
bzrlib.plugins.__path__)
def test_get_standard_plugins_path(self):
path = plugin.get_standard_plugins_path()
- self.assertEqual(plugin.get_default_plugin_path(), path[0])
for directory in path:
self.assertNotContainsRe(directory, r'\\/$')
try:
@@ -649,13 +619,11 @@
def test_get_standard_plugins_path_env(self):
os.environ['BZR_PLUGIN_PATH'] = 'foo/'
- self.assertEqual('foo', plugin.get_standard_plugins_path()[0])
-
-
-class TestLoadPlugins(tests.TestCaseInTempDir):
+ path = plugin.get_standard_plugins_path()
+ for directory in path:
+ self.assertNotContainsRe(directory, r'\\/$')
def test_load_plugins(self):
- clear_plugins(self)
plugin.load_plugins(['.'])
self.assertEqual(bzrlib.plugins.__path__, ['.'])
# subsequent loads are no-ops
@@ -663,7 +631,107 @@
self.assertEqual(bzrlib.plugins.__path__, ['.'])
def test_load_plugins_default(self):
- clear_plugins(self)
plugin.load_plugins()
path = plugin.get_standard_plugins_path()
self.assertEqual(path, bzrlib.plugins.__path__)
+
+
+class TestEnvPluginPath(tests.TestCaseInTempDir):
+
+ def setUp(self):
+ super(TestEnvPluginPath, self).setUp()
+ old_default = plugin.DEFAULT_PLUGIN_PATH
+
+ def restore():
+ plugin.DEFAULT_PLUGIN_PATH = old_default
+
+ self.addCleanup(restore)
+
+ plugin.DEFAULT_PLUGIN_PATH = None
+
+ self.user = plugin.get_user_plugin_path()
+ self.site = plugin.get_site_plugin_path()
+ self.core = plugin.get_core_plugin_path()
+
+ def _list2paths(self, *args):
+ paths = []
+ for p in args:
+ plugin._append_new_path(paths, p)
+ return paths
+
+ def _set_path(self, *args):
+ path = os.pathsep.join(self._list2paths(*args))
+ osutils.set_or_unset_env('BZR_PLUGIN_PATH', path)
+
+ def check_path(self, expected_dirs, setting_dirs):
+ if setting_dirs:
+ self._set_path(*setting_dirs)
+ actual = plugin.get_standard_plugins_path()
+ self.assertEquals(self._list2paths(*expected_dirs), actual)
+
+ def test_default(self):
+ self.check_path([self.user, self.core, self.site],
+ None)
+
+ def test_adhoc_policy(self):
+ self.check_path([self.user, self.core, self.site],
+ ['+user', '+core', '+site'])
+
+ def test_fallback_policy(self):
+ self.check_path([self.core, self.site, self.user],
+ ['+core', '+site', '+user'])
+
+ def test_override_policy(self):
+ self.check_path([self.user, self.site, self.core],
+ ['+user', '+site', '+core'])
+
+ def test_disable_user(self):
+ self.check_path([self.core, self.site], ['-user'])
+
+ def test_disable_user_twice(self):
+ # Ensures multiple removals don't left cruft
+ self.check_path([self.core, self.site], ['-user', '-user'])
+
+ def test_duplicates_are_removed(self):
+ self.check_path([self.user, self.core, self.site],
+ ['+user', '+user'])
+ # And only the first reference is kept (since the later references will
+ # onnly produce <plugin> already loaded mutters)
+ self.check_path([self.user, self.core, self.site],
+ ['+user', '+user', '+core',
+ '+user', '+site', '+site',
+ '+core'])
+
+ def test_disable_overrides_disable(self):
+ self.check_path([self.core, self.site], ['-user', '+user'])
+
+ def test_disable_core(self):
+ self.check_path([self.site], ['-core'])
+ self.check_path([self.user, self.site], ['+user', '-core'])
+
+ def test_disable_site(self):
+ self.check_path([self.core], ['-site'])
+ self.check_path([self.user, self.core], ['-site', '+user'])
+
+ def test_override_site(self):
+ self.check_path(['mysite', self.user, self.core],
+ ['mysite', '-site', '+user'])
+ self.check_path(['mysite', self.core],
+ ['mysite', '-site'])
+
+ def test_override_core(self):
+ self.check_path(['mycore', self.user, self.site],
+ ['mycore', '-core', '+user', '+site'])
+ self.check_path(['mycore', self.site],
+ ['mycore', '-core'])
+
+ def test_my_plugin_only(self):
+ self.check_path(['myplugin'], ['myplugin', '-user', '-core', '-site'])
+
+ def test_my_plugin_first(self):
+ self.check_path(['myplugin', self.core, self.site, self.user],
+ ['myplugin', '+core', '+site', '+user'])
+
+ def test_bogus_references(self):
+ self.check_path(['+foo', '-bar', self.core, self.site],
+ ['+foo', '-bar'])
=== modified file 'doc/en/user-guide/plugins.txt'
--- a/doc/en/user-guide/plugins.txt 2007-12-14 07:35:49 +0000
+++ b/doc/en/user-guide/plugins.txt 2009-08-20 13:26:36 +0000
@@ -56,23 +56,11 @@
Alternative plugin locations
----------------------------
-If you have the necessary permissions, plugins can also be installed on
-a system-wide basis. Two locations are currently checked for plugins:
-
- 1. the system location - bzrlib/plugins
- 2. the personal location - $BZR_HOME/plugins.
-
-On a Linux installation, these locations are typically
-``/usr/lib/python2.4/site-packages/bzrlib/plugins/`` and
-``$HOME/.bazaar/plugins/``.
-On a Windows installation, the system location might be
-``C:\\Program Files\\Bazaar\\plugins``
-while the personal location might be
-``C:\Documents and Settings\<username>\Application Data\Bazaar\2.0\plugins``.
-
-One can additionally override the personal plugins location
-by setting the environment variable ``BZR_PLUGIN_PATH``
-to a directory that contains plugins.
+If you have the necessary permissions, plugins can also be installed on a
+system-wide basis. One can additionally override the personal plugins
+location by setting the environment variable ``BZR_PLUGIN_PATH`` (see `User
+Reference <../user-reference/bzr_man.html#bzr-plugin-path>`_ for a detailed
+explanation).
Listing the installed plugins
-----------------------------
=== modified file 'doc/en/user-guide/writing_a_plugin.txt'
--- a/doc/en/user-guide/writing_a_plugin.txt 2008-10-15 19:34:48 +0000
+++ b/doc/en/user-guide/writing_a_plugin.txt 2009-08-20 13:26:36 +0000
@@ -32,11 +32,14 @@
Plugin searching rules
----------------------
-Bzr will scan ``bzrlib/plugins`` and ``~/.bazaar/plugins`` for plugins
-by default. You can override this with ``BZR_PLUGIN_PATH``. Plugins
-may be either modules or packages. If your plugin is a single file,
-you can structure it as a module. If it has multiple files, or if you
-want to distribute it as a bzr branch, you should structure it as a
+Bzr will scan ``~/.bazaar/plugins`` and ``bzrlib/plugins`` for plugins
+by default. You can override this with ``BZR_PLUGIN_PATH``
+(see `User Reference <../user-reference/bzr_man.html#bzr-plugin-path>`_
+for details).
+
+Plugins may be either modules or packages. If your plugin is a single
+file, you can structure it as a module. If it has multiple files, or if
+you want to distribute it as a bzr branch, you should structure it as a
package, i.e. a directory with an ``__init__.py`` file.
More information
More information about the bazaar-commits
mailing list