typing: type utils and modules that are mostly missing annotations - #6925
typing: type utils and modules that are mostly missing annotations#6925snejus wants to merge 11 commits into
Conversation
|
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 Report❌ Patch coverage is 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
🚀 New features to boost your workflow:
|
There was a problem hiding this comment.
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 toAlbumOrItem. - Make
dbcore.ResultsimplementSequencewith 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.
| from beets.autotag import ( | ||
| AlbumMatch, | ||
| Proposal, | ||
| Recommendation, | ||
| TrackMatch, | ||
| tag_album, |
| 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 |
| def __init__( | ||
| self, | ||
| values: Mapping[str, str] = {}, | ||
| functions: Mapping[str, Callable[[str], str]] = {}, | ||
| ) -> None: | ||
| self.values = values | ||
| self.functions = functions |
122fd1f to
59ad7e4
Compare
Now we are able to type Results simply as a Sequence. This way, we can also now use a more generic representation `Sequence[LibModel]` to refer to *any* of the two models.
59ad7e4 to
c989119
Compare
efca575 to
7ea17e2
Compare
Fixes: #6923
What changed
beets.libraryandbeets.dbcoreinstead of reaching into deeper internal modules.AnyLibModeltoAlbumOrItem, which makes intent clearer where code handles either anAlbumor anItem.import,embedart,_utils.art, andbeets.util.functemplate.Architecture impact
beets.libraryandbeets.dbcoreas the import boundary.dbcore.Resultsnow behaves like aSequence, which lets callers depend on a simpler, more general interface instead of a concrete internal result type.beets.util.functemplategot a deeper type pass and some small internal cleanup, but its role in the system stays the same.High-level impact