Rev 5630: (mbp) Don't show messages about plugins incompatible with bzr (Martin Pool) in file:///home/pqm/archives/thelove/bzr/%2Btrunk/

Canonical.com Patch Queue Manager pqm at pqm.ubuntu.com
Fri Jan 21 23:21:20 UTC 2011


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

------------------------------------------------------------
revno: 5630 [merge]
revision-id: pqm at pqm.ubuntu.com-20110121232118-bhaglx42njlyciqd
parent: pqm at pqm.ubuntu.com-20110121224545-j7ie4ubrplzw0tvv
parent: mbp at canonical.com-20110120230725-12l7ltnko5x3fgnz
committer: Canonical.com Patch Queue Manager <pqm at pqm.ubuntu.com>
branch nick: +trunk
timestamp: Fri 2011-01-21 23:21:18 +0000
message:
  (mbp) Don't show messages about plugins incompatible with bzr (Martin Pool)
modified:
  bzrlib/builtins.py             builtins.py-20050830033751-fc01482b9ca23183
  bzrlib/crash.py                crash.py-20090812083334-d6volool4lktdjcx-1
  bzrlib/plugin.py               plugin.py-20050622060424-829b654519533d69
  bzrlib/tests/test_crash.py     test_crash.py-20090820042958-jglgza3wrn03ha9e-2
  bzrlib/tests/test_plugins.py   plugins.py-20050622075746-32002b55e5e943e9
  doc/en/release-notes/bzr-2.4.txt bzr2.4.txt-20110114053217-k7ym9jfz243fddjm-1
=== modified file 'bzrlib/builtins.py'
--- a/bzrlib/builtins.py	2011-01-20 19:59:11 +0000
+++ b/bzrlib/builtins.py	2011-01-21 23:21:18 +0000
@@ -4591,26 +4591,9 @@
 
     @display_command
     def run(self, verbose=False):
-        import bzrlib.plugin
-        from inspect import getdoc
-        result = []
-        for name, plugin in bzrlib.plugin.plugins().items():
-            version = plugin.__version__
-            if version == 'unknown':
-                version = ''
-            name_ver = '%s %s' % (name, version)
-            d = getdoc(plugin.module)
-            if d:
-                doc = d.split('\n')[0]
-            else:
-                doc = '(no description)'
-            result.append((name_ver, doc, plugin.path()))
-        for name_ver, doc, path in sorted(result):
-            self.outf.write("%s\n" % name_ver)
-            self.outf.write("   %s\n" % doc)
-            if verbose:
-                self.outf.write("   %s\n" % path)
-            self.outf.write("\n")
+        from bzrlib import plugin
+        self.outf.writelines(
+            plugin.describe_plugins(show_paths=verbose))
 
 
 class cmd_testament(Command):

=== modified file 'bzrlib/crash.py'
--- a/bzrlib/crash.py	2011-01-18 00:25:04 +0000
+++ b/bzrlib/crash.py	2011-01-20 23:07:25 +0000
@@ -1,4 +1,4 @@
-# Copyright (C) 2009, 2010 Canonical Ltd
+# Copyright (C) 2009, 2010, 2011 Canonical Ltd
 #
 # This program is free software; you can redistribute it and/or modify
 # it under the terms of the GNU General Public License as published by
@@ -255,11 +255,7 @@
 
 
 def _format_plugin_list():
-    plugin_lines = []
-    for name, a_plugin in sorted(plugin.plugins().items()):
-        plugin_lines.append("  %-20s %s [%s]" %
-            (name, a_plugin.path(), a_plugin.__version__))
-    return '\n'.join(plugin_lines)
+    return ''.join(plugin.describe_plugins(show_paths=True))
 
 
 def _format_module_list():

=== modified file 'bzrlib/plugin.py'
--- a/bzrlib/plugin.py	2010-06-11 08:02:42 +0000
+++ b/bzrlib/plugin.py	2011-01-20 01:02:34 +0000
@@ -1,4 +1,4 @@
-# Copyright (C) 2005-2010 Canonical Ltd
+# Copyright (C) 2005-2011 Canonical Ltd
 #
 # This program is free software; you can redistribute it and/or modify
 # it under the terms of the GNU General Public License as published by
@@ -63,6 +63,11 @@
 _plugins_disabled = False
 
 
+plugin_warnings = {}
+# Map from plugin name, to list of string warnings about eg plugin
+# dependencies.
+
+
 def are_plugins_disabled():
     return _plugins_disabled
 
@@ -77,6 +82,43 @@
     load_plugins([])
 
 
