typing: type the rest of plugins - #6940
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. |
❌ 1 Tests Failed:
View the top 1 failed test(s) by shortest run time
To view more test analytics, go to the Test Analytics Dashboard |
There was a problem hiding this comment.
Pull request overview
grug see PR want finish typing for rest of beets plugins + turn on ruff flake8-annotations (ANN). goal good: make plugin boundary less mystery meat, catch bugs earlier.
Changes:
- turn on
ruffANN rules repo-wide, with per-file ignores and noise carve-outs - add type hints across many bundled plugins + helpers + scripts
- small runtime fixes found by typing (bytes/str, optionals, shapes)
Reviewed changes
Copilot reviewed 65 out of 65 changed files in this pull request and generated 5 comments.
Show a summary per file
| File | Description |
|---|---|
| test/plugins/test_listenbrainz.py | update MusicBrainz request expectation |
| pyproject.toml | enable ruff ANN + ignores/config |
| extra/release.py | add return types to click cmds |
| docs/conf.py | add types to Sphinx hooks |
| beetsplug/zero.py | add plugin API types + JSON typing |
| beetsplug/unimported.py | type plugin init + commands |
| beetsplug/types.py | type plugin type map building |
| beetsplug/titlecase.py | type callback args |
| beetsplug/the.py | type patterns + template funcs |
| beetsplug/substitute.py | type template substitute func |
| beetsplug/subsonicupdate.py | type token/url helpers |
| beetsplug/subsonicplaylist.py | type playlist parsing + requests |
| beetsplug/smartplaylist.py | type JSON playlist config parsing |
| beetsplug/scrub.py | type scrub entrypoints + mutagen classes |
| beetsplug/rewrite.py | add annotations + safer names |
| beetsplug/replaygain.py | add many annotations + small refactors |
| beetsplug/replace.py | type CLI args + return values |
| beetsplug/random.py | type random command plumbing |
| beetsplug/plexupdate.py | type plex request helpers |
| beetsplug/playlist.py | type playlist iter/update APIs |
| beetsplug/play.py | type play helpers + plugin hooks |
| beetsplug/permissions.py | type perms helpers + collections |
| beetsplug/parentwork.py | type import hooks + JSON results |
| beetsplug/mpdupdate.py | type socket wrapper + MPD update |
| beetsplug/missing.py | type missing-track helpers |
| beetsplug/metasync/itunes.py | type iTunes source + helpers |
| beetsplug/metasync/amarok.py | type amarok source + import |
| beetsplug/metasync/init.py | type metasync base + loader |
| beetsplug/mbsync.py | type sync command helpers |
| beetsplug/mbpseudo.py | type pseudo info + deepcopy |
| beetsplug/mbcollection.py | type commands return |
| beetsplug/lyrics.py | type backends + JSON helpers |
| beetsplug/limit.py | type query plugin return |
| beetsplug/lastimport.py | type pylast wrappers + parsing |
| beetsplug/lastgenre/client.py | add missing return annotation |
| beetsplug/kodiupdate.py | type kodi request helper |
| beetsplug/keyfinder.py | type import hooks + find_key |
| beetsplug/ipfs.py | type ipfs helpers + bytes/str fixes |
| beetsplug/inline.py | type inline compiler + env dict |
| beetsplug/info.py | type data emitters + printing |
| beetsplug/importsource.py | type import stage hook |
| beetsplug/importfeeds.py | type m3u helpers + recording |
| beetsplug/importadded.py | type mtimes + fix int cast |
| beetsplug/ihate.py | type classmethod hook |
| beetsplug/hook.py | type event choice + signatures |
| beetsplug/fuzzy.py | type query mapping return |
| beetsplug/fromfilename.py | type filename matcher helpers |
| beetsplug/freedesktop.py | type commands return |
| beetsplug/fish.py | type fish completion generators |
| beetsplug/filefilter.py | type file filter predicate |
| beetsplug/fetchart.py | type returns for sources/config |
| beetsplug/embyupdate.py | type emby API helpers |
| beetsplug/edit.py | type edit YAML + importer hooks |
| beetsplug/duplicates.py | type duplicate processing pipeline |
| beetsplug/convert.py | type parallel convert helper |
| beetsplug/bucket.py | type bucket span parsing/format |
| beetsplug/bpsync.py | type beatport sync plugin |
| beetsplug/bpm.py | type bpm helpers + plugin |
| beetsplug/bench.py | type benchmark entrypoints |
| beetsplug/bareasc.py | type query + usage set call |
| beetsplug/badfiles.py | type badfiles checker plumbing |
| beetsplug/albumtypes.py | type template field return |
| beetsplug/advancedrewrite.py | type rewrite factory + ignore |
| beetsplug/absubmit.py | type AB submit plugin + logging |
| beetsplug/_utils/vfs.py | type helper return |
Suppressed comments (3)
beetsplug/badfiles.py:44
- grug think cmd is str here, but code treat cmd like list. cmd[0] become first char, cmd[-1] become last char. error report wrong. make cmd be list[str].
This issue also appears in the following locations of the same file:
- line 65
- line 82
def __init__(self, cmd: str, oserror: OSError) -> None:
self.checker = cmd[0]
self.path = cmd[-1]
self.errno = oserror.errno
self.msg = str(oserror)
beetsplug/badfiles.py:69
- grug see run_command take str, then list2cmdline(cmd) and check_output(cmd) both wrong. check_output with str (no shell) try run whole string as program, so command with spaces fail. take list[str] and pass list to subprocess.
def run_command(self, cmd: str) -> tuple[int, int, list[str]]:
self._log.debug(
"running command: {}", displayable_path(list2cmdline(cmd))
)
try:
beetsplug/badfiles.py:90
- grug see command builder now glue path into one string. if file path have space, shlex split break and checker fail. pass argv list to run_command instead.
def check_mp3val(self, path: str) -> tuple[int, int, list[str]]:
status, errors, output = self.run_command(f"mp3val {path}")
if status == 0:
output = [line for line in output if line.startswith("WARNING:")]
errors = len(output)
return status, errors, output
def check_flac(self, path: str) -> tuple[int, int, list[str]]:
return self.run_command(f"flac -wst {path}")
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
cc060d4 to
c7dbd0d
Compare
f8ca952 to
9bbf769
Compare
c7dbd0d to
60d7763
Compare
9bbf769 to
96ad42b
Compare
60d7763 to
050c93e
Compare
96ad42b to
344300e
Compare
050c93e to
d4aced5
Compare
344300e to
07d4565
Compare
d4aced5 to
71eea34
Compare
07d4565 to
34db120
Compare
71eea34 to
85affd9
Compare
90f77c1 to
97d5634
Compare
85affd9 to
da994dc
Compare
97d5634 to
eaf3d58
Compare
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 68 out of 68 changed files in this pull request and generated 7 comments.
Suppressed comments (4)
beetsplug/the.py:1
patternsis now only an annotation with no class or instance default, but__init__readsself.patterns. This will raiseAttributeErrorunless something else sets it before__init__. Restore a concrete default (e.g., keep aClassVar[list[str]] = []) or initializeself.patternsin__init__.
beetsplug/the.py:1patternsis now only an annotation with no class or instance default, but__init__readsself.patterns. This will raiseAttributeErrorunless something else sets it before__init__. Restore a concrete default (e.g., keep aClassVar[list[str]] = []) or initializeself.patternsin__init__.
test/plugins/test_play.py:1osis used here but is not imported in the shown updated import section. Add animport osat the top of this test module to avoid aNameError.
beets/util/init.py:414- This change no longer handles general
os.PathLikeobjects (e.g.,pathlib.PurePath) as a single path; they will fall through to the iterable branch and be joined by components, producing incorrect output. It also removes the previous fallback for non-iterable, non-path objects (previouslystr(path)), which can now raiseTypeErrorwhen such objects are passed. Consider decoding viaos.fsdecode(os.fspath(path))in atryblock and only falling back to iterating whenos.fspathfails.
if isinstance(path, (Path, str, bytes)):
return os.fsdecode(path)
return separator.join(displayable_path(p) for p in path)
eaf3d58 to
fd71b30
Compare
fd71b30 to
a73f582
Compare
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 69 out of 69 changed files in this pull request and generated no new comments.
Suppressed comments (6)
beets/dbcore/query.py:54
- grug see query can be bytes. bytes is Sequence but not str. then code do " ".join(query) and crash (join want str, bytes give int). grug say exclude bytes from this branch.
if isinstance(query, Sequence) and not isinstance(query, str):
query = " ".join(query)
beets/util/init.py:961
- grug see targets allow Path, but execlp get Path object and may TypeError. also bytes targets + str args mix not good for windows. grug say fsdecode all targets to str before os.execlp.
first, *rest = args
# 'first' is duplicated because of argv[0]
os.execlp(*[first, first, *rest, *targets])
beetsplug/missing.py:49
- grug see return type say bool | int, but function always make int (subtract ints). wrong type make type checker sad.
test/plugins/test_badfiles.py:52 - grug see test name say play, but this file test badfiles. name confuse future grug.
beetsplug/play.py:65 - grug see keep_open path args is bytes but shlex.split give str. list have mix str+bytes. windows subprocess no like, go boom. grug say convert open_args to str (util.syspath / os.fsdecode) before subprocess.call, and update tests.
test/plugins/test_play.py:212 - grug see this test assert subprocess called with ["echo", ]. that bake in str+bytes mix. if code fix for windows (all str), this test need update to expect str path.
Fixes #6924.
Enables repo-wide annotation checks in
pyproject.tomlviaruff'sflake8-annotationsrules, with practical carve-outs fortest/**,docs/conf.py,Any, and noisy variadic args. This adds a clear enforcement layer without forcing annotations in low-value areas.Finishes typing the entire codebase, including command entrypoints, import hooks, config-driven code, helper utilities, and library/model-facing APIs. Architecturally, plugin boundaries now describe their inputs and outputs explicitly instead of relying on implicit contracts.
Includes a small set of follow-on fixes uncovered by typing, mainly around bytes vs. strings, optional values, and collection shapes. These are mostly correctness and consistency fixes rather than intentional behavior changes.
Overall impact: this completes typing coverage for the full codebase. The result is stronger static guarantees, clearer extension points, and safer future refactors across both core and plugin code.