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 286b84189..b38381438 100644 --- a/beets/library/__init__.py +++ b/beets/library/__init__.py @@ -1,8 +1,21 @@ +from beets.util import deprecate_imports + 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): + return deprecate_imports(__name__, NEW_MODULE_BY_NAME, name, "3.0.0") + + __all__ = [ "Library", "LibModel", 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/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 39af2f7c3..23672f2bd 100644 --- a/docs/changelog.rst +++ b/docs/changelog.rst @@ -55,7 +55,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 @@ -68,7 +68,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 @@ -77,7 +77,20 @@ 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: + * ``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``. + + 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. @@ -85,7 +98,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)