Rev 5101: (vila) Allow plugins to be disabled via BZR_DISABLE_PLUGINS in file:///home/pqm/archives/thelove/bzr/%2Btrunk/

Canonical.com Patch Queue Manager pqm at pqm.ubuntu.com
Fri Mar 19 12:44:49 GMT 2010


At file:///home/pqm/archives/thelove/bzr/%2Btrunk/

------------------------------------------------------------
revno: 5101 [merge]
revision-id: pqm at pqm.ubuntu.com-20100319124448-8xooaqwyiwqhrq49
parent: pqm at pqm.ubuntu.com-20100319104730-5jdnpkz0ghxd96no
parent: v.ladeuil+lp at free.fr-20100319121028-ion6boda1uh4s5wu
committer: Canonical.com Patch Queue Manager <pqm at pqm.ubuntu.com>
branch nick: +trunk
timestamp: Fri 2010-03-19 12:44:48 +0000
message:
  (vila) Allow plugins to be disabled via BZR_DISABLE_PLUGINS
modified:
  NEWS                           NEWS-20050323055033-4e00b5db738777ff
  bzrlib/help_topics/en/configuration.txt configuration.txt-20060314161707-868350809502af01
  bzrlib/plugin.py               plugin.py-20050622060424-829b654519533d69
  bzrlib/tests/__init__.py       selftest.py-20050531073622-8d0e3c8845c97a64
  bzrlib/tests/test_plugins.py   plugins.py-20050622075746-32002b55e5e943e9
=== modified file 'NEWS'
--- a/NEWS	2010-03-19 08:06:29 +0000
+++ b/NEWS	2010-03-19 12:10:28 +0000
@@ -51,6 +51,10 @@
   treats backslash as an escape character on Windows.   (Gordon Tyler,
   #392248)
 
+* Plugins can be disabled by defining ``BZR_DISABLE_PLUGINS`` as
+  a list of plugin names separated by ':' (';' on windows).
+  (Vincent Ladeuil, #411413)
+
 * Tree-shape conflicts can be resolved by providing ``--take-this`` and
   ``--take-other`` to the ``bzr resolve`` command. Just marking the conflict
   as resolved is still accessible via the ``--done`` default action.

=== modified file 'bzrlib/help_topics/en/configuration.txt'
--- a/bzrlib/help_topics/en/configuration.txt	2010-01-03 03:33:10 +0000
+++ b/bzrlib/help_topics/en/configuration.txt	2010-03-19 12:09:05 +0000
@@ -98,24 +98,40 @@
 used literally, they will be substituted by the corresponding,
 platform specific, values.
 
-Examples:
-^^^^^^^^^
-
-The examples below uses ':' as the separator, windows users
+The examples below use ':' 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``
-
+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
+
+BZR_DISABLE_PLUGINS
+~~~~~~~~~~~~~~~~~~~
+
+Under special circumstances, it's better to disable a plugin (or
+several) rather than uninstalling them completely. Such plugins
+can be specified in the ``BZR_DISABLE_PLUGINS`` environment
+variable.
+
+In that case, ``bzr`` will stop loading the specified plugins and
+will raise an import error if they are explicitly imported (by
+another plugin that depends on them for example).
+
+Disabling ``myplugin`` and ``yourplugin`` is achieved by::
+
+  BZR_DISABLE_PLUGINS='myplugin:yourplugin'
 
 
 BZRPATH

=== modified file 'bzrlib/plugin.py'
--- a/bzrlib/plugin.py	2010-03-12 13:41:08 +0000
+++ b/bzrlib/plugin.py	2010-03-17 07:16:32 +0000
@@ -91,6 +91,12 @@
     if path is None:
         path = get_standard_plugins_path()
     _mod_plugins.__path__ = path
+    # Set up a blacklist for disabled plugins if any
+    PluginBlackListImporter.blacklist = {}
+    disabled_plugins = os.environ.get('BZR_DISABLE_PLUGINS', None)
+    if disabled_plugins is not None:
+        for name in disabled_plugins.split(os.pathsep):
+            PluginBlackListImporter.blacklist['bzrlib.plugins.' + name] = True
     return path
 
 
@@ -183,8 +189,8 @@
             try:
                 p = refs[p[1:]]
             except KeyError:
-                # Leave them untouched otherwise, user may have paths starting
-                # with '+'...
+                # Leave them untouched so user can still use paths starting
+                # with '+'
                 pass
         _append_new_path(paths, p)
 
@@ -202,7 +208,7 @@
     files (and whatever other extensions are used in the platform,
     such as *.pyd).
 
-    load_from_dirs() provides the underlying mechanism and is called with
+    load_from_path() provides the underlying mechanism and is called with
     the default directory list to provide the normal behaviour.
 
     :param path: The list of paths to search for plugins.  By default,
@@ -290,6 +296,8 @@
             plugin_names.add(f)
 
     for name in plugin_names:
+        if ('bzrlib.plugins.%s' % name) in PluginBlackListImporter.blacklist:
+            continue
         try:
             exec "import bzrlib.plugins.%s" % name in {}
         except KeyboardInterrupt:
@@ -476,3 +484,19 @@
         return version_string
 
     __version__ = property(_get__version__)
+
+
+class _PluginBlackListImporter(object):
+
+    def __init__(self):
+        self.blacklist = {}
+
+    def find_module(self, fullname, parent_path=None):
+        if fullname in self.blacklist:
+            raise ImportError('%s is disabled' % fullname)
+        return None
+
+PluginBlackListImporter = _PluginBlackListImporter()
+sys.meta_path.append(PluginBlackListImporter)
+
+

=== modified file 'bzrlib/tests/__init__.py'
--- a/bzrlib/tests/__init__.py	2010-03-17 05:36:11 +0000
+++ b/bzrlib/tests/__init__.py	2010-03-19 12:10:28 +0000
@@ -1519,6 +1519,7 @@
             'BZR_PROGRESS_BAR': None,
             'BZR_LOG': None,
             'BZR_PLUGIN_PATH': None,
+            'BZR_DISABLE_PLUGINS': None,
             'BZR_CONCURRENCY': None,
             # Make sure that any text ui tests are consistent regardless of
             # the environment the test case is run in; you may want tests that

=== modified file 'bzrlib/tests/test_plugins.py'
--- a/bzrlib/tests/test_plugins.py	2010-03-15 11:17:44 +0000
+++ b/bzrlib/tests/test_plugins.py	2010-03-17 07:16:32 +0000
@@ -31,6 +31,7 @@
     plugin,
     plugins,
     tests,
+    trace,
     )
 
 
