Merge lp:~mbp/bzr/704195-plugin-warnings into lp:bzr

Proposed by Martin Pool
Status: Merged
Approved by: Martin Pool
Approved revision: no longer in the source branch.
Merged at revision: 5630
Proposed branch: lp:~mbp/bzr/704195-plugin-warnings
Merge into: lp:bzr
Diff against target: 332 lines (+119/-38)
6 files modified
bzrlib/builtins.py (+3/-20)
bzrlib/crash.py (+2/-6)
bzrlib/plugin.py (+54/-3)
bzrlib/tests/test_crash.py (+9/-0)
bzrlib/tests/test_plugins.py (+46/-9)
doc/en/release-notes/bzr-2.4.txt (+5/-0)
To merge this branch: bzr merge lp:~mbp/bzr/704195-plugin-warnings
Reviewer Review Type Date Requested Status
Jelmer Vernooij (community) Approve
Bazaar Developers Pending
Review via email: mp+46771@code.launchpad.net

This proposal supersedes a proposal from 2011-01-19.

Commit message

Don't show messages about plugins incompatible with bzr

Description of the change

This stops bzr spamming you about out-of-date plugins. Telling people on every single invocation is not helpful. Now we just tell you on in `bzr plugins`.

As a follow-on in bug 704238 we can try to load them anyhow.

To post a comment you must log in.
Revision history for this message
Martin Pool (mbp) wrote :

I previously targeted 2.3 but this is more useful in 2.4. The NEWS probably needs to be fixed before landing.

Revision history for this message
Jelmer Vernooij (jelmer) :
review: Approve
Revision history for this message
Martin Pool (mbp) wrote :

I'll update a couple of things before landing based on conversations
with Jelmer:

 * the code that formats the list could fold both the loaded and
failed-to-load plugins together, and be a bit simpler
 * should move into the 2.4 NEWS

Note that with this change, missing-dependency plugins will continue
to not load; we just represent that differently.

--
Martin

Revision history for this message
Martin Pool (mbp) wrote :

