From 3dd18dc3be58792dfbda539359c83f5ecc02551c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=A0ar=C5=ABnas=20Nejus?= Date: Thu, 24 Jul 2025 02:57:04 +0100 Subject: [PATCH 1/3] Bring back and deprecate queries and types imports from beets.library --- beets/library/__init__.py | 25 +++++++++++++++++++++++++ docs/changelog.rst | 15 +++++++++++---- 2 files changed, 36 insertions(+), 4 deletions(-) diff --git a/beets/library/__init__.py b/beets/library/__init__.py index 286b84189..929f935a6 100644 --- a/beets/library/__init__.py +++ b/beets/library/__init__.py @@ -1,8 +1,33 @@ +import warnings +from importlib import import_module + from .exceptions import FileOperationError, ReadError, WriteError from .library import Library from .models import Album, Item, LibModel from .queries import parse_query_parts, parse_query_string +NEW_MODULE_BY_NAME = dict.fromkeys( + ("DateType", "DurationType", "MusicalKey", "PathType"), "beets.dbcore.types" +) | dict.fromkeys( + ("BLOB_TYPE", "SingletonQuery", "PathQuery"), "beets.dbcore.query" +) + + +def __getattr__(name: str): + if name in NEW_MODULE_BY_NAME: + new_module = NEW_MODULE_BY_NAME[name] + warnings.warn( + ( + f"'beets.library.{name}' import is deprecated and will be removed" + f"in v3.0.0; import '{new_module}.{name}' instead." + ), + DeprecationWarning, + stacklevel=2, + ) + return getattr(import_module(new_module), name) + raise AttributeError(f"module '{__name__}' has no attribute '{name}'") + + __all__ = [ "Library", "LibModel", diff --git a/docs/changelog.rst b/docs/changelog.rst index 7fb81237e..21fe951ac 100644 --- a/docs/changelog.rst +++ b/docs/changelog.rst @@ -54,7 +54,7 @@ Bug fixes: e.g. non latin characters as 盗作. If you want to keep the legacy behavior set the config option ``spotify.search_query_ascii: yes``. :bug:`5699` - + For packagers: * Optional ``extra_tags`` parameter has been removed from @@ -67,7 +67,7 @@ For plugin developers: source registration in the process of introducing typings to the code. Custom art sources might need to be adapted. * We split the responsibilities of plugins into two base classes - #. :class:`beets.plugins.BeetsPlugin` + #. :class:`beets.plugins.BeetsPlugin` is the base class for all plugins, any plugin needs to inherit from this class. #. :class:`beets.metadata_plugin.MetadataSourcePlugin` allows plugins to act like metadata sources. E.g. used by the MusicBrainz plugin. All plugins @@ -76,7 +76,14 @@ For plugin developers: ``album_for_id``, ``candidates``, ``item_candidates``, ``album_distance``, ``track_distance`` methods, please update your plugin to inherit from the new baseclass, as otherwise your plugin will stop working with the next major release. - +* Several definitions have been moved away from ``beets.library`` module: + * ``BLOB_TYPE`` constant, ``PathQuery`` and ``SingletonQuery`` queries have moved + to ``beets.dbcore.queries`` module + * ``DateType``, ``DurationType``, ``PathType`` types and ``MusicalKey`` class have + moved to ``beets.dbcore.types`` module. + Old imports are now deprecated and will be removed in version ``3.0.0``. + + Other changes: * Refactor: Split responsibilities of Plugins into MetaDataPlugins and general Plugins. @@ -84,7 +91,7 @@ Other changes: Autogenerated API references are now located in the `docs/api` subdirectory. * :doc:`/plugins/substitute`: Fix rST formatting for example cases so that each case is shown on separate lines. -* Refactored library.py file by splitting it into multiple modules within the +* Refactored library.py file by splitting it into multiple modules within the beets/library directory. 2.3.1 (May 14, 2025) From ebc0709c400fc23785af9ac444707fc5e842eb93 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=A0ar=C5=ABnas=20Nejus?= Date: Thu, 24 Jul 2025 09:46:17 +0100 Subject: [PATCH 2/3] Deprecate beets.autotag.Distance beets.autotag.current_metadata --- beets/autotag/__init__.py | 25 +++++++++++++++++++++---- beets/library/__init__.py | 16 ++-------------- beets/util/__init__.py | 24 ++++++++++++++++++++++++ docs/changelog.rst | 16 +++++++++------- 4 files changed, 56 insertions(+), 25 deletions(-) diff --git a/beets/autotag/__init__.py b/beets/autotag/__init__.py index 8cfe534ab..4d107b3a1 100644 --- a/beets/autotag/__init__.py +++ b/beets/autotag/__init__.py @@ -16,15 +16,16 @@ from __future__ import annotations +import warnings +from importlib import import_module from typing import TYPE_CHECKING, Union from beets import config, logging -from beets.util import get_most_common_tags as current_metadata # Parts of external interface. from beets.util import unique_list -from .distance import Distance +from ..util import deprecate_imports from .hooks import AlbumInfo, AlbumMatch, TrackInfo, TrackMatch from .match import Proposal, Recommendation, tag_album, tag_item @@ -33,10 +34,27 @@ if TYPE_CHECKING: from beets.library import Album, Item, LibModel + +def __getattr__(name: str): + if name == "current_metadata": + warnings.warn( + ( + f"'beets.autotag.{name}' is deprecated and will be removed in" + " 3.0.0. Use 'beets.util.get_most_common_tags' instead." + ), + DeprecationWarning, + stacklevel=2, + ) + return import_module("beets.util").get_most_common_tags + + return deprecate_imports( + __name__, {"Distance": "beets.autotag.distance"}, name, "3.0.0" + ) + + __all__ = [ "AlbumInfo", "AlbumMatch", - "Distance", # for backwards compatibility "Proposal", "Recommendation", "TrackInfo", @@ -44,7 +62,6 @@ __all__ = [ "apply_album_metadata", "apply_item_metadata", "apply_metadata", - "current_metadata", # for backwards compatibility "tag_album", "tag_item", ] diff --git a/beets/library/__init__.py b/beets/library/__init__.py index 929f935a6..b38381438 100644 --- a/beets/library/__init__.py +++ b/beets/library/__init__.py @@ -1,5 +1,4 @@ -import warnings -from importlib import import_module +from beets.util import deprecate_imports from .exceptions import FileOperationError, ReadError, WriteError from .library import Library @@ -14,18 +13,7 @@ NEW_MODULE_BY_NAME = dict.fromkeys( def __getattr__(name: str): - if name in NEW_MODULE_BY_NAME: - new_module = NEW_MODULE_BY_NAME[name] - warnings.warn( - ( - f"'beets.library.{name}' import is deprecated and will be removed" - f"in v3.0.0; import '{new_module}.{name}' instead." - ), - DeprecationWarning, - stacklevel=2, - ) - return getattr(import_module(new_module), name) - raise AttributeError(f"module '{__name__}' has no attribute '{name}'") + return deprecate_imports(__name__, NEW_MODULE_BY_NAME, name, "3.0.0") __all__ = [ diff --git a/beets/util/__init__.py b/beets/util/__init__.py index 58b08c844..e2f7f46bd 100644 --- a/beets/util/__init__.py +++ b/beets/util/__init__.py @@ -27,6 +27,7 @@ import subprocess import sys import tempfile import traceback +import warnings from collections import Counter from collections.abc import Sequence from contextlib import suppress @@ -1191,3 +1192,26 @@ def get_temp_filename( def unique_list(elements: Iterable[T]) -> list[T]: """Return a list with unique elements in the original order.""" return list(dict.fromkeys(elements)) + + +def deprecate_imports( + old_module: str, new_module_by_name: dict[str, str], name: str, version: str +) -> Any: + """Handle deprecated module imports by redirecting to new locations. + + Facilitates gradual migration of module structure by intercepting import + attempts for relocated functionality. Issues deprecation warnings while + transparently providing access to the moved implementation, allowing + existing code to continue working during transition periods. + """ + if new_module := new_module_by_name.get(name): + warnings.warn( + ( + f"'{old_module}.{name}' is deprecated and will be removed" + f" in {version}. Use '{new_module}.{name}' instead." + ), + DeprecationWarning, + stacklevel=2, + ) + return getattr(import_module(new_module), name) + raise AttributeError(f"module '{old_module}' has no attribute '{name}'") diff --git a/docs/changelog.rst b/docs/changelog.rst index 21fe951ac..cf092d456 100644 --- a/docs/changelog.rst +++ b/docs/changelog.rst @@ -76,13 +76,15 @@ For plugin developers: ``album_for_id``, ``candidates``, ``item_candidates``, ``album_distance``, ``track_distance`` methods, please update your plugin to inherit from the new baseclass, as otherwise your plugin will stop working with the next major release. -* Several definitions have been moved away from ``beets.library`` module: - * ``BLOB_TYPE`` constant, ``PathQuery`` and ``SingletonQuery`` queries have moved - to ``beets.dbcore.queries`` module - * ``DateType``, ``DurationType``, ``PathType`` types and ``MusicalKey`` class have - moved to ``beets.dbcore.types`` module. - Old imports are now deprecated and will be removed in version ``3.0.0``. - +* Several definitions have been moved: + * ``BLOB_TYPE`` constant, ``PathQuery`` and ``SingletonQuery`` queries have + moved from ``beets.library`` to ``beets.dbcore.query`` module + * ``DateType``, ``DurationType``, ``PathType`` types and ``MusicalKey`` + class have moved from ``beets.library`` to ``beets.dbcore.types`` module. + * ``Distance`` has moved from ``beets.autotag`` to + ``beets.autotag.distance`` module. + * ``beets.autotag.current_metadata`` has been renamed to + ``beets.util.get_most_common_tags``. Other changes: From c2a8651b68332e17cb74314d2771298e16fe6788 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=A0ar=C5=ABnas=20Nejus?= Date: Thu, 24 Jul 2025 09:58:46 +0100 Subject: [PATCH 3/3] Deprecate decargs --- beets/ui/__init__.py | 16 ++++++++++++++++ docs/changelog.rst | 4 ++++ 2 files changed, 20 insertions(+) diff --git a/beets/ui/__init__.py b/beets/ui/__init__.py index 8b2419a07..b5e2cf579 100644 --- a/beets/ui/__init__.py +++ b/beets/ui/__init__.py @@ -28,6 +28,7 @@ import struct import sys import textwrap import traceback +import warnings from difflib import SequenceMatcher from typing import TYPE_CHECKING, Any, Callable @@ -104,6 +105,21 @@ def _stream_encoding(stream, default="utf-8"): return stream.encoding or default +def decargs(arglist): + """Given a list of command-line argument bytestrings, attempts to + decode them to Unicode strings when running under Python 2. + + .. deprecated:: 2.4.0 + This function will be removed in 3.0.0. + """ + warnings.warn( + "decargs() is deprecated and will be removed in version 3.0.0.", + DeprecationWarning, + stacklevel=2, + ) + return arglist + + def print_(*strings: str, end: str = "\n") -> None: """Like print, but rather than raising an error when a character is not in the terminal's encoding's character set, just silently diff --git a/docs/changelog.rst b/docs/changelog.rst index cf092d456..cc35552be 100644 --- a/docs/changelog.rst +++ b/docs/changelog.rst @@ -86,6 +86,10 @@ For plugin developers: * ``beets.autotag.current_metadata`` has been renamed to ``beets.util.get_most_common_tags``. + Old imports are now deprecated and will be removed in version ``3.0.0``. +* ``beets.ui.decargs`` is deprecated and will be removed in version ``3.0.0``. + + Other changes: * Refactor: Split responsibilities of Plugins into MetaDataPlugins and general Plugins.