From 327b62b6103771bd19465f10a6d4f0412c2b9f1c Mon Sep 17 00:00:00 2001 From: Bruno Cauet Date: Tue, 10 Feb 2015 16:55:06 +0100 Subject: [PATCH] Improve logging management for plugins: fixes Delete the remaining usages of BeetsPlugin.listen(). Since BeetsPlugin.listeners are wrapped by a loglevel-setting function, we cannot easily check their unicity anymore. BeetsPlugin._raw_listeners set holds the raw listeners. Legacy plugins that did not handle enough arguments in their listenings functions may break: dedicated code is now deleted for it would not work with the decorated listeners. Tests got fixed. Some modifications were done empirically: if it passes then it's okay. --- beets/plugins.py | 19 ++++++++++--------- beetsplug/chroma.py | 2 +- beetsplug/fromfilename.py | 5 +++-- beetsplug/permissions.py | 5 +++-- test/test_plugins.py | 17 +++++++---------- 5 files changed, 24 insertions(+), 24 deletions(-) diff --git a/beets/plugins.py b/beets/plugins.py index b7d4c9445..58d9bc0ee 100755 --- a/beets/plugins.py +++ b/beets/plugins.py @@ -18,7 +18,6 @@ from __future__ import (division, absolute_import, print_function, unicode_literals) import traceback -import inspect import re from collections import defaultdict from functools import wraps @@ -180,17 +179,21 @@ class BeetsPlugin(object): mediafile.MediaFile.add_field(name, descriptor) library.Item._media_fields.add(name) + _raw_listeners = None listeners = None def register_listener(self, event, func): """Add a function as a listener for the specified event. """ - func = self._set_log_level(logging.WARNING, func) + wrapped_func = self._set_log_level(logging.WARNING, func) - if self.listeners is None: - self.listeners = defaultdict(list) - if func not in self.listeners[event]: - self.listeners[event].append(func) + cls = self.__class__ + if cls.listeners is None or cls._raw_listeners is None: + cls._raw_listeners = defaultdict(list) + cls.listeners = defaultdict(list) + if func not in cls._raw_listeners[event]: + cls._raw_listeners[event].append(func) + cls.listeners[event].append(wrapped_func) template_funcs = None template_fields = None @@ -440,9 +443,7 @@ def send(event, **arguments): results = [] for handler in event_handlers()[event]: # Don't break legacy plugins if we want to pass more arguments - argspec = inspect.getargspec(handler).args - args = dict((k, v) for k, v in arguments.items() if k in argspec) - result = handler(**args) + result = handler(**arguments) if result is not None: results.append(result) return results diff --git a/beetsplug/chroma.py b/beetsplug/chroma.py index 928f90479..8a83c0002 100644 --- a/beetsplug/chroma.py +++ b/beetsplug/chroma.py @@ -136,6 +136,7 @@ class AcoustidPlugin(plugins.BeetsPlugin): if self.config['auto']: self.register_listener('import_task_start', self.fingerprint_task) + self.register_listener('import_task_apply', apply_acoustid_metadata) def fingerprint_task(self, task, session): return fingerprint_task(self._log, task, session) @@ -211,7 +212,6 @@ def fingerprint_task(log, task, session): acoustid_match(log, item.path) -@AcoustidPlugin.listen('import_task_apply') def apply_acoustid_metadata(task, session): """Apply Acoustid metadata (fingerprint and ID) to the task's items. """ diff --git a/beetsplug/fromfilename.py b/beetsplug/fromfilename.py index 169c02ff6..dc040a0a2 100644 --- a/beetsplug/fromfilename.py +++ b/beetsplug/fromfilename.py @@ -140,10 +140,11 @@ def apply_matches(d): # Plugin structure and hook into import process. class FromFilenamePlugin(plugins.BeetsPlugin): - pass + def __init__(self): + super(FromFilenamePlugin, self).__init__() + self.register_listener('import_task_start', filename_task) -@FromFilenamePlugin.listen('import_task_start') def filename_task(task, session): """Examine each item in the task to see if we can extract a title from the filename. Try to match all filenames to a number of diff --git a/beetsplug/permissions.py b/beetsplug/permissions.py index 256f09e52..5068c2a0a 100644 --- a/beetsplug/permissions.py +++ b/beetsplug/permissions.py @@ -37,9 +37,10 @@ class Permissions(BeetsPlugin): u'file': 644 }) + self.register_listener('item_imported', permissions) + self.register_listener('album_imported', permissions) + -@Permissions.listen('item_imported') -@Permissions.listen('album_imported') def permissions(lib, item=None, album=None): """Running the permission fixer. """ diff --git a/test/test_plugins.py b/test/test_plugins.py index d46c5bd5d..c9c5be502 100644 --- a/test/test_plugins.py +++ b/test/test_plugins.py @@ -35,6 +35,7 @@ class TestHelper(helper.TestHelper): def setup_plugin_loader(self): # FIXME the mocking code is horrific, but this is the lowest and # earliest level of the plugin mechanism we can hook into. + self.load_plugins() self._plugin_loader_patch = patch('beets.plugins.load_plugins') self._plugin_classes = set() load_plugins = self._plugin_loader_patch.start() @@ -95,7 +96,7 @@ class ItemWriteTest(unittest.TestCase, TestHelper): class EventListenerPlugin(plugins.BeetsPlugin): pass - self.event_listener_plugin = EventListenerPlugin + self.event_listener_plugin = EventListenerPlugin() self.register_plugin(EventListenerPlugin) def tearDown(self): @@ -298,19 +299,15 @@ class ListenersTest(unittest.TestCase, TestHelper): pass d = DummyPlugin() - self.assertEqual(DummyPlugin.listeners['cli_exit'], [d.dummy]) + self.assertEqual(DummyPlugin._raw_listeners['cli_exit'], [d.dummy]) d2 = DummyPlugin() - DummyPlugin.register_listener('cli_exit', d.dummy) - self.assertEqual(DummyPlugin.listeners['cli_exit'], + self.assertEqual(DummyPlugin._raw_listeners['cli_exit'], [d.dummy, d2.dummy]) - @DummyPlugin.listen('cli_exit') - def dummy(lib): - pass - - self.assertEqual(DummyPlugin.listeners['cli_exit'], - [d.dummy, d2.dummy, dummy]) + d.register_listener('cli_exit', d2.dummy) + self.assertEqual(DummyPlugin._raw_listeners['cli_exit'], + [d.dummy, d2.dummy]) def suite():