Cache found metadata source plugins

This commit is contained in:
Šarūnas Nejus 2025-10-07 14:01:20 +01:00
parent 01e2eb4665
commit 96670cf971
No known key found for this signature in database
2 changed files with 40 additions and 31 deletions

View file

@ -9,7 +9,7 @@ from __future__ import annotations
import abc import abc
import re import re
import warnings from functools import cache
from typing import TYPE_CHECKING, Generic, Literal, Sequence, TypedDict, TypeVar from typing import TYPE_CHECKING, Generic, Literal, Sequence, TypedDict, TypeVar
import unidecode import unidecode
@ -29,30 +29,11 @@ if TYPE_CHECKING:
from .autotag.hooks import AlbumInfo, Item, TrackInfo from .autotag.hooks import AlbumInfo, Item, TrackInfo
@cache
def find_metadata_source_plugins() -> list[MetadataSourcePlugin]: def find_metadata_source_plugins() -> list[MetadataSourcePlugin]:
"""Returns a list of MetadataSourcePlugin subclass instances """Return a list of all loaded metadata source plugins."""
# TODO: Make this an isinstance(MetadataSourcePlugin, ...) check in v3.0.0
Resolved from all currently loaded beets plugins. return [p for p in find_plugins() if hasattr(p, "data_source")] # type: ignore[misc]
"""
all_plugins = find_plugins()
metadata_plugins: list[MetadataSourcePlugin | BeetsPlugin] = []
for plugin in all_plugins:
if isinstance(plugin, MetadataSourcePlugin):
metadata_plugins.append(plugin)
elif hasattr(plugin, "data_source"):
# TODO: Remove this in the future major release, v3.0.0
warnings.warn(
f"{plugin.__class__.__name__} is used as a legacy metadata source. "
"It should extend MetadataSourcePlugin instead of BeetsPlugin. "
"Support for this will be removed in the v3.0.0 release!",
DeprecationWarning,
stacklevel=2,
)
metadata_plugins.append(plugin)
# typeignore: BeetsPlugin is not a MetadataSourcePlugin (legacy support)
return metadata_plugins # type: ignore[return-value]
@notify_info_yielded("albuminfo_received") @notify_info_yielded("albuminfo_received")

View file

@ -20,6 +20,7 @@ import abc
import inspect import inspect
import re import re
import sys import sys
import warnings
from collections import defaultdict from collections import defaultdict
from functools import wraps from functools import wraps
from importlib import import_module from importlib import import_module
@ -160,19 +161,46 @@ class BeetsPlugin(metaclass=abc.ABCMeta):
import_stages: list[ImportStageFunc] import_stages: list[ImportStageFunc]
def __init_subclass__(cls) -> None: def __init_subclass__(cls) -> None:
# Dynamically copy methods to BeetsPlugin for legacy support """Enable legacy metadatasource plugins to work with the new interface.
# TODO: Remove this in the future major release, v3.0.0
When a plugin subclass of BeetsPlugin defines a `data_source` attribute
but does not inherit from MetadataSourcePlugin, this hook:
1. Skips abstract classes.
2. Warns that the class should extend MetadataSourcePlugin (deprecation).
3. Copies any nonabstract methods from MetadataSourcePlugin onto the
subclass to provide the full plugin API.
This compatibility layer will be removed in the v3.0.0 release.
"""
# TODO: Remove in v3.0.0
if inspect.isabstract(cls): if inspect.isabstract(cls):
return return
from beets.metadata_plugins import MetadataSourcePlugin from beets.metadata_plugins import MetadataSourcePlugin
abstractmethods = MetadataSourcePlugin.__abstractmethods__ if issubclass(cls, MetadataSourcePlugin) or not hasattr(
for name, method in inspect.getmembers( cls, "data_source"
MetadataSourcePlugin, predicate=inspect.isfunction
): ):
if name not in abstractmethods and not hasattr(cls, name): return
setattr(cls, name, method)
warnings.warn(
f"{cls.__name__} is used as a legacy metadata source. "
"It should extend MetadataSourcePlugin instead of BeetsPlugin. "
"Support for this will be removed in the v3.0.0 release!",
DeprecationWarning,
stacklevel=3,
)
for name, method in inspect.getmembers(
MetadataSourcePlugin,
predicate=lambda f: (
inspect.isfunction(f)
and f.__name__ not in MetadataSourcePlugin.__abstractmethods__
and not hasattr(cls, f.__name__)
),
):
setattr(cls, name, method)
def __init__(self, name: str | None = None): def __init__(self, name: str | None = None):
"""Perform one-time plugin setup.""" """Perform one-time plugin setup."""