From 8776354b7aae7087f090a0989181400899f6c0d0 Mon Sep 17 00:00:00 2001 From: dgw Date: Thu, 9 Apr 2020 16:22:51 -0500 Subject: [PATCH] bot, plugins: fix per-channel configuration by plugin name When this feature was first implemented, core plugins and user plugins (from `~/.sopel/modules`) both loaded in a similar way. Their Python module names matched the file names, and nobody thought to test how this feature worked (or might not) for `sopel_modules` packages. Certainly, no one thought to test entry-point plugins (new in 7.0, implemented *after* this was). To make a long story short (the full narrative is summarized in #1839), this feature was never going to work as intended in all cases. Changes to the plugin machinery simply made it *also* not work for core plugins, which made the shortcomings of its implementation much more obvious. Before passing the module contents back to `bot` during registration, the `plugins.handlers.PyModulePlugin` class (and derivatives) now adds a `plugin_name` attribute to each "relevant part" (callable). This is immediately useful for improving the per-channel filtering so it works as expected, and will likely find more use in the future (e.g. as a substitute for the long-deprecated `bot.command_groups` property). In `bot`, in addition to using the new callable attribute instead of Python's module name for per-channel filtering, I also added log output to debug mode for completeness. --- sopel/bot.py | 18 +++++++++++++++--- sopel/plugins/handlers.py | 5 +++++ 2 files changed, 20 insertions(+), 3 deletions(-) diff --git a/sopel/bot.py b/sopel/bot.py index 988ead4045..e91300fae6 100644 --- a/sopel/bot.py +++ b/sopel/bot.py @@ -574,16 +574,28 @@ def call(self, func, sopel, trigger): # if "*" is used, we are disabling all plugins on provided channel if '*' in disabled_plugins: + LOGGER.debug( + "All plugins disabled in %s; skipping execution of %s.%s", + trigger.sender, func.plugin_name, func.__name__ + ) return - if func.__module__ in disabled_plugins: + if func.plugin_name in disabled_plugins: + LOGGER.debug( + "Plugin %s is disabled in %s; skipping execution of %s", + func.plugin_name, trigger.sender, func.__name__ + ) return # disable chosen methods from plugins if 'disable_commands' in channel_config: disabled_commands = literal_eval(channel_config.disable_commands) - if func.__module__ in disabled_commands: - if func.__name__ in disabled_commands[func.__module__]: + if func.plugin_name in disabled_commands: + if func.__name__ in disabled_commands[func.plugin_name]: + LOGGER.debug( + "Skipping execution of %s.%s in %s: disabled_commands matched", + func.plugin_name, func.__name__, trigger.sender + ) return try: diff --git a/sopel/plugins/handlers.py b/sopel/plugins/handlers.py index 4de4b8021c..755bfb7107 100644 --- a/sopel/plugins/handlers.py +++ b/sopel/plugins/handlers.py @@ -36,6 +36,7 @@ import inspect import imp import importlib +import itertools import os from sopel import loader @@ -259,6 +260,10 @@ def has_setup(self): def register(self, bot): relevant_parts = loader.clean_module(self._module, bot.config) + for part in itertools.chain(*relevant_parts): + # annotate all callables in relevant_parts with `plugin_name` + # attribute to make per-channel config work; see #1839 + setattr(part, 'plugin_name', self.name) bot.add_plugin(self, *relevant_parts) def unregister(self, bot):