Skip to content

Refactor musicbrainz to reduce cognitive complexity#6530

Open
snejus wants to merge 33 commits intomasterfrom
refactor-musicbrainz
Open

Refactor musicbrainz to reduce cognitive complexity#6530
snejus wants to merge 33 commits intomasterfrom
refactor-musicbrainz

Conversation

@snejus
Copy link
Copy Markdown
Member

@snejus snejus commented Apr 12, 2026

MusicBrainz Plugin Refactor

Summary

A broad internal refactor of beetsplug/musicbrainz.py and its test suite. No user-facing behaviour changes are intended. The goal is to make parsing logic easier to understand, test, and maintain.

This PR halves the cognitive complexity in musicbrainz plugin:

Before

$ complexipy beetsplug/musicbrainz.py
🧠 Total Cognitive Complexity: 181
1 file analyzed in 0.0116 seconds

After

$ complexipy beetsplug/musicbrainz.py
🧠 Total Cognitive Complexity: 93
1 file analyzed in 0.0097 seconds

Production Code Changes

Parsing Decomposition

The monolithic album_info method is broken into focused @staticmethod helpers, each returning a typed TypedDict:

Method Responsibility
_parse_artist_credits Replaces _flatten_artist_credit / _multi_artist_credit
_parse_release_group albumtype, original_year, etc.
_parse_label_infos label, catalognum
_parse_genres Genre list
_parse_external_ids Third-party IDs from URL relations
_parse_work_relations Composer / lyricist credits
_parse_artist_relations Arranger / remixer credits
get_tracks_from_medium Per-medium track iteration

Each helper returns a typed TypedDict, making the data contract explicit and the code easier to compose with **kwargs unpacking into AlbumInfo / TrackInfo.

Other Simplifications

  • _set_date_str (mutable, side-effecting) is replaced by _get_date, a pure function returning (year, month, day).
  • _preferred_release_event and _preferred_alias are simplified.
  • Three @cached_property entries (ignored_media, ignore_data_tracks, ignore_video_tracks) are moved up to sit with other class-level properties.

Test Infrastructure Changes

Factory Layer (test/plugins/factories/musicbrainz.py)

factory-boy / pytest-factoryboy are added as test dependencies. A new factory module introduces composable, deterministic factories:

  • AliasFactory, ArtistFactory, ArtistCreditFactory
  • RecordingFactory, TrackFactory, MediumFactory
  • ReleaseGroupFactory, ReleaseFactory

Factories use a shared _IdFactory base class that generates stable, predictable UUIDs (00000000-0000-0000-0000-000000001001, etc.) based on an id_base + index pair. This makes assertions on IDs readable and deterministic without hard-coded magic strings.

Before:

recording = {
    "title": "foo",
    "id": "bar",
    "length": 42,
    "artist_credit": [{"artist": {"name": "some-artist", "id": "some-id", ...}, ...}],
    ...
}

After:

recording = recording_factory(title="foo", length=42)

Test Consolidation

Many small single-field assertion tests that each constructed an identical release are collapsed into a single test_parse_release snapshot assertion, covering all AlbumInfo fields at once. This reduces redundant fixture setup and makes it immediately obvious what the full output of album_info looks like for a default release.

The hand-rolled _make_release / _make_recording helpers are removed entirely, replaced by composable factory calls using factory-boy's __ double-underscore traversal syntax:

# Before
release = self._make_release(date="1987-03-31")

# After
release = release_factory(release_group__first_release_date="1987-03-31")

@snejus snejus requested a review from a team as a code owner April 12, 2026 15:29
@github-actions github-actions bot added the musicbrainz musicbrainz plugin label Apr 12, 2026
@github-actions
Copy link
Copy Markdown

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.

Base automatically changed from type-musicbrainz to master April 12, 2026 15:34
@snejus snejus force-pushed the refactor-musicbrainz branch from 7c0b1e3 to 804c835 Compare April 12, 2026 15:36
Copilot AI review requested due to automatic review settings April 12, 2026 15:36
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

grug see PR try break big MusicBrainz parse monster into small helpers, plus new test factories, to make brain hurt less. Goal say “no user-facing change”, but grug spot few behavior leaks and test factory sharp edge.

Changes:

  • Split MusicBrainzPlugin.album_info parsing into smaller helpers and add TypedDict contracts.
  • Add factory-boy based factories for MusicBrainz payloads and refactor tests to use them (plus one snapshot-style assertion).
  • Add pytest-factoryboy dependency (and lockfile updates).

Reviewed changes

Copilot reviewed 5 out of 6 changed files in this pull request and generated 8 comments.

Show a summary per file
File Description
beetsplug/musicbrainz.py Refactor parsing into helpers; new date/alias logic; new medium/track iteration helper.
test/plugins/test_musicbrainz.py Switch tests to new factories and consolidate many assertions into one snapshot-style test.
test/plugins/factories/musicbrainz.py New deterministic-ish payload factories for MusicBrainz structures.
test/plugins/factories/init.py New package marker for factories.
pyproject.toml Add pytest-factoryboy dependency.
poetry.lock Lock dependency graph for new test deps.

@snejus snejus force-pushed the refactor-musicbrainz branch from 804c835 to c2528ca Compare April 12, 2026 16:05
@snejus snejus force-pushed the refactor-musicbrainz branch from c2528ca to 2785606 Compare April 13, 2026 09:23
@codecov
Copy link
Copy Markdown

codecov bot commented Apr 13, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 70.99%. Comparing base (ff9f93e) to head (0a40300).
✅ All tests successful. No failed tests found.

Additional details and impacted files
@@           Coverage Diff           @@
##           master    #6530   +/-   ##
=======================================
  Coverage   70.99%   70.99%           
=======================================
  Files         150      150           
  Lines       19124    19124           
  Branches     3078     3078           
=======================================
  Hits        13577    13577           
  Misses       4896     4896           
  Partials      651      651           
Files with missing lines Coverage Δ
beetsplug/_utils/musicbrainz.py 97.29% <100.00%> (ø)
🚀 New features to boost your workflow:
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@aereaux
Copy link
Copy Markdown
Contributor

aereaux commented Apr 13, 2026

I haven't had a chance to look too closely at the code but at least on my library this doesn't cause any unexpected tagging changes!

@snejus snejus mentioned this pull request Apr 13, 2026
5 tasks
@snejus snejus force-pushed the refactor-musicbrainz branch from 2785606 to 0a40300 Compare April 13, 2026 18:43
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

musicbrainz musicbrainz plugin

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants