Skip to content

typing: type utils and modules that are mostly missing annotations - #6925

Open
snejus wants to merge 11 commits into
type-handlers-and-listeners-explicitlyfrom
type-embedart-art-utils-import-command
Open

typing: type utils and modules that are mostly missing annotations#6925
snejus wants to merge 11 commits into
type-handlers-and-listeners-explicitlyfrom
type-embedart-art-utils-import-command

Conversation

@snejus

@snejus snejus commented Aug 10, 2026

Copy link
Copy Markdown
Member

Fixes: #6923

What changed

  • This PR is mostly a typing and module-boundary cleanup.
  • Code now prefers public package exports like beets.library and beets.dbcore instead of reaching into deeper internal modules.
  • Shared model typing was renamed from AnyLibModel to AlbumOrItem, which makes intent clearer where code handles either an Album or an Item.
  • Several typing fixes were added around import, embedart, _utils.art, and beets.util.functemplate.

Architecture impact

  • The main architectural shift is toward using stable, package-level APIs such as beets.library and beets.dbcore as the import boundary.
  • dbcore.Results now behaves like a Sequence, which lets callers depend on a simpler, more general interface instead of a concrete internal result type.
  • beets.util.functemplate got a deeper type pass and some small internal cleanup, but its role in the system stays the same.

High-level impact

  • Improves type safety and IDE support across importer, library, plugin, and template code.
  • Reduces coupling to internal module layout, which should make future refactors safer.
  • Makes a few core interfaces easier to understand and reuse, especially around library model collections and import-session callbacks.
  • Overall, this looks like low-risk maintenance work with small correctness improvements and no intended feature change.

Copilot AI lite review requested due to automatic review settings August 10, 2026 23:06
@snejus
snejus requested review from a team and semohr as code owners August 10, 2026 23:06
@github-actions

Copy link
Copy Markdown

Thank you for the PR! The changelog has not been updated, so here is a friendly reminder to check if you need to add an entry.

@codecov

codecov Bot commented Aug 10, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 87.75510% with 18 lines in your changes missing coverage. Please review.
✅ Project coverage is 75.48%. Comparing base (efca575) to head (59ad7e4).
✅ All tests successful. No failed tests found.

Files with missing lines Patch % Lines
beets/ui/commands/import_/session.py 76.19% 3 Missing and 2 partials ⚠️
beets/util/functemplate.py 92.18% 2 Missing and 3 partials ⚠️
beets/ui/commands/import_/__init__.py 70.00% 3 Missing ⚠️
beetsplug/beatport.py 0.00% 2 Missing ⚠️
beets/autotag/distance.py 50.00% 1 Missing ⚠️
beetsplug/_utils/art.py 90.00% 1 Missing ⚠️
beetsplug/embedart.py 92.85% 0 Missing and 1 partial ⚠️
Additional details and impacted files
@@                            Coverage Diff                             @@
##           type-handlers-and-listeners-explicitly    #6925      +/-   ##
==========================================================================
+ Coverage                                   75.43%   75.48%   +0.04%     
==========================================================================
  Files                                         165      165              
  Lines                                       21564    21575      +11     
  Branches                                     3378     3377       -1     
==========================================================================
+ Hits                                        16266    16285      +19     
+ Misses                                       4503     4495       -8     
  Partials                                      795      795              
Files with missing lines Coverage Δ
beets/autotag/match.py 88.09% <100.00%> (ø)
beets/dbcore/db.py 94.45% <100.00%> (+0.01%) ⬆️
beets/importer/session.py 93.00% <ø> (ø)
beets/library/__init__.py 100.00% <100.00%> (ø)
beets/library/models.py 87.20% <100.00%> (ø)
beets/ui/commands/import_/display.py 81.59% <ø> (ø)
beets/util/__init__.py 81.68% <100.00%> (ø)
beets/util/diff.py 100.00% <ø> (ø)
beetsplug/_utils/playcount.py 95.91% <100.00%> (+0.08%) ⬆️
beetsplug/aura.py 57.61% <100.00%> (+0.12%) ⬆️
... and 11 more
🚀 New features to boost your workflow:
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

PR make typing more strong and make module boundary more clean. Big goal: use public beets.library / beets.dbcore exports, and make dbcore.Results act like normal Sequence so callers can type simple.

Changes:

  • Switch many imports to package-level exports (beets.library, beets.dbcore) and rename shared model type to AlbumOrItem.
  • Make dbcore.Results implement Sequence with negative index + slicing, and add tests for that behavior.
  • Add/adjust type annotations in importer UI flow, embedart/art utils, and functemplate parser/compiler.

Reviewed changes

