typing: type beets core - #6941
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. |
There was a problem hiding this comment.
Pull request overview
grug see PR make type more tight in beets core. many signature now say what they mean (ui command helpers, library model remove flow, events list, util helpers). big change, many place for bug hide; grug like safety, but need fix few real runtime bugs first.
Changes:
- add/adjust type annotations across ui command modules, util helpers, logging, and test helpers
- refactor library model removal to split internal
_remove()from publicremove(...) - derive
ALL_EVENTSfromEventTypeso runtime list stay in sync with typing
Reviewed changes
Copilot reviewed 36 out of 36 changed files in this pull request and generated 3 comments.
Show a summary per file
| File | Description |
|---|---|
| test/test_importer.py | update test helper base + import_session call to pass lib |
| beets/util/units.py | add param/return type hints for time/bytes formatting helpers |
| beets/util/pipeline.py | add typing + tweak doc examples for stage helpers |
| beets/util/m3u.py | type playlist path/contents as bytes + annotate methods |
| beets/util/extension.py | make fix_extension() return bytes consistently |
| beets/util/config.py | type UnknownPairError ctor args |
| beets/ui/commands/write.py | type write command helper signature |
| beets/ui/commands/version.py | type show_version return |
| beets/ui/commands/utils.py | type do_query inputs/outputs |
| beets/ui/commands/update.py | type update helper + make id/album handling explicit |
| beets/ui/commands/stats.py | type stats helper signature |
| beets/ui/commands/remove.py | type remove helper + typed printing via singledispatch |
| beets/ui/commands/move.py | type move helper + add TypeIs helper for album selection |
| beets/ui/commands/modify.py | type modify helper functions and return types |
| beets/ui/commands/list.py | type list helper + adjust usage string handling |
| beets/ui/commands/help.py | type help command ctor + assert root parser type |
| beets/ui/commands/fields.py | type internal print helpers |
| beets/ui/commands/config.py | type config_edit signature |
| beets/ui/commands/completion.py | type completion script generator + minor renames |
| beets/ui/commands/init.py | type default command list + __getattr__ return |
| beets/ui/init.py | add/adjust types across ui core (parsing, prompts, parsers) |
| beets/test/helper.py | add typed attrs + type ctor for TerminalImportSessionFixture |
| beets/test/fixtures.py | type fixture model getters/types + tweak DummyIMBackend version handling |
| beets/test/_common.py | type test helpers, require lib for import_session |
| beets/plugins.py | type plugin ctor + import error ctor |
| beets/metadata_plugins.py | type contextmanager yield type |
| beets/logging.py | type logger/formatter methods and overloads |
| beets/library/models.py | split remove into internal _remove() + public remove contract |
| beets/library/init.py | type __getattr__ return |
| beets/events.py | derive ALL_EVENTS from EventType via typing introspection |
| beets/dbcore/db.py | type add() return + widen formatted included_keys type |
| beets/context.py | add future annotations + type contextmanager yield |
| beets/autotag/match.py | type helper return as None |
| beets/autotag/distance.py | tighten Distance numeric ops typing + reject unsupported operands |
| beets/autotag/init.py | type __getattr__ return |
| beets/init.py | type __getattr__ return |
Suppressed comments (1)
beets/autotag/distance.py:226
- grug see bug: Distance.sub/rsub accept Distance, but then do
self.distance - other/other - self.distance. when other is Distance, float - Distance raise TypeError. need unwrap Distance to float before math.
def __sub__(self, other: object) -> float:
if not isinstance(other, (float, Distance)):
raise TypeError(
"unsupported operand type(s) for -: "
f"'Distance' and {type(other).__name__!r}"
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
Codecov Report❌ Patch coverage is Additional details and impacted files@@ Coverage Diff @@
## typing-type-command-handlers #6941 +/- ##
================================================================
- Coverage 76.29% 76.12% -0.17%
================================================================
Files 164 164
Lines 21529 21556 +27
Branches 3332 3335 +3
================================================================
- Hits 16425 16410 -15
- Misses 4315 4357 +42
Partials 789 789
🚀 New features to boost your workflow:
|
0fdce0b to
aaf058b
Compare
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 36 out of 36 changed files in this pull request and generated 1 comment.
Suppressed comments (3)
test/test_importer.py:1624
- grug see this test class stop inherit unittest.TestCase, but class name still not start with "Test". pytest default no collect this class, so these tests no run. rename class to start with "Test".
beets/util/m3u.py:18 - grug see M3UFile init type say path is bytes, but callers pass pathlib.Path (and normpath accept PathLike). type too strict, make type checker sad. make param PathLike and add TYPE_CHECKING import.
beets/autotag/distance.py:220 - grug see lt/__sub say they accept Distance, but they do self.distance < other / self.distance - other. when other is Distance, this still TypeError. coerce with float(other) so Distance-to-Distance work, and keep NotImplemented for weird types.
def __lt__(self, other: object) -> bool:
if isinstance(other, (int, float, Distance)):
return self.distance < other
return NotImplemented
65cc028 to
60bfc97
Compare
60bfc97 to
6913b74
Compare
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 36 out of 36 changed files in this pull request and generated 1 comment.
Suppressed comments (4)
beets/util/m3u.py:17
- grug see M3UFile init say path: bytes, but tests and caller pass Path. typing now lie. use PathLike so type check not fight real code.
beets/ui/commands/help.py:27 - grug no like assert for user CLI path. python -O remove assert, then code crash weird. do real check and raise UserError.
assert isinstance(self.root_parser, ui.SubcommandsOptionParser)
beets/ui/commands/move.py:88
- grug see move_items type say dest_path always PathLike, but move_func pass None. typing now fight call site. also
if dest_pathtreat empty bytes as None. make dest_path Optional and check is not None.
def move_items(
lib: Library,
dest_path: PathLike,
query: list[str],
copy: bool,
album: bool,
pretend: bool,
confirm: bool = False,
export: bool = False,
) -> None:
"""Moves or copies items to a new base directory, given by dest. If
dest is None, then the library's base directory is used, making the
command "consolidate" files.
"""
dest = os.fsencode(dest_path) if dest_path else None
beets/autotag/distance.py:235
- grug see Distance lt/__sub compare float with Distance object. runtime maybe ok, but type checker cry. also return NotImplemented not match annotation. unwrap Distance to float/int first, and ignore return type for NotImplemented line.
def __lt__(self, other: object) -> bool:
if isinstance(other, (int, float, Distance)):
return self.distance < other
return NotImplemented
def __float__(self) -> float:
return self.distance
def __sub__(self, other: object) -> float:
if isinstance(other, (int, float, Distance)):
return self.distance - other
return NotImplemented
def __rsub__(self, other: object) -> float:
if isinstance(other, (int, float, Distance)):
return other - self.distance
return NotImplemented
| | NoArgsEventType | ||
| | AfterConvertEventType | ||
| ) | ||
| ALL_EVENTS = list(chain.from_iterable(get_args(e) for e in get_args(EventType))) |
74d1233 to
7f8f933
Compare
7f8f933 to
ba078ad
Compare
6913b74 to
3064c95
Compare
ba078ad to
8afa61f
Compare
3064c95 to
812abbc
Compare
8afa61f to
ed37e83
Compare
812abbc to
ce88be5
Compare
ed37e83 to
f083d14
Compare
ce88be5 to
66aa044
Compare
f083d14 to
11bee48
Compare
66aa044 to
d3ca372
Compare
11bee48 to
2191fd8
Compare
d3ca372 to
13e8ec8
Compare
2191fd8 to
0ebd95c
Compare
13e8ec8 to
2d687de
Compare
0ebd95c to
e7b3692
Compare
2d687de to
b62798c
Compare
e7b3692 to
40538b7
Compare
40538b7 to
6dacb0c
Compare
b62798c to
6dbbc76
Compare
Part of #6924.
This PR tightens typing across core surfaces in
beets, especially arounduicommand handling,librarymodels,autotag,util, logging, and test helpers.Architecturally, the change makes several implicit contracts explicit:
uicommand parsers and subcommands now have clearer typed interfaces.libraryremoval flow is split into internal_remove()and publicremove(...), which better separates shared database-change behavior from model-specific delete logic.eventsnow derivesALL_EVENTSfromEventType, so the runtime event list stays aligned with the type definition instead of being maintained separately.fix_extension()and playlist helpers now expose more precise return/value types.High-level impact is mostly safety and maintainability rather than new functionality. The goal is to make core APIs easier to reason about, easier to type-check, and less likely to drift between declared and actual behavior.
There are a few small behavioral hardening changes:
Distancearithmetic/comparison now rejects unsupported operand types instead of silently accepting invalid values.Reviewer takeaway: this is primarily a core typing cleanup with light refactoring, aimed at improving internal API clarity and catching mistakes earlier, with only limited runtime behavior changes in edge cases.