mirror of
https://github.com/beetbox/beets.git
synced 2025-12-06 08:39:17 +01:00
Use only plugins/disabled_plugins config in plugin loading
This commit is contained in:
parent
e9feb41709
commit
54b31d01e9
5 changed files with 58 additions and 41 deletions
|
|
@ -31,6 +31,7 @@ from typing_extensions import ParamSpec
|
|||
|
||||
import beets
|
||||
from beets import logging
|
||||
from beets.util import unique_list
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from collections.abc import Callable, Iterable, Sequence
|
||||
|
|
@ -275,9 +276,7 @@ class BeetsPlugin(metaclass=abc.ABCMeta):
|
|||
return helper
|
||||
|
||||
|
||||
def get_plugin_names(
|
||||
include: set[str] | None = None, exclude: set[str] | None = None
|
||||
) -> set[str]:
|
||||
def get_plugin_names() -> list[str]:
|
||||
"""Discover and return the set of plugin names to be loaded.
|
||||
|
||||
Configures the plugin search paths and resolves the final set of plugins
|
||||
|
|
@ -298,14 +297,18 @@ def get_plugin_names(
|
|||
# For backwards compatibility, also support plugin paths that
|
||||
# *contain* a `beetsplug` package.
|
||||
sys.path += paths
|
||||
plugins = include or set(beets.config["plugins"].as_str_seq())
|
||||
plugins = unique_list(beets.config["plugins"].as_str_seq())
|
||||
# TODO: Remove in v3.0.0
|
||||
if "musicbrainz" in beets.config and beets.config["musicbrainz"].get().get(
|
||||
"enabled"
|
||||
if (
|
||||
"musicbrainz" not in plugins
|
||||
and "musicbrainz" in beets.config
|
||||
and beets.config["musicbrainz"].get().get("enabled")
|
||||
):
|
||||
plugins.add("musicbrainz")
|
||||
plugins.append("musicbrainz")
|
||||
|
||||
return plugins - (exclude or set())
|
||||
beets.config.add({"disabled_plugins": []})
|
||||
disabled_plugins = set(beets.config["disabled_plugins"].as_str_seq())
|
||||
return [p for p in plugins if p not in disabled_plugins]
|
||||
|
||||
|
||||
def _get_plugin(name: str) -> BeetsPlugin | None:
|
||||
|
|
@ -342,7 +345,7 @@ def _get_plugin(name: str) -> BeetsPlugin | None:
|
|||
_instances: list[BeetsPlugin] = []
|
||||
|
||||
|
||||
def load_plugins(*args, **kwargs) -> None:
|
||||
def load_plugins() -> None:
|
||||
"""Initialize the plugin system by loading all configured plugins.
|
||||
|
||||
Performs one-time plugin discovery and instantiation, storing loaded plugin
|
||||
|
|
@ -350,7 +353,7 @@ def load_plugins(*args, **kwargs) -> None:
|
|||
to notify other components.
|
||||
"""
|
||||
if not _instances:
|
||||
names = get_plugin_names(*args, **kwargs)
|
||||
names = get_plugin_names()
|
||||
log.info("Loading plugins: {}", ", ".join(sorted(names)))
|
||||
_instances.extend(filter(None, map(_get_plugin, names)))
|
||||
|
||||
|
|
|
|||
|
|
@ -1579,13 +1579,7 @@ def _setup(
|
|||
"""
|
||||
config = _configure(options)
|
||||
|
||||
def _parse_list(option: str | None) -> set[str]:
|
||||
return set((option or "").split(",")) - {""}
|
||||
|
||||
plugins.load_plugins(
|
||||
include=_parse_list(options.plugins),
|
||||
exclude=_parse_list(options.exclude),
|
||||
)
|
||||
plugins.load_plugins()
|
||||
|
||||
# Get the default subcommands.
|
||||
from beets.ui.commands import default_commands
|
||||
|
|
@ -1704,16 +1698,31 @@ def _raw_main(args: list[str], lib=None) -> None:
|
|||
parser.add_option(
|
||||
"-c", "--config", dest="config", help="path to configuration file"
|
||||
)
|
||||
|
||||
def parse_csl_callback(
|
||||
option: optparse.Option, _, value: str, parser: SubcommandsOptionParser
|
||||
):
|
||||
"""Parse a comma-separated list of values."""
|
||||
setattr(
|
||||
parser.values,
|
||||
option.dest, # type: ignore[arg-type]
|
||||
list(filter(None, value.split(","))),
|
||||
)
|
||||
|
||||
parser.add_option(
|
||||
"-p",
|
||||
"--plugins",
|
||||
dest="plugins",
|
||||
action="callback",
|
||||
callback=parse_csl_callback,
|
||||
help="a comma-separated list of plugins to load",
|
||||
)
|
||||
parser.add_option(
|
||||
"-P",
|
||||
"--disable-plugins",
|
||||
dest="exclude",
|
||||
dest="disabled_plugins",
|
||||
action="callback",
|
||||
callback=parse_csl_callback,
|
||||
help="a comma-separated list of plugins to disable",
|
||||
)
|
||||
parser.add_option(
|
||||
|
|
|
|||
|
|
@ -25,7 +25,7 @@ class LoadExtPlugin(BeetsPlugin):
|
|||
super().__init__()
|
||||
|
||||
if not Database.supports_extensions:
|
||||
self._log.warn(
|
||||
self._log.warning(
|
||||
"loadext is enabled but the current SQLite "
|
||||
"installation does not support extensions"
|
||||
)
|
||||
|
|
|
|||
|
|
@ -97,6 +97,9 @@ For plugin developers:
|
|||
type checking for downstream users of the beets API.
|
||||
* ``plugins.find_plugins`` function does not anymore load plugins. You need to
|
||||
explicitly call ``plugins.load_plugins()`` to load them.
|
||||
* ``plugins.load_plugins`` function does not anymore accept the list of plugins
|
||||
to load. Instead, it loads all plugins that are configured by
|
||||
:ref:`plugins-config` configuration.
|
||||
|
||||
Other changes:
|
||||
|
||||
|
|
|
|||
|
|
@ -91,9 +91,6 @@ class PluginImportTestCase(ImportHelper, PluginTestCase):
|
|||
|
||||
|
||||
class EventsTest(PluginImportTestCase):
|
||||
def setUp(self):
|
||||
super().setUp()
|
||||
|
||||
def test_import_task_created(self):
|
||||
self.importer = self.setup_importer(pretend=True)
|
||||
|
||||
|
|
@ -472,10 +469,22 @@ def get_available_plugins():
|
|||
]
|
||||
|
||||
|
||||
class TestImportAllPlugins(PluginMixin):
|
||||
def unimport_plugins(self):
|
||||
class TestImportPlugin(PluginMixin):
|
||||
@pytest.fixture(params=get_available_plugins())
|
||||
def plugin_name(self, request):
|
||||
"""Fixture to provide the name of each available plugin."""
|
||||
name = request.param
|
||||
|
||||
# skip gstreamer plugins on windows
|
||||
gstreamer_plugins = {"bpd", "replaygain"}
|
||||
if sys.platform == "win32" and name in gstreamer_plugins:
|
||||
pytest.skip(f"GStreamer is not available on Windows: {name}")
|
||||
|
||||
return name
|
||||
|
||||
def unload_plugins(self):
|
||||
"""Unimport plugins before each test to avoid conflicts."""
|
||||
self.unload_plugins()
|
||||
super().unload_plugins()
|
||||
for mod in list(sys.modules):
|
||||
if mod.startswith("beetsplug."):
|
||||
del sys.modules[mod]
|
||||
|
|
@ -483,28 +492,21 @@ class TestImportAllPlugins(PluginMixin):
|
|||
@pytest.fixture(autouse=True)
|
||||
def cleanup(self):
|
||||
"""Ensure plugins are unimported before and after each test."""
|
||||
self.unimport_plugins()
|
||||
self.unload_plugins()
|
||||
yield
|
||||
self.unimport_plugins()
|
||||
self.unload_plugins()
|
||||
|
||||
@pytest.mark.skipif(
|
||||
os.environ.get("GITHUB_ACTIONS") != "true",
|
||||
reason="Requires all dependencies to be installed, "
|
||||
+ "which we can't guarantee in the local environment.",
|
||||
reason=(
|
||||
"Requires all dependencies to be installed, which we can't"
|
||||
" guarantee in the local environment."
|
||||
),
|
||||
)
|
||||
@pytest.mark.parametrize("plugin_name", get_available_plugins())
|
||||
def test_import_plugin(self, caplog, plugin_name): #
|
||||
"""Test that a plugin is importable without an error using the
|
||||
load_plugins function."""
|
||||
|
||||
# skip gstreamer plugins on windows
|
||||
gstreamer_plugins = ["bpd", "replaygain"]
|
||||
if sys.platform == "win32" and plugin_name in gstreamer_plugins:
|
||||
pytest.xfail("GStreamer is not available on Windows: {plugin_name}")
|
||||
|
||||
def test_import_plugin(self, caplog, plugin_name):
|
||||
"""Test that a plugin is importable without an error."""
|
||||
caplog.set_level(logging.WARNING)
|
||||
caplog.clear()
|
||||
plugins.load_plugins(include={plugin_name})
|
||||
self.load_plugins(plugin_name)
|
||||
|
||||
assert "PluginImportError" not in caplog.text, (
|
||||
f"Plugin '{plugin_name}' has issues during import."
|
||||
|
|
|
|||
Loading…
Reference in a new issue