Skip to content

Commit b1e9732

Browse files
committed
lastgenre: Named types; Document flatten_tree
- Define types for whitelist and canonicalization tree - Better document the flatten_tree function
1 parent 14e54a0 commit b1e9732

4 files changed

Lines changed: 60 additions & 9 deletions

File tree

beetsplug/lastgenre/__init__.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,8 @@
4343
from beets.importer import ImportSession, ImportTask
4444
from beets.library import LibModel
4545

46+
from .types import CanonTree
47+
4648

4749
class LastGenrePlugin(plugins.BeetsPlugin):
4850
def __init__(self) -> None:
@@ -117,7 +119,7 @@ def _sort_by_depth(self, tags: list[str]) -> list[str]:
117119
return [p[1] for p in depth_tag_pairs]
118120

119121
@staticmethod
120-
def find_parents(candidate: str, branches: list[list[str]]) -> list[str]:
122+
def find_parents(candidate: str, branches: CanonTree) -> list[str]:
121123
"""Find parent genres of a given genre, ordered from closest to furthest."""
122124
for branch in branches:
123125
try:

beetsplug/lastgenre/client.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,8 @@
3131

3232
from beets.logging import Logger
3333

34+
from .types import GenreCache
35+
3436
LASTFM = pylast.LastFMNetwork(api_key=plugins.LASTFM_KEY)
3537

3638
PYLAST_EXCEPTIONS = (
@@ -51,7 +53,7 @@ def __init__(self, log: Logger, min_weight: int):
5153
self._log = log
5254
self._tunelog = make_tunelog(log)
5355
self._min_weight = min_weight
54-
self._genre_cache: dict[str, list[str]] = {}
56+
self._genre_cache: GenreCache = {}
5557

5658
def fetch_genre(
5759
self, lastfm_obj: pylast.Album | pylast.Artist | pylast.Track

beetsplug/lastgenre/loaders.py

Lines changed: 24 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,8 @@
2828

2929
from beets.logging import Logger
3030

31+
from .types import CanonTree, Whitelist
32+
3133

3234
class DataFileLoader:
3335
"""Loads genre-related data files for the lastgenre plugin."""
@@ -36,8 +38,8 @@ def __init__(
3638
self,
3739
log: Logger,
3840
plugin_dir: Path,
39-
whitelist: set[str],
40-
c14n_branches: list[list[str]],
41+
whitelist: Whitelist,
42+
c14n_branches: CanonTree,
4143
canonicalize: bool,
4244
):
4345
"""Initialize with pre-loaded data.
@@ -83,7 +85,7 @@ def from_config(
8385
@staticmethod
8486
def _load_whitelist(
8587
log: Logger, config_value: str | bool | None, default_path: str
86-
) -> set[str]:
88+
) -> Whitelist:
8789
"""Load the whitelist from a text file.
8890
8991
Returns set of valid genre names (lowercase).
@@ -107,12 +109,12 @@ def _load_tree(
107109
config_value: str | bool | None,
108110
default_path: str,
109111
prefer_specific: bool,
110-
) -> tuple[list[list[str]], bool]:
112+
) -> tuple[CanonTree, bool]:
111113
"""Load the canonicalization tree from a YAML file.
112114
113115
Returns tuple of (branches, canonicalize_enabled).
114116
"""
115-
c14n_branches: list[list[str]] = []
117+
c14n_branches: CanonTree = []
116118
c14n_filename = config_value
117119
canonicalize = c14n_filename is not False
118120
# Default tree
@@ -133,9 +135,24 @@ def _load_tree(
133135
def flatten_tree(
134136
elem: dict[str, Any] | list[Any] | str,
135137
path: list[str],
136-
branches: list[list[str]],
138+
branches: CanonTree,
137139
) -> None:
138-
"""Flatten nested lists/dictionaries into lists of strings (branches)."""
140+
"""Flatten nested YAML structure into genre hierarchy branches.
141+
142+
Recursively converts nested dicts/lists from YAML into a flat list
143+
of genre paths, where each path goes from general to specific genre.
144+
145+
Args:
146+
elem: The YAML element to process (dict, list, or string leaf).
147+
path: Current path from root to this element (used in recursion).
148+
branches: OUTPUT PARAMETER - Empty list that will be populated
149+
with genre paths. Gets mutated by this method.
150+
151+
Example:
152+
branches = []
153+
flatten_tree({'rock': ['indie', 'punk']}, [], branches)
154+
# branches is now: [['rock', 'indie'], ['rock', 'punk']]
155+
"""
139156
if not path:
140157
path = []
141158

beetsplug/lastgenre/types.py

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
# This file is part of beets.
2+
# Copyright 2026, J0J0 Todos.
3+
#
4+
# Permission is hereby granted, free of charge, to any person obtaining
5+
# a copy of this software and associated documentation files (the
6+
# "Software"), to deal in the Software without restriction, including
7+
# without limitation the rights to use, copy, modify, merge, publish,
8+
# distribute, sublicense, and/or sell copies of the Software, and to
9+
# permit persons to whom the Software is furnished to do so, subject to
10+
# the following conditions:
11+
#
12+
# The above copyright notice and this permission notice shall be
13+
# included in all copies or substantial portions of the Software.
14+
15+
16+
"""Type aliases for the lastgenre plugin."""
17+
18+
from __future__ import annotations
19+
20+
Whitelist = set[str]
21+
"""Set of valid genre names (lowercase). Empty set means all genres allowed."""
22+
23+
CanonTree = list[list[str]]
24+
"""Genre hierarchy as list of paths from general to specific.
25+
Example: [['electronic', 'house'], ['electronic', 'techno']]"""
26+
27+
GenreCache = dict[str, list[str]]
28+
"""Cache mapping entity keys to their genre lists.
29+
Keys are formatted as 'entity.arg1-arg2-...' (e.g., 'album.artist-title').
30+
Values are lists of lowercase genre strings."""

0 commit comments

Comments
 (0)