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 re
import warnings
from functools import cache
from typing import TYPE_CHECKING, Generic, Literal, Sequence, TypedDict, TypeVar
import unidecode
@ -29,30 +29,11 @@ if TYPE_CHECKING:
from .autotag.hooks import AlbumInfo, Item, TrackInfo
@cache
def find_metadata_source_plugins() -> list[MetadataSourcePlugin]:
"""Returns a list of MetadataSourcePlugin subclass instances
Resolved from all currently loaded beets plugins.
"""
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]
"""Return a list of all loaded metadata source plugins."""
# TODO: Make this an isinstance(MetadataSourcePlugin, ...) check in v3.0.0
return [p for p in find_plugins() if hasattr(p, "data_source")] # type: ignore[misc]
@notify_info_yielded("albuminfo_received")

View file

@ -20,6 +20,7 @@ import abc
import inspect
import re
import sys
import warnings
from collections import defaultdict
from functools import wraps
from importlib import import_module
@ -160,18 +161,45 @@ class BeetsPlugin(metaclass=abc.ABCMeta):
import_stages: list[ImportStageFunc]
def __init_subclass__(cls) -> None:
# Dynamically copy methods to BeetsPlugin for legacy support
# TODO: Remove this in the future major release, v3.0.0
"""Enable legacy metadatasource plugins to work with the new interface.
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):
return
from beets.metadata_plugins import MetadataSourcePlugin
abstractmethods = MetadataSourcePlugin.__abstractmethods__
for name, method in inspect.getmembers(
MetadataSourcePlugin, predicate=inspect.isfunction
if issubclass(cls, MetadataSourcePlugin) or not hasattr(
cls, "data_source"
):
return
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__)
),
):
if name not in abstractmethods and not hasattr(cls, name):
setattr(cls, name, method)
def __init__(self, name: str | None = None):