Fixes: #6121
This PR introduces a centralized deprecation system and adjusts
`musicbrainz` plugin loading to properly handle the deprecated
`musicbrainz.enabled` configuration option.
#### MusicBrainz
- Added deprecation warnings for the `musicbrainz.enabled` configuration
option:
- When set to `true`, warns users to explicitly add `musicbrainz` to
their `plugins` configuration and adds it if not already present
- When set to `false`, warns users and adds the plugin to
`disabled_plugins` (list
received by the `--disable-plugins` flag)
#### Deprecations
- Created new `beets/util/deprecation.py` module with standardized
deprecation helpers:
- `deprecate_for_user()` - logs warnings visible to end users
- `deprecate_for_maintainers()` - emits `DeprecationWarning` for
developers
- `deprecate_imports()` - handles deprecated module imports with
automatic version calculation
- `_format_message()` - generates consistent deprecation messages that
auto-calculate next major version
- Migrated all deprecation handling to use the new centralized
functions:
- Replaced inline `warnings.warn()` calls throughout codebase
- Updated `deprecate_imports()` signature to remove explicit `version`
parameter
- Converted user-facing deprecation warnings in plugins to use
logger-based `deprecate_for_user()`
Sometimes it is time to let go of old things:
This PR removes the old gmusic plugin and all related docs.
---
The google play music service was shutdown in 2020 and already
deprecated in beets 1.6.0.
## Description
Fixes [#6115](https://github.com/beetbox/beets/issues/6115).
When an inline field definition shadows a built-in database field (e.g.,
redefining `track_no` in `item_fields`), the inline plugin evaluates the
field template by constructing a dictionary of all item values.
Previously, this triggered unbounded recursion because `_dict_for(obj)`
re-entered `__getitem__` for the same key while evaluating the computed
field.
This PR adds a per-object, per-key evaluation guard to prevent re-entry
when the same inline field is accessed during expression evaluation.
This resolves the recursion error while preserving normal computed-field
behavior.
A regression test
(`TestInlineRecursion.test_no_recursion_when_inline_shadows_fixed_field`)
verifies that `$track_no` evaluates correctly (`'01'`) when shadowed.
## To Do
- [x] ~Documentation.~
- [x] ~Changelog.~
- [x] Tests.
This plugin aims to address the shortcomings of the %title function, as
brought up in issues #152, #3298 and an initial look to improvement with
#3411. It supplies a new string format command, `%titlecase` which
doesn't interfere with any prior expected behavior of the `%title`
format command.
It also adds the ability to apply titlecase logic to metadata fields
that a user selects, which is useful if you, like me, are looking for
stylistic consistency and the minor stylistic differences between
Musizbrainz, Discogs, Deezer etc, with title case are slightly
infuriating.
This will add an optional dependency of
[titlecase](https://pypi.org/project/titlecase/), which allows the
titlecase core logic to be externally maintained.
If there's not enough draw to have this as a core plugin, I can also
spin this into an independent one, but it seemed like a recurring theme
that the %title string format didn't really behave as expected, and I
wanted my metadata to match too.
- [x] Documentation. (If you've added a new command-line flag, for
example, find the appropriate page under `docs/` to describe it.)
- [x] Changelog. (Add an entry to `docs/changelog.rst` to the bottom of
one of the lists near the top of the document.)
- [x] Tests. - Not 100% coverage, but didn't see a lot of other plugins
with testing for import stages.
## Description
I was hoping to use the functionality from `ftintitle` to set the path's
album artist as the main artist, but that wasn't possible, so I added a
template value `album_artist_no_feat`.
Catch ValueError when setting gst required version
`pytest.importorskip` is used to catch the case when beetsplug.bpd cannot
be imported. On macOS, the `gi` module was able to be imported, but when
trying to specify `gi.require_version`, a `ValueError` is raised about
Gst being unavailable. pytest does not catch this `ValueError` during
`importskip` as it is not an `ImportError`, and thus the test suite
errors during the test collection phase.
With this change, we catch the ValueError, and re-raise it as an
`ImportError` and pytest gracefully skips those tests.
Fixes#3324
pytest.importskip is used to catch the case when beetsplug.bpd cannot be
imported. On macOS, the gi module was able to be imported, but when
trying to specify `gi.require_version`, a ValueError is raised about
Gst being unavailable. pytest does not catch this ValueError during
importskip as it is not an ImportError, and thus the test suite errors
during the test collection phase.
With this change, we catch the ValueError, and re-raise it as an
ImportError and pytest gracefully skips those tests.
Following #4709 and #5447, the web plugin used single-quotes (ie. string
litteral) in the SQL query for table columns.
Thus, for instance, the query `GET /item/values/albumartist` would
return the litteral "albumartist" instead of a list of unique album
artists.
This prevents the Mopidy beets integration from working, returning the
single artist "albumartist".
Following #4709 and #5447, the web plugin used single-quotes (ie. string
litteral) in the SQL query for table columns. Thus, for instance, the query
`GET /item/values/albumartist` would return the litteral "albumartist"
instead of a list of unique album artists.
When creating a hardlink, either during import or `beet convert`, if the
origin of the hardlink was a symlink, that symlink used to be directly
copied. This could create broken symlinks if the origin symlink was
relative, and in either case, probably wasn't the user's desired
behavior.
This change de-references all symlinks before creating a hardlink, such
that the end result is a normal file with the same inode as the original
file. See #5676 for more discussion about the original issue.
Fixes#5676