Skip to content

Slightly simplify lastgenre client#6495

Open
snejus wants to merge 3 commits intomasterfrom
lastgenre-use-a-single-fetch-method
Open

Slightly simplify lastgenre client#6495
snejus wants to merge 3 commits intomasterfrom
lastgenre-use-a-single-fetch-method

Conversation

@snejus
Copy link
Copy Markdown
Member

@snejus snejus commented Apr 4, 2026

Consolidate Last.fm genre fetching into a single fetch method

This PR simplifies the lastgenre client API by replacing three separate fetch methods (fetch_track_genre, fetch_album_genre, fetch_artist_genre) with a single unified fetch(kind, obj) method.

What changed

client.py:

  • Introduces a class-level FETCH_METHODS registry (ClassVar dict) mapping fetch "kinds" ("track", "album", "artist", "album_artist") to a (pylast_method, arg_extractor) tuple.
  • Replaces the three fetch_* methods with a single fetch(kind, obj) that dispatches via this registry.
  • Removes a private _tags_for wrapper — its logic is inlined into the now-public fetch_genres.
  • Drops the workaround for a pylast.Album.get_top_tags() inconsistency fixed in 2014.

__init__.py:

  • All call sites updated to use client.fetch(kind, obj) — the client now owns field extraction (e.g. obj.artist, obj.album), removing that concern from the plugin layer.

test_lastgenre.py:

  • Test mocking simplified: a single monkeypatch on LastFmClient.fetch replaces three separate method patches.

Impact

  • Reduced surface area: one method to mock, test, and reason about instead of three.
  • Field extraction centralised: callers no longer need to know which fields to pass per entity type.
  • No behaviour change — pure refactor.

snejus added 3 commits April 4, 2026 01:20
Delegate the responsibility of getting relevant model fields to the
client by declaring the genre fetching spec on the class.
This issue has been resolved in 2014.
Copilot AI review requested due to automatic review settings April 4, 2026 00:22
@snejus snejus requested a review from a team as a code owner April 4, 2026 00:22
@github-actions
Copy link
Copy Markdown

github-actions bot commented Apr 4, 2026

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.

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

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 lastgenre Last.fm client API smaller. Instead of 3 fetch funcs, now one fetch(kind, obj) dispatch table, and plugin call sites updated to use it.

Changes:

  • Add LastFmClient.FETCH_METHODS registry and new fetch(kind, obj) dispatcher.
  • Inline old tag parsing into fetch_genres and remove old per-entity fetch methods.
  • Update plugin + tests to use new fetch API and simpler mocking.

Reviewed changes

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

File Description
beetsplug/lastgenre/client.py Add unified fetch dispatch + simplify tag fetch/filter path
beetsplug/lastgenre/init.py Switch genre resolution stages to call client.fetch(kind, obj)
test/plugins/test_lastgenre.py Update tests to new API + monkeypatch LastFmClient.fetch

Comment on lines 394 to 400
for albumartist in obj.albumartists:
self._log.extra_debug(
'Fetching artist genre for "{}"',
albumartist,
)
new_genres += self.client.fetch_artist_genre(
albumartist
)
new_genres += self.client.fetch("album_artist", obj)
if new_genres:
Copy link

Copilot AI Apr 4, 2026

Choose a reason for hiding this comment

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

grug see bug. in multi-valued album artist loop, code log each albumartist but call self.client.fetch("album_artist", obj) each time. fetch arg_fn use obj.albumartist, so loop repeat same lookup and ignore per-artist value. need pass albumartist into client lookup (maybe new kind that takes explicit string, or let fetch accept override args) so each artist actually fetched.

Copilot uses AI. Check for mistakes.
"""Core genre identification routine.
"""Return genres for a pylast entity.

Given a pylast entity (album or track), return a list of
Copy link

Copilot AI Apr 4, 2026

Choose a reason for hiding this comment

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

grug confused docstring. fetch_genres now take Album/Artist/Track, but doc text still say "album or track". update doc so match real types and behavior.

Suggested change
Given a pylast entity (album or track), return a list of
Given a pylast entity (album, artist, or track), return a list of

Copilot uses AI. Check for mistakes.
"track", LASTFM.get_track, trackartist, tracktitle
)
def fetch(self, kind: str, obj: LibModel) -> list[str]:
"""Fetch fetcher for Last.fm genres."""
Copy link

Copilot AI Apr 4, 2026

Choose a reason for hiding this comment

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

grug spot typo in docstring: "Fetch fetcher". should be clear what method do (fetch genres for kind) so reader not trip.

Suggested change
"""Fetch fetcher for Last.fm genres."""
"""Fetch Last.fm genres for the specified kind."""

Copilot uses AI. Check for mistakes.
assert res == ["pop", "rap"]
res = plugin.client._tags_for(MockPylastObj(), min_weight=50)

plugin.client._min_weight = 50
Copy link

Copilot AI Apr 4, 2026

Choose a reason for hiding this comment

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

grug not like test reach into private _min_weight. test will break if client internals change. better make client with min_weight=50 (new plugin instance / re-run setup with config) or expose small public way to set min_weight for test.

Suggested change
plugin.client._min_weight = 50
self.config["lastgenre"]["min_weight"] = 50
plugin = lastgenre.LastGenrePlugin()
plugin.setup()

Copilot uses AI. Check for mistakes.
@codecov
Copy link
Copy Markdown

codecov bot commented Apr 4, 2026

Codecov Report

❌ Patch coverage is 40.00000% with 12 lines in your changes missing coverage. Please review.
✅ Project coverage is 70.06%. Comparing base (131768b) to head (1029750).
✅ All tests successful. No failed tests found.

Files with missing lines Patch % Lines
beetsplug/lastgenre/client.py 46.15% 6 Missing and 1 partial ⚠️
beetsplug/lastgenre/__init__.py 28.57% 4 Missing and 1 partial ⚠️
Additional details and impacted files
@@           Coverage Diff           @@
##           master    #6495   +/-   ##
=======================================
  Coverage   70.06%   70.06%           
=======================================
  Files         147      147           
  Lines       18562    18554    -8     
  Branches     3026     3025    -1     
=======================================
- Hits        13005    13000    -5     
+ Misses       4924     4921    -3     
  Partials      633      633           
Files with missing lines Coverage Δ
beetsplug/lastgenre/__init__.py 75.54% <28.57%> (ø)
beetsplug/lastgenre/client.py 51.28% <46.15%> (-1.91%) ⬇️
🚀 New features to boost your workflow:
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@JOJ0 JOJ0 added the plugin Pull requests that are plugins related label Apr 4, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

plugin Pull requests that are plugins related

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants