Skip to content

Commit 54b31d0

Browse files
committed
Use only plugins/disabled_plugins config in plugin loading
1 parent e9feb41 commit 54b31d0

File tree

5 files changed

+58
-41
lines changed

5 files changed

+58
-41
lines changed

beets/plugins.py

Lines changed: 13 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131

3232
import beets
3333
from beets import logging
34+
from beets.util import unique_list
3435

3536
if TYPE_CHECKING:
3637
from collections.abc import Callable, Iterable, Sequence
@@ -275,9 +276,7 @@ def helper(func: TFunc[Item]) -> TFunc[Item]:
275276
return helper
276277

277278

278-
def get_plugin_names(
279-
include: set[str] | None = None, exclude: set[str] | None = None
280-
) -> set[str]:
279+
def get_plugin_names() -> list[str]:
281280
"""Discover and return the set of plugin names to be loaded.
282281
283282
Configures the plugin search paths and resolves the final set of plugins
@@ -298,14 +297,18 @@ def get_plugin_names(
298297
# For backwards compatibility, also support plugin paths that
299298
# *contain* a `beetsplug` package.
300299
sys.path += paths
301-
plugins = include or set(beets.config["plugins"].as_str_seq())
300+
plugins = unique_list(beets.config["plugins"].as_str_seq())
302301
# TODO: Remove in v3.0.0
303-
if "musicbrainz" in beets.config and beets.config["musicbrainz"].get().get(
304-
"enabled"
302+
if (
303+
"musicbrainz" not in plugins
304+
and "musicbrainz" in beets.config
305+
and beets.config["musicbrainz"].get().get("enabled")
305306
):
306-
plugins.add("musicbrainz")
307+
plugins.append("musicbrainz")
307308

308-
return plugins - (exclude or set())
309+
beets.config.add({"disabled_plugins": []})
310+
disabled_plugins = set(beets.config["disabled_plugins"].as_str_seq())
311+
return [p for p in plugins if p not in disabled_plugins]
309312

310313

311314
def _get_plugin(name: str) -> BeetsPlugin | None:
@@ -342,15 +345,15 @@ def _get_plugin(name: str) -> BeetsPlugin | None:
342345
_instances: list[BeetsPlugin] = []
343346

344347

345-
def load_plugins(*args, **kwargs) -> None:
348+
def load_plugins() -> None:
346349
"""Initialize the plugin system by loading all configured plugins.
347350
348351
Performs one-time plugin discovery and instantiation, storing loaded plugin
349352
instances globally. Emits a pluginload event after successful initialization
350353
to notify other components.
351354
"""
352355
if not _instances:
353-
names = get_plugin_names(*args, **kwargs)
356+
names = get_plugin_names()
354357
log.info("Loading plugins: {}", ", ".join(sorted(names)))
355358
_instances.extend(filter(None, map(_get_plugin, names)))
356359

beets/ui/__init__.py

Lines changed: 17 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1579,13 +1579,7 @@ def _setup(
15791579
"""
15801580
config = _configure(options)
15811581

1582-
def _parse_list(option: str | None) -> set[str]:
1583-
return set((option or "").split(",")) - {""}
1584-
1585-
plugins.load_plugins(
1586-
include=_parse_list(options.plugins),
1587-
exclude=_parse_list(options.exclude),
1588-
)
1582+
plugins.load_plugins()
15891583

15901584
# Get the default subcommands.
15911585
from beets.ui.commands import default_commands
@@ -1704,16 +1698,31 @@ def _raw_main(args: list[str], lib=None) -> None:
17041698
parser.add_option(
17051699
"-c", "--config", dest="config", help="path to configuration file"
17061700
)
1701+
1702+
def parse_csl_callback(
1703+
option: optparse.Option, _, value: str, parser: SubcommandsOptionParser
1704+
):
1705+
"""Parse a comma-separated list of values."""
1706+
setattr(
1707+
parser.values,
1708+
option.dest, # type: ignore[arg-type]
1709+
list(filter(None, value.split(","))),
1710+
)
1711+
17071712
parser.add_option(
17081713
"-p",
17091714
"--plugins",
17101715
dest="plugins",
1716+
action="callback",
1717+
callback=parse_csl_callback,
17111718
help="a comma-separated list of plugins to load",
17121719
)
17131720
parser.add_option(
17141721
"-P",
17151722
"--disable-plugins",
1716-
dest="exclude",
1723+
dest="disabled_plugins",
1724+
action="callback",
1725+
callback=parse_csl_callback,
17171726
help="a comma-separated list of plugins to disable",
17181727
)
17191728
parser.add_option(

beetsplug/loadext.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ def __init__(self):
2525
super().__init__()
2626

2727
if not Database.supports_extensions:
28-
self._log.warn(
28+
self._log.warning(
2929
"loadext is enabled but the current SQLite "
3030
"installation does not support extensions"
3131
)

docs/changelog.rst

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,9 @@ For plugin developers:
9797
type checking for downstream users of the beets API.
9898
* ``plugins.find_plugins`` function does not anymore load plugins. You need to
9999
explicitly call ``plugins.load_plugins()`` to load them.
100+
* ``plugins.load_plugins`` function does not anymore accept the list of plugins
101+
to load. Instead, it loads all plugins that are configured by
102+
:ref:`plugins-config` configuration.
100103

101104
Other changes:
102105

test/test_plugins.py

Lines changed: 24 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -91,9 +91,6 @@ def setUp(self):
9191

9292

9393
class EventsTest(PluginImportTestCase):
94-
def setUp(self):
95-
super().setUp()
96-
9794
def test_import_task_created(self):
9895
self.importer = self.setup_importer(pretend=True)
9996

@@ -472,39 +469,44 @@ def get_available_plugins():
472469
]
473470

474471

475-
class TestImportAllPlugins(PluginMixin):
476-
def unimport_plugins(self):
472+
class TestImportPlugin(PluginMixin):
473+
@pytest.fixture(params=get_available_plugins())
474+
def plugin_name(self, request):
475+
"""Fixture to provide the name of each available plugin."""
476+
name = request.param
477+
478+
# skip gstreamer plugins on windows
479+
gstreamer_plugins = {"bpd", "replaygain"}
480+
if sys.platform == "win32" and name in gstreamer_plugins:
481+
pytest.skip(f"GStreamer is not available on Windows: {name}")
482+
483+
return name
484+
485+
def unload_plugins(self):
477486
"""Unimport plugins before each test to avoid conflicts."""
478-
self.unload_plugins()
487+
super().unload_plugins()
479488
for mod in list(sys.modules):
480489
if mod.startswith("beetsplug."):
481490
del sys.modules[mod]
482491

483492
@pytest.fixture(autouse=True)
484493
def cleanup(self):
485494
"""Ensure plugins are unimported before and after each test."""
486-
self.unimport_plugins()
495+
self.unload_plugins()
487496
yield
488-
self.unimport_plugins()
497+
self.unload_plugins()
489498

490499
@pytest.mark.skipif(
491500
os.environ.get("GITHUB_ACTIONS") != "true",
492-
reason="Requires all dependencies to be installed, "
493-
+ "which we can't guarantee in the local environment.",
501+
reason=(
502+
"Requires all dependencies to be installed, which we can't"
503+
" guarantee in the local environment."
504+
),
494505
)
495-
@pytest.mark.parametrize("plugin_name", get_available_plugins())
496-
def test_import_plugin(self, caplog, plugin_name): #
497-
"""Test that a plugin is importable without an error using the
498-
load_plugins function."""
499-
500-
# skip gstreamer plugins on windows
501-
gstreamer_plugins = ["bpd", "replaygain"]
502-
if sys.platform == "win32" and plugin_name in gstreamer_plugins:
503-
pytest.xfail("GStreamer is not available on Windows: {plugin_name}")
504-
506+
def test_import_plugin(self, caplog, plugin_name):
507+
"""Test that a plugin is importable without an error."""
505508
caplog.set_level(logging.WARNING)
506-
caplog.clear()
507-
plugins.load_plugins(include={plugin_name})
509+
self.load_plugins(plugin_name)
508510

509511
assert "PluginImportError" not in caplog.text, (
510512
f"Plugin '{plugin_name}' has issues during import."

0 commit comments

Comments
 (0)