+def describe_plugins(show_paths=False):
+    """Generate text description of plugins.
+
+    Includes both those that have loaded, and those that failed to 
+    load.
+
+    :param show_paths: If true,
+    :returns: Iterator of text lines (including newlines.)
+    """
+    from inspect import getdoc
+    loaded_plugins = plugins()
+    all_names = sorted(list(set(
+        loaded_plugins.keys() + plugin_warnings.keys())))
+    for name in all_names:
+        if name in loaded_plugins:
+            plugin = loaded_plugins[name]
+            version = plugin.__version__
+            if version == 'unknown':
+                version = ''
+            yield '%s %s\n' % (name, version)
+            d = getdoc(plugin.module)
+            if d:
+                doc = d.split('\n')[0]
+            else:
+                doc = '(no description)'
+            yield ("  %s\n" % doc)
+            if show_paths:
+                yield ("   %s\n" % plugin.path())
+            del plugin
+        else:
+            yield "%s (failed to load)\n" % name
+        if name in plugin_warnings:
+            for line in plugin_warnings[name]:
+                yield "  ** " + line + '\n'
+        yield '\n'
+
+
 def _strip_trailing_sep(path):
     return path.rstrip("\\/")
 
@@ -327,6 +369,11 @@
     return None, None, (None, None, None)
 
 