Copilot reviewed 28 out of 28 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
test/plugins/test_tidal.py Use public beets.library.Item import
test/plugins/test_ftintitle.py Use public beets.library.Album import
test/library/test_migrations.py Consolidate Album/Item/migrations import from beets.library
test/dbcore/test_query.py Import common queries from beets.dbcore export surface
test/dbcore/test_db.py Add tests for negative indexing + slicing on results
beetsplug/tidal/init.py Type results args as Sequence[...] instead of internal Results
beetsplug/spotify.py Simplify typing around item collections and config vars
beetsplug/smartplaylist.py Prefer exported beets.dbcore.Query
beetsplug/replaygain.py Minor typing cleanup
beetsplug/embedart.py Add annotations and refactor extract logic; bytes-path handling for temp image
beetsplug/beatport.py Minor typing cleanup
beetsplug/aura.py Prefer exported AndQuery/MatchQuery
beetsplug/_utils/playcount.py Prefer exported query types
beetsplug/_utils/art.py Add type annotations and align image path types with artresizer backend
beets/util/functemplate.py Deeper typing pass + parser internals cleanup
beets/util/diff.py Use public beets.library.LibModel export
beets/util/init.py Tighten PromptChoice callback typing
beets/ui/commands/import_/session.py Add typing + adjust interactive choice flow
beets/ui/commands/import_/display.py Use public beets.library.Item import
beets/ui/commands/import_/init.py Add annotations + small variable rename for clarity
beets/test/helper.py Update test fixture typing for singleton import flow
beets/test/fixtures.py Prefer exported beets.dbcore.Index
beets/library/models.py Rename AnyLibModel to AlbumOrItem
beets/library/init.py Re-export AlbumOrItem instead of AnyLibModel
beets/importer/session.py Update types for query and duplicate action param
beets/dbcore/db.py Make Results implement Sequence and add slice/negative indexing support
beets/autotag/match.py Minor typing cleanup
beets/autotag/distance.py Minor typing cleanup
Suppressed comments (6)

beets/ui/commands/import_/session.py:165

  • grug see same problem in choose_item: PromptChoice callback can return Proposal (manual search/id) but code ignore it, so user cannot update candidates for singleton import. handle Proposal and continue loop.
            # Plugin-provided choices. We invoke the associated callback
            # function.
            if isinstance(choice, PromptChoice) and choice.callback:
                post_choice = choice.callback(self, task)
                if isinstance(post_choice, importer.Action):
                    return post_choice

beets/ui/commands/import_/session.py:110

  • grug see PromptChoice callback (manual_search/manual_id) return Proposal with new candidates+rec. choose_match now ignore Proposal, so manual search/id no work and user stuck loop. handle Proposal and update task.candidates/task.rec then continue.
            # Plugin-provided choices. We invoke the associated callback
            # function.
            if isinstance(choice, PromptChoice) and choice.callback:
                post_choice = choice.callback(self, task)
                if isinstance(post_choice, importer.Action):
                    return post_choice

beets/util/init.py:173

  • grug see PromptChoice callbacks return Proposal (manual_search/manual_id). callback type here too narrow (Action|None), so typing lie and mypy sad. widen to include Proposal.
class PromptChoice(NamedTuple):
    short: str
    long: str
    callback: Callable[[ImportSession, ImportTask], Action | None] | None

beets/util/functemplate.py:547

  • grug see Template.interpret use default {} for values/functions. same mutable default trap. use None and make new dict inside.
    def interpret(
        self,
        values: Mapping[str, str] = {},
        functions: Mapping[str, Callable[[str], str]] = {},
    ) -> str:

beets/util/functemplate.py:559

  • grug see Template.substitute use default {} for values/functions. same mutable default trap. use None and make new dict inside before calling compiled func.
    def substitute(
        self,
        values: Mapping[str, str] = {},
        functions: Mapping[str, Callable[[str], str]] = {},
    ) -> str:

beets/util/functemplate.py:585

  • grug see wrapper_func use default {} too. mutable default trap again. use None and init inside.
        def wrapper_func(
            values: Mapping[str, str] = {},
            functions: Mapping[str, Callable[[str], str]] = {},
        ) -> str:

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment on lines 8 to 12
from beets.autotag import (
AlbumMatch,
Proposal,
Recommendation,
TrackMatch,
tag_album,
Comment thread beets/util/__init__.py
Comment on lines 46 to 51
if TYPE_CHECKING:
from collections.abc import Callable, Iterable, Iterator
from logging import Logger

from beets.importer import Action, ImportSession, ImportTask
from beets.library import Item
Comment on lines +45 to 51
def __init__(
self,
values: Mapping[str, str] = {},
functions: Mapping[str, Callable[[str], str]] = {},
) -> None:
self.values = values
self.functions = functions
@snejus
snejus force-pushed the type-embedart-art-utils-import-command branch from 122fd1f to 59ad7e4 Compare August 10, 2026 23:38
@snejus
snejus force-pushed the type-embedart-art-utils-import-command branch from 59ad7e4 to c989119 Compare August 11, 2026 21:31
@snejus
snejus force-pushed the type-handlers-and-listeners-explicitly branch from efca575 to 7ea17e2 Compare August 11, 2026 21:31
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add typing to sparsely annotated core and plugin modules

2 participants