diff --git a/beets/autotag/match.py b/beets/autotag/match.py index 8adbaeda1..374ea3c13 100644 --- a/beets/autotag/match.py +++ b/beets/autotag/match.py @@ -25,7 +25,7 @@ import lap import numpy as np from beets import config, logging, metadata_plugins, plugins -from beets.autotag import AlbumInfo, AlbumMatch, TrackInfo, TrackMatch, hooks +from beets.autotag import AlbumMatch, TrackMatch, hooks from beets.util import get_most_common_tags from .distance import VA_ARTISTS, distance, track_distance @@ -33,6 +33,7 @@ from .distance import VA_ARTISTS, distance, track_distance if TYPE_CHECKING: from collections.abc import Iterable, Sequence + from beets.autotag import AlbumInfo, TrackInfo from beets.library import Item # Global logger. diff --git a/beets/dbcore/db.py b/beets/dbcore/db.py index 5d721a121..08664bdf2 100755 --- a/beets/dbcore/db.py +++ b/beets/dbcore/db.py @@ -26,16 +26,9 @@ import threading import time from abc import ABC from collections import defaultdict -from collections.abc import ( - Callable, - Generator, - Iterable, - Iterator, - Mapping, - Sequence, -) +from collections.abc import Mapping from functools import cached_property -from sqlite3 import Connection, sqlite_version_info +from sqlite3 import sqlite_version_info from typing import TYPE_CHECKING, Any, AnyStr, Generic from typing_extensions import ( @@ -48,20 +41,20 @@ import beets from ..util import cached_classproperty, functemplate from . import types -from .query import ( - FieldQueryType, - FieldSort, - MatchQuery, - NullSort, - Query, - Sort, - TrueQuery, -) +from .query import MatchQuery, NullSort, TrueQuery if TYPE_CHECKING: + from collections.abc import ( + Callable, + Generator, + Iterable, + Iterator, + Sequence, + ) + from sqlite3 import Connection from types import TracebackType - from .query import SQLiteType + from .query import FieldQueryType, FieldSort, Query, Sort, SQLiteType D = TypeVar("D", bound="Database", default=Any) diff --git a/beets/dbcore/query.py b/beets/dbcore/query.py index dfeb42707..9556cdf77 100644 --- a/beets/dbcore/query.py +++ b/beets/dbcore/query.py @@ -20,7 +20,7 @@ import os import re import unicodedata from abc import ABC, abstractmethod -from collections.abc import Iterator, MutableSequence, Sequence +from collections.abc import Sequence from datetime import datetime, timedelta from functools import cached_property, reduce from operator import mul, or_ @@ -31,6 +31,8 @@ from beets import util from beets.util.units import raw_seconds_short if TYPE_CHECKING: + from collections.abc import Iterator, MutableSequence + from beets.dbcore.db import AnyModel, Model P = TypeVar("P", default=Any) diff --git a/beets/importer/session.py b/beets/importer/session.py index 83c5ad4e3..123cc7248 100644 --- a/beets/importer/session.py +++ b/beets/importer/session.py @@ -17,7 +17,7 @@ import os import time from typing import TYPE_CHECKING -from beets import config, dbcore, library, logging, plugins, util +from beets import config, logging, plugins, util from beets.importer.tasks import Action from beets.util import displayable_path, normpath, pipeline, syspath @@ -27,6 +27,7 @@ from .state import ImportState if TYPE_CHECKING: from collections.abc import Sequence + from beets import dbcore, library from beets.util import PathBytes from .tasks import ImportTask diff --git a/beets/importer/tasks.py b/beets/importer/tasks.py index 3a9c044b2..f6417401b 100644 --- a/beets/importer/tasks.py +++ b/beets/importer/tasks.py @@ -20,7 +20,7 @@ import re import shutil import time from collections import defaultdict -from collections.abc import Callable, Iterable, Sequence +from collections.abc import Callable from enum import Enum from tempfile import mkdtemp from typing import TYPE_CHECKING, Any @@ -33,6 +33,8 @@ from beets.dbcore.query import PathQuery from .state import ImportState if TYPE_CHECKING: + from collections.abc import Iterable, Sequence + from beets.autotag.match import Recommendation from .session import ImportSession diff --git a/beets/logging.py b/beets/logging.py index 5a837cd80..ecde9b33d 100644 --- a/beets/logging.py +++ b/beets/logging.py @@ -35,7 +35,6 @@ from logging import ( Handler, Logger, NullHandler, - RootLogger, StreamHandler, ) from typing import TYPE_CHECKING, Any, TypeVar, Union, overload @@ -56,6 +55,7 @@ __all__ = [ if TYPE_CHECKING: from collections.abc import Mapping + from logging import RootLogger T = TypeVar("T") from types import TracebackType diff --git a/beets/ui/commands/import_/display.py b/beets/ui/commands/import_/display.py index 113462d19..7858c7152 100644 --- a/beets/ui/commands/import_/display.py +++ b/beets/ui/commands/import_/display.py @@ -7,7 +7,7 @@ from typing import TYPE_CHECKING, TypedDict from typing_extensions import NotRequired -from beets import autotag, config, ui +from beets import config, ui from beets.autotag import hooks from beets.util import displayable_path from beets.util.units import human_seconds_short @@ -17,6 +17,7 @@ if TYPE_CHECKING: import confuse + from beets import autotag from beets.autotag.distance import Distance from beets.library.models import Item from beets.ui import ColorName diff --git a/beets/ui/commands/move.py b/beets/ui/commands/move.py index 40a9d1b83..206c24dcf 100644 --- a/beets/ui/commands/move.py +++ b/beets/ui/commands/move.py @@ -1,18 +1,18 @@ """The 'move' command: Move/copy files to the library or a new base directory.""" +from __future__ import annotations + import os +from typing import TYPE_CHECKING from beets import logging, ui -from beets.util import ( - MoveOperation, - PathLike, - displayable_path, - normpath, - syspath, -) +from beets.util import MoveOperation, displayable_path, normpath, syspath from .utils import do_query +if TYPE_CHECKING: + from beets.util import PathLike + # Global logger. log = logging.getLogger("beets") diff --git a/beets/util/__init__.py b/beets/util/__init__.py index 517e076de..10508aaaf 100644 --- a/beets/util/__init__.py +++ b/beets/util/__init__.py @@ -28,7 +28,7 @@ import sys import tempfile import traceback from collections import Counter -from collections.abc import Callable, Sequence +from collections.abc import Sequence from contextlib import suppress from enum import Enum from functools import cache @@ -54,7 +54,7 @@ import beets from beets.util import hidden if TYPE_CHECKING: - from collections.abc import Iterable, Iterator + from collections.abc import Callable, Iterable, Iterator from logging import Logger from beets.library import Item diff --git a/beetsplug/albumtypes.py b/beetsplug/albumtypes.py index 180773f58..3b6535d85 100644 --- a/beetsplug/albumtypes.py +++ b/beetsplug/albumtypes.py @@ -14,11 +14,17 @@ """Adds an album template field for formatted album types.""" -from beets.library import Album +from __future__ import annotations + +from typing import TYPE_CHECKING + from beets.plugins import BeetsPlugin from .musicbrainz import VARIOUS_ARTISTS_ID +if TYPE_CHECKING: + from beets.library import Album + class AlbumTypesPlugin(BeetsPlugin): """Adds an album template field for formatted album types.""" diff --git a/beetsplug/aura.py b/beetsplug/aura.py index 7b75f31e5..c1877db82 100644 --- a/beetsplug/aura.py +++ b/beetsplug/aura.py @@ -14,12 +14,13 @@ """An AURA server using Flask.""" +from __future__ import annotations + import os import re -from collections.abc import Mapping from dataclasses import dataclass from mimetypes import guess_type -from typing import ClassVar +from typing import TYPE_CHECKING, ClassVar from flask import ( Blueprint, @@ -40,12 +41,17 @@ from beets.dbcore.query import ( NotQuery, RegexpQuery, SlowFieldSort, - SQLiteType, ) -from beets.library import Album, Item, LibModel, Library +from beets.library import Album, Item from beets.plugins import BeetsPlugin from beets.ui import Subcommand, _open_library +if TYPE_CHECKING: + from collections.abc import Mapping + + from beets.dbcore.query import SQLiteType + from beets.library import LibModel, Library + # Constants # AURA server information diff --git a/beetsplug/chroma.py b/beetsplug/chroma.py index 1e9835789..748e6f5cd 100644 --- a/beetsplug/chroma.py +++ b/beetsplug/chroma.py @@ -16,20 +16,26 @@ autotagger. Requires the pyacoustid library. """ +from __future__ import annotations + import re from collections import defaultdict -from collections.abc import Iterable from functools import cached_property, partial +from typing import TYPE_CHECKING import acoustid import confuse from beets import config, ui, util from beets.autotag.distance import Distance -from beets.autotag.hooks import TrackInfo from beets.metadata_plugins import MetadataSourcePlugin from beetsplug.musicbrainz import MusicBrainzPlugin +if TYPE_CHECKING: + from collections.abc import Iterable + + from beets.autotag.hooks import TrackInfo + API_KEY = "1vOwZtEn" SCORE_THRESH = 0.5 TRACK_ID_WEIGHT = 10.0 diff --git a/beetsplug/deezer.py b/beetsplug/deezer.py index ef27dddc7..f113dcca2 100644 --- a/beetsplug/deezer.py +++ b/beetsplug/deezer.py @@ -25,16 +25,13 @@ import requests from beets import ui from beets.autotag import AlbumInfo, TrackInfo from beets.dbcore import types -from beets.metadata_plugins import ( - IDResponse, - SearchApiMetadataSourcePlugin, - SearchFilter, -) +from beets.metadata_plugins import IDResponse, SearchApiMetadataSourcePlugin if TYPE_CHECKING: from collections.abc import Sequence from beets.library import Item, Library + from beets.metadata_plugins import SearchFilter from ._typing import JSONDict diff --git a/beetsplug/mbpseudo.py b/beetsplug/mbpseudo.py index 30ef2e428..d084d1531 100644 --- a/beetsplug/mbpseudo.py +++ b/beetsplug/mbpseudo.py @@ -24,7 +24,7 @@ import mediafile from typing_extensions import override from beets import config -from beets.autotag.distance import Distance, distance +from beets.autotag.distance import distance from beets.autotag.hooks import AlbumInfo from beets.autotag.match import assign_items from beets.plugins import find_plugins @@ -39,6 +39,7 @@ if TYPE_CHECKING: from collections.abc import Iterable, Sequence from beets.autotag import AlbumMatch + from beets.autotag.distance import Distance from beets.library import Item from beetsplug._typing import JSONDict diff --git a/beetsplug/missing.py b/beetsplug/missing.py index 63a7bae22..081a73dcd 100644 --- a/beetsplug/missing.py +++ b/beetsplug/missing.py @@ -15,19 +15,26 @@ """List missing tracks.""" +from __future__ import annotations + from collections import defaultdict -from collections.abc import Iterator +from typing import TYPE_CHECKING import requests from beets import config, metadata_plugins from beets.dbcore import types -from beets.library import Album, Item, Library +from beets.library import Item from beets.plugins import BeetsPlugin from beets.ui import Subcommand, print_ from ._utils.musicbrainz import MusicBrainzAPIMixin +if TYPE_CHECKING: + from collections.abc import Iterator + + from beets.library import Album, Library + MB_ARTIST_QUERY = r"mb_albumartistid::^\w{8}-\w{4}-\w{4}-\w{4}-\w{12}$" diff --git a/beetsplug/playlist.py b/beetsplug/playlist.py index 07c12e0e0..34e7a2fe3 100644 --- a/beetsplug/playlist.py +++ b/beetsplug/playlist.py @@ -10,17 +10,20 @@ # # The above copyright notice and this permission notice shall be # included in all copies or substantial portions of the Software. - +from __future__ import annotations import os import tempfile -from collections.abc import Sequence from pathlib import Path +from typing import TYPE_CHECKING import beets from beets.dbcore.query import BLOB_TYPE, InQuery from beets.util import path_as_posix +if TYPE_CHECKING: + from collections.abc import Sequence + def is_m3u_file(path: str) -> bool: return Path(path).suffix.lower() in {".m3u", ".m3u8"} diff --git a/beetsplug/replace.py b/beetsplug/replace.py index 0c570877b..b585a13c1 100644 --- a/beetsplug/replace.py +++ b/beetsplug/replace.py @@ -1,12 +1,17 @@ +from __future__ import annotations + import shutil from pathlib import Path +from typing import TYPE_CHECKING import mediafile from beets import ui, util -from beets.library import Item, Library from beets.plugins import BeetsPlugin +if TYPE_CHECKING: + from beets.library import Item, Library + class ReplacePlugin(BeetsPlugin): def commands(self): diff --git a/beetsplug/smartplaylist.py b/beetsplug/smartplaylist.py index ed417f2b9..e22a65787 100644 --- a/beetsplug/smartplaylist.py +++ b/beetsplug/smartplaylist.py @@ -17,13 +17,13 @@ from __future__ import annotations import os -from typing import Any, TypeAlias +from typing import TYPE_CHECKING, Any, TypeAlias from urllib.parse import quote from urllib.request import pathname2url from beets import ui from beets.dbcore.query import ParsingError, Query, Sort -from beets.library import Album, Item, Library, parse_query_string +from beets.library import Album, Item, parse_query_string from beets.plugins import BeetsPlugin from beets.plugins import send as send_event from beets.util import ( @@ -36,6 +36,9 @@ from beets.util import ( syspath, ) +if TYPE_CHECKING: + from beets.library import Library + QueryAndSort = tuple[Query, Sort] PlaylistQuery = Query | tuple[QueryAndSort, ...] | None PlaylistMatch: TypeAlias = tuple[ diff --git a/beetsplug/spotify.py b/beetsplug/spotify.py index ab920cdd4..4a55dea5d 100644 --- a/beetsplug/spotify.py +++ b/beetsplug/spotify.py @@ -36,16 +36,13 @@ from beets import ui from beets.autotag.hooks import AlbumInfo, TrackInfo from beets.dbcore import types from beets.library import Library -from beets.metadata_plugins import ( - IDResponse, - SearchApiMetadataSourcePlugin, - SearchFilter, -) +from beets.metadata_plugins import IDResponse, SearchApiMetadataSourcePlugin if TYPE_CHECKING: from collections.abc import Sequence from beets.library import Library + from beets.metadata_plugins import SearchFilter from beetsplug._typing import JSONDict DEFAULT_WAITING_TIME = 5 diff --git a/beetsplug/titlecase.py b/beetsplug/titlecase.py index e7003fd28..d722d4d16 100644 --- a/beetsplug/titlecase.py +++ b/beetsplug/titlecase.py @@ -16,18 +16,23 @@ Title case logic is derived from the python-titlecase library. Provides a template function and a tag modification function.""" +from __future__ import annotations + import re from functools import cached_property -from typing import TypedDict +from typing import TYPE_CHECKING, TypedDict from titlecase import titlecase from beets import ui -from beets.autotag.hooks import AlbumInfo, Info -from beets.importer import ImportSession, ImportTask -from beets.library import Item +from beets.autotag.hooks import AlbumInfo from beets.plugins import BeetsPlugin +if TYPE_CHECKING: + from beets.autotag.hooks import Info + from beets.importer import ImportSession, ImportTask + from beets.library import Item + __author__ = "henryoberholtzer@gmail.com" __version__ = "1.0" diff --git a/poetry.lock b/poetry.lock index 5a0832399..8eb7c74ac 100644 --- a/poetry.lock +++ b/poetry.lock @@ -4583,4 +4583,4 @@ web = ["flask", "flask-cors"] [metadata] lock-version = "2.0" python-versions = ">=3.10,<4" -content-hash = "8a1714daca55eab559558f2d4bd63d4857686eb607bf4b24f1ea6dbd412e6641" +content-hash = "f8ce55ae74c5e3c5d1d330582f83dae30ef963a0b8dd8c8b79f16c3bcfdb525a" diff --git a/pyproject.toml b/pyproject.toml index dbfc2715b..1e98b189a 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -117,7 +117,7 @@ titlecase = "^2.4.1" [tool.poetry.group.lint.dependencies] docstrfmt = ">=1.11.1" -ruff = ">=0.6.4" +ruff = ">=0.13.0" sphinx-lint = ">=1.0.0" [tool.poetry.group.typing.dependencies] @@ -226,7 +226,7 @@ cmd = "make -C docs $COMMANDS" [tool.poe.tasks.format] help = "Format the codebase" -cmd = "ruff format" +cmd = "ruff format --config=pyproject.toml" [tool.poe.tasks.format-docs] help = "Format the documentation" @@ -234,7 +234,7 @@ cmd = "docstrfmt docs *.rst" [tool.poe.tasks.lint] help = "Check the code for linting issues. Accepts ruff options." -cmd = "ruff check" +cmd = "ruff check --config=pyproject.toml" [tool.poe.tasks.lint-docs] help = "Lint the documentation" @@ -294,6 +294,7 @@ target-version = "py39" line-length = 80 [tool.ruff.lint] +future-annotations = true select = [ # "ARG", # flake8-unused-arguments # "C4", # flake8-comprehensions diff --git a/test/plugins/test_albumtypes.py b/test/plugins/test_albumtypes.py index 0a9d53349..371bf0415 100644 --- a/test/plugins/test_albumtypes.py +++ b/test/plugins/test_albumtypes.py @@ -14,12 +14,17 @@ """Tests for the 'albumtypes' plugin.""" -from collections.abc import Sequence +from __future__ import annotations + +from typing import TYPE_CHECKING from beets.test.helper import PluginTestCase from beetsplug.albumtypes import AlbumTypesPlugin from beetsplug.musicbrainz import VARIOUS_ARTISTS_ID +if TYPE_CHECKING: + from collections.abc import Sequence + class AlbumTypesPluginTest(PluginTestCase): """Tests for albumtypes plugin.""" diff --git a/test/plugins/test_aura.py b/test/plugins/test_aura.py index 7e840008e..188c44c9e 100644 --- a/test/plugins/test_aura.py +++ b/test/plugins/test_aura.py @@ -1,13 +1,17 @@ +from __future__ import annotations + import os from http import HTTPStatus from pathlib import Path -from typing import Any +from typing import TYPE_CHECKING, Any import pytest -from flask.testing import Client from beets.test.helper import TestHelper +if TYPE_CHECKING: + from flask.testing import Client + @pytest.fixture(scope="session", autouse=True) def helper(): diff --git a/test/plugins/test_convert.py b/test/plugins/test_convert.py index 9ae0ebf6d..2a1a3b94d 100644 --- a/test/plugins/test_convert.py +++ b/test/plugins/test_convert.py @@ -11,14 +11,14 @@ # # The above copyright notice and this permission notice shall be # included in all copies or substantial portions of the Software. - +from __future__ import annotations import fnmatch import os.path import re import sys import unittest -from pathlib import Path +from typing import TYPE_CHECKING import pytest from mediafile import MediaFile @@ -35,6 +35,9 @@ from beets.test.helper import ( ) from beetsplug import convert +if TYPE_CHECKING: + from pathlib import Path + def shell_quote(text): import shlex diff --git a/test/plugins/test_ftintitle.py b/test/plugins/test_ftintitle.py index 51bd4f9c8..aff4dda18 100644 --- a/test/plugins/test_ftintitle.py +++ b/test/plugins/test_ftintitle.py @@ -14,15 +14,21 @@ """Tests for the 'ftintitle' plugin.""" -from collections.abc import Generator -from typing import TypeAlias +from __future__ import annotations + +from typing import TYPE_CHECKING, TypeAlias import pytest -from beets.library.models import Album, Item +from beets.library.models import Album from beets.test.helper import PluginTestCase from beetsplug import ftintitle +if TYPE_CHECKING: + from collections.abc import Generator + + from beets.library.models import Item + ConfigValue: TypeAlias = str | bool | list[str] diff --git a/test/plugins/test_lyrics.py b/test/plugins/test_lyrics.py index 945a7158c..23db03fef 100644 --- a/test/plugins/test_lyrics.py +++ b/test/plugins/test_lyrics.py @@ -14,11 +14,13 @@ """Tests for the 'lyrics' plugin.""" +from __future__ import annotations + import re import textwrap from functools import partial from http import HTTPStatus -from pathlib import Path +from typing import TYPE_CHECKING import pytest @@ -26,7 +28,12 @@ from beets.library import Item from beets.test.helper import PluginMixin, TestHelper from beetsplug import lyrics -from .lyrics_pages import LyricsPage, lyrics_pages +from .lyrics_pages import lyrics_pages + +if TYPE_CHECKING: + from pathlib import Path + + from .lyrics_pages import LyricsPage PHRASE_BY_TITLE = { "Lady Madonna": "friday night arrives without a suitcase", diff --git a/test/plugins/test_mbpseudo.py b/test/plugins/test_mbpseudo.py index 6b382ab16..2fb6321b3 100644 --- a/test/plugins/test_mbpseudo.py +++ b/test/plugins/test_mbpseudo.py @@ -1,6 +1,8 @@ +from __future__ import annotations + import json -import pathlib from copy import deepcopy +from typing import TYPE_CHECKING import pytest @@ -9,13 +11,17 @@ from beets.autotag.distance import Distance from beets.autotag.hooks import AlbumInfo, TrackInfo from beets.library import Item from beets.test.helper import PluginMixin -from beetsplug._typing import JSONDict from beetsplug.mbpseudo import ( _STATUS_PSEUDO, MusicBrainzPseudoReleasePlugin, PseudoAlbumInfo, ) +if TYPE_CHECKING: + import pathlib + + from beetsplug._typing import JSONDict + @pytest.fixture(scope="module") def rsrc_dir(pytestconfig: pytest.Config):