sent to pqm by email

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'bzrlib/builtins.py'
--- bzrlib/builtins.py 2011-01-19 18:40:15 +0000
+++ bzrlib/builtins.py 2011-01-20 23:09:55 +0000
@@ -4587,26 +4587,9 @@
45874587
4588 @display_command4588 @display_command
4589 def run(self, verbose=False):4589 def run(self, verbose=False):
4590 import bzrlib.plugin4590 from bzrlib import plugin
4591 from inspect import getdoc4591 self.outf.writelines(
4592 result = []4592 plugin.describe_plugins(show_paths=verbose))
4593 for name, plugin in bzrlib.plugin.plugins().items():
4594 version = plugin.__version__
4595 if version == 'unknown':
4596 version = ''
4597 name_ver = '%s %s' % (name, version)
4598 d = getdoc(plugin.module)
4599 if d:
4600 doc = d.split('\n')[0]
4601 else:
4602 doc = '(no description)'
4603 result.append((name_ver, doc, plugin.path()))
4604 for name_ver, doc, path in sorted(result):
4605 self.outf.write("%s\n" % name_ver)
4606 self.outf.write(" %s\n" % doc)
4607 if verbose:
4608 self.outf.write(" %s\n" % path)
4609 self.outf.write("\n")
46104593
46114594
4612class cmd_testament(Command):4595class cmd_testament(Command):
46134596
=== modified file 'bzrlib/crash.py'
--- bzrlib/crash.py 2011-01-18 00:25:04 +0000
+++ bzrlib/crash.py 2011-01-20 23:09:55 +0000
@@ -1,4 +1,4 @@
1# Copyright (C) 2009, 2010 Canonical Ltd1# Copyright (C) 2009, 2010, 2011 Canonical Ltd
2#2#
3# This program is free software; you can redistribute it and/or modify3# 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 by4# it under the terms of the GNU General Public License as published by
@@ -255,11 +255,7 @@
255255
256256
257def _format_plugin_list():257def _format_plugin_list():
258 plugin_lines = []258 return ''.join(plugin.describe_plugins(show_paths=True))
259 for name, a_plugin in sorted(plugin.plugins().items()):
260 plugin_lines.append(" %-20s %s [%s]" %
261 (name, a_plugin.path(), a_plugin.__version__))
262 return '\n'.join(plugin_lines)
263259
264260
265def _format_module_list():261def _format_module_list():
266262
=== modified file 'bzrlib/plugin.py'
--- bzrlib/plugin.py 2010-06-11 08:02:42 +0000
+++ bzrlib/plugin.py 2011-01-20 23:09:55 +0000
@@ -1,4 +1,4 @@
1# Copyright (C) 2005-2010 Canonical Ltd1# Copyright (C) 2005-2011 Canonical Ltd
2#2#
3# This program is free software; you can redistribute it and/or modify3# 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 by4# it under the terms of the GNU General Public License as published by
@@ -63,6 +63,11 @@
63_plugins_disabled = False63_plugins_disabled = False
6464
6565
66plugin_warnings = {}
67# Map from plugin name, to list of string warnings about eg plugin
68# dependencies.
69
70
66def are_plugins_disabled():71def are_plugins_disabled():
67 return _plugins_disabled72 return _plugins_disabled
6873
@@ -77,6 +82,43 @@
77 load_plugins([])82 load_plugins([])
7883
7984
85def describe_plugins(show_paths=False):
86 """Generate text description of plugins.
87
88 Includes both those that have loaded, and those that failed to
89 load.
90
91 :param show_paths: If true,
92 :returns: Iterator of text lines (including newlines.)
93 """
94 from inspect import getdoc
95 loaded_plugins = plugins()
96 all_names = sorted(list(set(
97 loaded_plugins.keys() + plugin_warnings.keys())))
98 for name in all_names:
99 if name in loaded_plugins:
100 plugin = loaded_plugins[name]
101 version = plugin.__version__
102 if version == 'unknown':
103 version = ''
104 yield '%s %s\n' % (name, version)
105 d = getdoc(plugin.module)
106 if d:
107 doc = d.split('\n')[0]
108 else:
109 doc = '(no description)'
110 yield (" %s\n" % doc)
111 if show_paths:
112 yield (" %s\n" % plugin.path())
113 del plugin
114 else:
115 yield "%s (failed to load)\n" % name
116 if name in plugin_warnings:
117 for line in plugin_warnings[name]:
118 yield " ** " + line + '\n'
119 yield '\n'
120
121
80def _strip_trailing_sep(path):122def _strip_trailing_sep(path):
81 return path.rstrip("\\/")123 return path.rstrip("\\/")
82124
@@ -327,6 +369,11 @@
327 return None, None, (None, None, None)369 return None, None, (None, None, None)
328370
329371
372def record_plugin_warning(plugin_name, warning_message):
373 trace.mutter(warning_message)
374 plugin_warnings.setdefault(plugin_name, []).append(warning_message)
375
376
330def _load_plugin_module(name, dir):377def _load_plugin_module(name, dir):
331 """Load plugin name from dir.378 """Load plugin name from dir.
332379
@@ -340,10 +387,12 @@
340 except KeyboardInterrupt:387 except KeyboardInterrupt:
341 raise388 raise
342 except errors.IncompatibleAPI, e:389 except errors.IncompatibleAPI, e:
343 trace.warning("Unable to load plugin %r. It requested API version "390 warning_message = (
391 "Unable to load plugin %r. It requested API version "
344 "%s of module %s but the minimum exported version is %s, and "392 "%s of module %s but the minimum exported version is %s, and "
345 "the maximum is %s" %393 "the maximum is %s" %
346 (name, e.wanted, e.api, e.minimum, e.current))394 (name, e.wanted, e.api, e.minimum, e.current))
395 record_plugin_warning(name, warning_message)
347 except Exception, e:396 except Exception, e:
348 trace.warning("%s" % e)397 trace.warning("%s" % e)
349 if re.search('\.|-| ', name):398 if re.search('\.|-| ', name):
@@ -354,7 +403,9 @@
354 "file path isn't a valid module name; try renaming "403 "file path isn't a valid module name; try renaming "
355 "it to %r." % (name, dir, sanitised_name))404 "it to %r." % (name, dir, sanitised_name))
356 else:405 else:
357 trace.warning('Unable to load plugin %r from %r' % (name, dir))406 record_plugin_warning(
407 name,
408 'Unable to load plugin %r from %r' % (name, dir))
358 trace.log_exception_quietly()409 trace.log_exception_quietly()
359 if 'error' in debug.debug_flags:410 if 'error' in debug.debug_flags:
360 trace.print_exception(sys.exc_info(), sys.stderr)411 trace.print_exception(sys.exc_info(), sys.stderr)
361412
=== modified file 'bzrlib/tests/test_crash.py'
--- bzrlib/tests/test_crash.py 2011-01-12 01:01:53 +0000
+++ bzrlib/tests/test_crash.py 2011-01-20 23:09:55 +0000
@@ -26,6 +26,7 @@
26 config,26 config,
27 crash,27 crash,
28 osutils,28 osutils,
29 plugin,
29 tests,30 tests,
30 )31 )
3132
@@ -42,6 +43,11 @@
42 self.overrideEnv('APPORT_CRASH_DIR', crash_dir)43 self.overrideEnv('APPORT_CRASH_DIR', crash_dir)
43 self.assertEquals(crash_dir, config.crash_dir())44 self.assertEquals(crash_dir, config.crash_dir())
4445
46 self.overrideAttr(
47 plugin,
48 'plugin_warnings',
49 {'example': ['Failed to load plugin foo']})
50
45 stderr = StringIO()51 stderr = StringIO()
4652
47 try:53 try:
@@ -71,3 +77,6 @@
71 self.assertContainsRe(report, 'test_apport_report')77 self.assertContainsRe(report, 'test_apport_report')
72 # should also be in there78 # should also be in there
73 self.assertContainsRe(report, '(?m)^CommandLine:')79 self.assertContainsRe(report, '(?m)^CommandLine:')
80 self.assertContainsRe(
81 report,
82 'Failed to load plugin foo')
7483
=== modified file 'bzrlib/tests/test_plugins.py'
--- bzrlib/tests/test_plugins.py 2011-01-10 22:20:12 +0000
+++ bzrlib/tests/test_plugins.py 2011-01-20 23:09:55 +0000
@@ -38,7 +38,7 @@
3838
39# TODO: Write a test for plugin decoration of commands.39# TODO: Write a test for plugin decoration of commands.
4040
41class TestPluginMixin(object):41class BaseTestPlugins(tests.TestCaseInTempDir):
4242
43 def create_plugin(self, name, source=None, dir='.', file_name=None):43 def create_plugin(self, name, source=None, dir='.', file_name=None):
44 if source is None:44 if source is None:
@@ -99,7 +99,7 @@
99 self.failUnless('bzrlib.plugins.%s' % name in sys.modules)99 self.failUnless('bzrlib.plugins.%s' % name in sys.modules)
100100
101101
102class TestLoadingPlugins(tests.TestCaseInTempDir, TestPluginMixin):102class TestLoadingPlugins(BaseTestPlugins):
103103
104 activeattributes = {}104 activeattributes = {}
105105
@@ -267,8 +267,13 @@
267 stream.close()267 stream.close()
268268
269 def test_plugin_with_bad_api_version_reports(self):269 def test_plugin_with_bad_api_version_reports(self):
270 # This plugin asks for bzrlib api version 1.0.0, which is not supported270 """Try loading a plugin that requests an unsupported api.
271 # anymore.271
272 Observe that it records the problem but doesn't complain on stderr.
273
274 See https://bugs.launchpad.net/bzr/+bug/704195
275 """
276 self.overrideAttr(plugin, 'plugin_warnings', {})
272 name = 'wants100.py'277 name = 'wants100.py'
273 f = file(name, 'w')278 f = file(name, 'w')
274 try:279 try:
@@ -276,9 +281,14 @@
276 "bzrlib.api.require_any_api(bzrlib, [(1, 0, 0)])\n")281 "bzrlib.api.require_any_api(bzrlib, [(1, 0, 0)])\n")
277 finally:282 finally:
278 f.close()283 f.close()
279
280 log = self.load_and_capture(name)284 log = self.load_and_capture(name)
281 self.assertContainsRe(log,285 self.assertNotContainsRe(log,
286 r"It requested API version")
287 self.assertEquals(
288 ['wants100'],
289 plugin.plugin_warnings.keys())
290 self.assertContainsRe(
291 plugin.plugin_warnings['wants100'][0],
282 r"It requested API version")292 r"It requested API version")
283293
284 def test_plugin_with_bad_name_does_not_load(self):294 def test_plugin_with_bad_name_does_not_load(self):
@@ -292,7 +302,7 @@
292 "it to 'bad_plugin_name_'\.")302 "it to 'bad_plugin_name_'\.")
293303
294304
295class TestPlugins(tests.TestCaseInTempDir, TestPluginMixin):305class TestPlugins(BaseTestPlugins):
296306
297 def setup_plugin(self, source=""):307 def setup_plugin(self, source=""):
298 # This test tests a new plugin appears in bzrlib.plugin.plugins().308 # This test tests a new plugin appears in bzrlib.plugin.plugins().
@@ -768,7 +778,7 @@
768 ['+foo', '-bar'])778 ['+foo', '-bar'])
769779
770780
771class TestDisablePlugin(tests.TestCaseInTempDir, TestPluginMixin):781class TestDisablePlugin(BaseTestPlugins):
772782
773 def setUp(self):783 def setUp(self):
774 super(TestDisablePlugin, self).setUp()784 super(TestDisablePlugin, self).setUp()
@@ -809,6 +819,7 @@
809 self.assertLength(0, self.warnings)819 self.assertLength(0, self.warnings)
810820
811821
822
812class TestLoadPluginAtSyntax(tests.TestCase):823class TestLoadPluginAtSyntax(tests.TestCase):
813824
814 def _get_paths(self, paths):825 def _get_paths(self, paths):
@@ -832,7 +843,7 @@
832 os.pathsep.join(['batman@cave', '', 'robin@mobile']))843 os.pathsep.join(['batman@cave', '', 'robin@mobile']))
833844
834845
835class TestLoadPluginAt(tests.TestCaseInTempDir, TestPluginMixin):846class TestLoadPluginAt(BaseTestPlugins):
836847
837 def setUp(self):848 def setUp(self):
838 super(TestLoadPluginAt, self).setUp()849 super(TestLoadPluginAt, self).setUp()
@@ -847,6 +858,9 @@
847 self.create_plugin_package('test_foo', dir='standard/test_foo')858 self.create_plugin_package('test_foo', dir='standard/test_foo')
848 # All the tests will load the 'test_foo' plugin from various locations859 # All the tests will load the 'test_foo' plugin from various locations
849 self.addCleanup(self._unregister_plugin, 'test_foo')860 self.addCleanup(self._unregister_plugin, 'test_foo')
861 # Unfortunately there's global cached state for the specific
862 # registered paths.
863 self.addCleanup(plugin.PluginImporter.reset)
850864
851 def assertTestFooLoadedFrom(self, path):865 def assertTestFooLoadedFrom(self, path):
852 self.assertPluginKnown('test_foo')866 self.assertPluginKnown('test_foo')
@@ -945,3 +959,26 @@
945 self.overrideEnv('BZR_PLUGINS_AT', 'test_foo@%s' % plugin_path)959 self.overrideEnv('BZR_PLUGINS_AT', 'test_foo@%s' % plugin_path)
946 plugin.load_plugins(['standard'])960 plugin.load_plugins(['standard'])
947 self.assertTestFooLoadedFrom(plugin_path)961 self.assertTestFooLoadedFrom(plugin_path)
962
963
964class TestDescribePlugins(BaseTestPlugins):
965
966 def test_describe_plugins(self):
967 class DummyModule(object):
968 __doc__ = 'Hi there'
969 class DummyPlugin(object):
970 __version__ = '0.1.0'
971 module = DummyModule()
972 def dummy_plugins():
973 return { 'good': DummyPlugin() }
974 self.overrideAttr(plugin, 'plugin_warnings',
975 {'bad': ['Failed to load (just testing)']})
976 self.overrideAttr(plugin, 'plugins', dummy_plugins)
977 self.assertEquals("""\
978bad (failed to load)
979 ** Failed to load (just testing)
980
981good 0.1.0
982 Hi there
983
984""", ''.join(plugin.describe_plugins()))
948985
=== modified file 'doc/en/release-notes/bzr-2.4.txt'
--- doc/en/release-notes/bzr-2.4.txt 2011-01-19 18:18:48 +0000
+++ doc/en/release-notes/bzr-2.4.txt 2011-01-20 23:09:55 +0000
@@ -54,6 +54,11 @@
54* ``bzr whoami`` will now display an error if both a new identity and ``--email``54* ``bzr whoami`` will now display an error if both a new identity and ``--email``
55 were specified. (Jelmer Vernooij, #680449)55 were specified. (Jelmer Vernooij, #680449)
5656
57* Plugins incompatible with the current version of bzr no longer produce a
58 warning on every command invocation. Instead, a message is shown by
59 ``bzr plugins`` and in crash reports.
60 (#704195, Martin Pool)
61
57Documentation62Documentation
58*************63*************
5964