@@ -38,6 +39,25 @@
 
 class TestPluginMixin(object):
 
+    def create_plugin(self, name, source='', dir='.', file_name=None):
+        if file_name is None:
+            file_name = name + '.py'
+        # 'source' must not fail to load
+        path = osutils.pathjoin(dir, file_name)
+        f = open(path, 'w')
+        self.addCleanup(os.unlink, path)
+        try:
+            f.write(source + '\n')
+        finally:
+            f.close()
+
+    def create_plugin_package(self, name, source='', dir='.'):
+        plugin_dir = osutils.pathjoin(dir, name)
+        os.mkdir(plugin_dir)
+        self.addCleanup(osutils.rmtree, plugin_dir)
+        self.create_plugin(name, source, dir=plugin_dir,
+                           file_name='__init__.py')
+
     def _unregister_plugin(self, name):
         """Remove the plugin from sys.modules and the bzrlib namespace."""
         py_name = 'bzrlib.plugins.%s' % name
@@ -47,11 +67,11 @@
             delattr(bzrlib.plugins, name)
 
     def assertPluginUnknown(self, name):
-        self.failIf(getattr(bzrlib.plugins, 'plugin', None) is not None)
+        self.failIf(getattr(bzrlib.plugins, name, None) is not None)
         self.failIf('bzrlib.plugins.%s' % name in sys.modules)
 
     def assertPluginKnown(self, name):
-        self.failUnless(getattr(bzrlib.plugins, 'plugin', None) is not None)
+        self.failUnless(getattr(bzrlib.plugins, name, None) is not None)
         self.failUnless('bzrlib.plugins.%s' % name in sys.modules)
 
 
@@ -723,3 +743,40 @@
         self.check_path(['+foo', '-bar', self.core, self.site],
                         ['+foo', '-bar'])
 
+
+class TestDisablePlugin(tests.TestCaseInTempDir, TestPluginMixin):
+
+    def setUp(self):
+        super(TestDisablePlugin, self).setUp()
+        self.create_plugin_package('test_foo')
+        # Make sure we don't pollute the plugins namespace
+        self.overrideAttr(plugins, '__path__')
+        # Be paranoid in case a test fail
+        self.addCleanup(self._unregister_plugin, 'test_foo')
+
+    def test_cannot_import(self):
+        osutils.set_or_unset_env('BZR_DISABLE_PLUGINS', 'test_foo')
+        plugin.set_plugins_path(['.'])
+        try:
+            import bzrlib.plugins.test_foo
+        except ImportError:
+            pass
+        self.assertPluginUnknown('test_foo')
+
+    def test_regular_load(self):
+        self.overrideAttr(plugin, '_loaded', False)
+        plugin.load_plugins(['.'])
+        self.assertPluginKnown('test_foo')
+
+    def test_not_loaded(self):
+        self.warnings = []
+        def captured_warning(*args, **kwargs):
+            self.warnings.append((args, kwargs))
+        self.overrideAttr(trace, 'warning', captured_warning)
+        self.overrideAttr(plugin, '_loaded', False)
+        osutils.set_or_unset_env('BZR_DISABLE_PLUGINS', 'test_foo')
+        plugin.load_plugins(plugin.set_plugins_path(['.']))
+        self.assertPluginUnknown('test_foo')
+        # Make sure we don't warn about the plugin ImportError since this has
+        # been *requested* by the user.
+        self.assertLength(0, self.warnings)




More information about the bazaar-commits mailing list