+def record_plugin_warning(plugin_name, warning_message):
+    trace.mutter(warning_message)
+    plugin_warnings.setdefault(plugin_name, []).append(warning_message)
+
+
 def _load_plugin_module(name, dir):
     """Load plugin name from dir.
 
@@ -340,10 +387,12 @@
     except KeyboardInterrupt:
         raise
     except errors.IncompatibleAPI, e:
-        trace.warning("Unable to load plugin %r. It requested API version "
+        warning_message = (
+            "Unable to load plugin %r. It requested API version "
             "%s of module %s but the minimum exported version is %s, and "
             "the maximum is %s" %
             (name, e.wanted, e.api, e.minimum, e.current))
+        record_plugin_warning(name, warning_message)
     except Exception, e:
         trace.warning("%s" % e)
         if re.search('\.|-| ', name):
@@ -354,7 +403,9 @@
                     "file path isn't a valid module name; try renaming "
                     "it to %r." % (name, dir, sanitised_name))
         else:
-            trace.warning('Unable to load plugin %r from %r' % (name, dir))
+            record_plugin_warning(
+                name,
+                'Unable to load plugin %r from %r' % (name, dir))
         trace.log_exception_quietly()
         if 'error' in debug.debug_flags:
             trace.print_exception(sys.exc_info(), sys.stderr)

=== modified file 'bzrlib/tests/test_crash.py'
--- a/bzrlib/tests/test_crash.py	2011-01-12 01:01:53 +0000
+++ b/bzrlib/tests/test_crash.py	2011-01-18 00:41:29 +0000
@@ -26,6 +26,7 @@
     config,
     crash,
     osutils,
+    plugin,
     tests,
     )
 
@@ -42,6 +43,11 @@
         self.overrideEnv('APPORT_CRASH_DIR', crash_dir)
         self.assertEquals(crash_dir, config.crash_dir())
 
+        self.overrideAttr(
+            plugin,
+            'plugin_warnings',
+            {'example': ['Failed to load plugin foo']})
+
         stderr = StringIO()
 
         try:
@@ -71,3 +77,6 @@
         self.assertContainsRe(report, 'test_apport_report')
         # should also be in there
         self.assertContainsRe(report, '(?m)^CommandLine:')
+        self.assertContainsRe(
+            report,
+            'Failed to load plugin foo')

=== modified file 'bzrlib/tests/test_plugins.py'
--- a/bzrlib/tests/test_plugins.py	2011-01-10 22:20:12 +0000
+++ b/bzrlib/tests/test_plugins.py	2011-01-20 23:06:17 +0000
@@ -38,7 +38,7 @@
 
 # TODO: Write a test for plugin decoration of commands.
 
-class TestPluginMixin(object):
+class BaseTestPlugins(tests.TestCaseInTempDir):
 
     def create_plugin(self, name, source=None, dir='.', file_name=None):
         if source is None:
@@ -99,7 +99,7 @@
         self.failUnless('bzrlib.plugins.%s' % name in sys.modules)
 
 
-class TestLoadingPlugins(tests.TestCaseInTempDir, TestPluginMixin):
+class TestLoadingPlugins(BaseTestPlugins):
 
     activeattributes = {}
 
@@ -267,8 +267,13 @@
             stream.close()
 
     def test_plugin_with_bad_api_version_reports(self):
-        # This plugin asks for bzrlib api version 1.0.0, which is not supported
-        # anymore.
+        """Try loading a plugin that requests an unsupported api.
+        
+        Observe that it records the problem but doesn't complain on stderr.
+
+        See https://bugs.launchpad.net/bzr/+bug/704195
+        """
+        self.overrideAttr(plugin, 'plugin_warnings', {})
         name = 'wants100.py'
         f = file(name, 'w')
         try:
@@ -276,9 +281,14 @@
                 "bzrlib.api.require_any_api(bzrlib, [(1, 0, 0)])\n")
         finally:
             f.close()
-
         log = self.load_and_capture(name)
-        self.assertContainsRe(log,
+        self.assertNotContainsRe(log,
+            r"It requested API version")
+        self.assertEquals(
+            ['wants100'],
+            plugin.plugin_warnings.keys())
+        self.assertContainsRe(
+            plugin.plugin_warnings['wants100'][0],
             r"It requested API version")
 
     def test_plugin_with_bad_name_does_not_load(self):
@@ -292,7 +302,7 @@
             "it to 'bad_plugin_name_'\.")
 
 
-class TestPlugins(tests.TestCaseInTempDir, TestPluginMixin):
+class TestPlugins(BaseTestPlugins):
 
     def setup_plugin(self, source=""):
         # This test tests a new plugin appears in bzrlib.plugin.plugins().
@@ -768,7 +778,7 @@
                         ['+foo', '-bar'])
 
 
-class TestDisablePlugin(tests.TestCaseInTempDir, TestPluginMixin):
+class TestDisablePlugin(BaseTestPlugins):
 
     def setUp(self):
         super(TestDisablePlugin, self).setUp()
@@ -809,6 +819,7 @@
         self.assertLength(0, self.warnings)
 
 
+
 class TestLoadPluginAtSyntax(tests.TestCase):
 
     def _get_paths(self, paths):
@@ -832,7 +843,7 @@
                           os.pathsep.join(['batman at cave', '', 'robin at mobile']))
 
 
-class TestLoadPluginAt(tests.TestCaseInTempDir, TestPluginMixin):
+class TestLoadPluginAt(BaseTestPlugins):
 
     def setUp(self):
         super(TestLoadPluginAt, self).setUp()
@@ -847,6 +858,9 @@
         self.create_plugin_package('test_foo', dir='standard/test_foo')
         # All the tests will load the 'test_foo' plugin from various locations
         self.addCleanup(self._unregister_plugin, 'test_foo')
+        # Unfortunately there's global cached state for the specific
+        # registered paths.
+        self.addCleanup(plugin.PluginImporter.reset)
 
     def assertTestFooLoadedFrom(self, path):
         self.assertPluginKnown('test_foo')
@@ -945,3 +959,26 @@
         self.overrideEnv('BZR_PLUGINS_AT', 'test_foo@%s' % plugin_path)
         plugin.load_plugins(['standard'])
         self.assertTestFooLoadedFrom(plugin_path)
+
+
+class TestDescribePlugins(BaseTestPlugins):
+
+    def test_describe_plugins(self):
+        class DummyModule(object):
+            __doc__ = 'Hi there'
+        class DummyPlugin(object):
+            __version__ = '0.1.0'
+            module = DummyModule()
+        def dummy_plugins():
+            return { 'good': DummyPlugin() }
+        self.overrideAttr(plugin, 'plugin_warnings',
+            {'bad': ['Failed to load (just testing)']})
+        self.overrideAttr(plugin, 'plugins', dummy_plugins)
+        self.assertEquals("""\
+bad (failed to load)
+  ** Failed to load (just testing)
+
+good 0.1.0
+  Hi there
+
+""", ''.join(plugin.describe_plugins()))

=== modified file 'doc/en/release-notes/bzr-2.4.txt'
--- a/doc/en/release-notes/bzr-2.4.txt	2011-01-20 19:59:11 +0000
+++ b/doc/en/release-notes/bzr-2.4.txt	2011-01-21 23:21:18 +0000
@@ -57,6 +57,11 @@
 * ``bzr whoami`` will now display an error if both a new identity and ``--email``
   were specified. (Jelmer Vernooij, #680449)
 
+* Plugins incompatible with the current version of bzr no longer produce a
+  warning on every command invocation.  Instead, a message is shown by
+  ``bzr plugins`` and in crash reports.
+  (#704195, Martin Pool)
+
 Documentation
 *************
 




More information about the bazaar-commits mailing list