A bit niche but I tried setting my bareasc prefix to an empty string,
and was getting an obtuse error. This should help make clearer what is
happening when queries fail.
The exception is not properly raised up the stack in the first place
because it happens across 2 FFI boundaries: the DB query
(Python -> SQLite), and the custom DB function (SQLite -> Python).
Thus Python cannot forwarded it back to itself through SQLite, and it's
treated as an "unraisable" exception.
We could override `sys.unraisablehook` to not print anything for the
original exception, and store it in a global for the outer Python
interpreter to fetch and raise properly, but that's pretty hacky,
limited to a single DB instance and query at once, and risks swallowing
other "unraisable" exceptions.
Instead we just tell the user to look above for what Python prints.
Sample output:
```
Exception ignored in: <function unidecode_expect_ascii at
0x7f7fa20bb060>
Traceback (most recent call last):
File "site-packages/unidecode/__init__.py", line 60, in
unidecode_expect_ascii
bytestring = string.encode('ASCII')
^^^^^^^^^^^^^
AttributeError: 'bytes' object has no attribute 'encode'
Traceback (most recent call last):
File "site-packages/beets/dbcore/db.py", line 988, in query
cursor = self.db._connection().execute(statement, subvals)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
sqlite3.OperationalError: user-defined function raised exception
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
File "site-packages/beets/__main__.py", line 9, in <module>
sys.exit(main())
^^^^^^
File "site-packages/beets/ui/__init__.py", line 1865, in main
_raw_main(args)
File "site-packages/beets/ui/__init__.py", line 1852, in _raw_main
subcommand.func(lib, suboptions, subargs)
File "site-packages/beets/ui/commands.py", line 1599, in list_func
list_items(lib, decargs(args), opts.album)
File "site-packages/beets/ui/commands.py", line 1594, in list_items
for item in lib.items(query):
^^^^^^^^^^^^^^^^
File "site-packages/beets/library.py", line 1695, in items
return self._fetch(Item, query, sort or
self.get_default_item_sort())
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "site-packages/beets/library.py", line 1673, in _fetch
return super()._fetch(model_cls, query, sort)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "site-packages/beets/dbcore/db.py", line 1301, in _fetch
rows = tx.query(sql, subvals)
^^^^^^^^^^^^^^^^^^^^^^
File "site-packages/beets/dbcore/db.py", line 991, in query
raise DBCustomFunctionError()
beets.dbcore.db.DBCustomFunctionError: beets defined SQLite function
failed; see the other errors above for details
```
Simplify wording by replacing flowery or pretentious terms throughout the project.
A bouquet refers to trees or flowers. The etymology is pretty clear on
the Latin root via French.
Co-authored-by: Sebastian Mohr <sebastian@mohrenclan.de>
- Test non-whitelisted genres resolving "up" in the tree.
- Test whitelisted original and whitelisted new genre resolving "up"
- Test non-whitelisted original genre resolving "up" (and deduplication
works)
Centralise plugin loading in `beets.plugins` and refactor the plugin
loading system to be more straightforward and eliminate complex mocking
in tests. Replace the two-stage class collection and instantiation
process with direct instance creation and storage.
Add plugins.PluginImportError and adjust plugin import tests to only
complain about plugin import issues.
Add `py.typed` marker file to support PEP 561 typing
This PR adds a `py.typed` marker file to the package directory to
indicate that the package includes inline type hints and is PEP 561
compliant.
Convert static _types dictionaries to dynamic cached class properties to
enable proper plugin type inheritance and avoid mutating shared state.
Key changes:
- Replace static _types dicts with @cached_classproperty decorators
- Update cached_classproperty to support proper caching with class names
- Remove manual _types mutation in plugin loading/unloading
- Add pluginload event and cache clearing for proper plugin integration
- Fix test to trigger type checking during item creation
This ensures plugin types are properly inherited through the class
hierarchy and eliminates issues with shared mutable state between
test runs.
The case_sensitive parameter was only used in tests, which now use
monkeypatch to control the behavior of util.case_sensitive() instead.
This simplifies the PathQuery initialization logic while maintaining
test coverage.
Fixes an issue where each spotify query was converted to ascii before sending. Adds a
new config option to enable legacy behaviour.
A file called japanese_track_request.json was made to mimic the Spotify
API response since I don't have the credentials. Entries in that will
need to be modified with the actual entries.
Co-authored-by: Sebastian Mohr <sebastian@mohrenclan.de>
Co-authored-by: Sebastian Mohr <39738318+semohr@users.noreply.github.com>
Co-authored-by: J0J0 Todos <2733783+JOJ0@users.noreply.github.com>
avoid linter error
avoid other linter error
fix format
changing deps (no lock!)
poetry lock?
lint & format
attempt 2 at poetry lock
crlf -> lf line endings
changelog!
## Description
Fixes#5560. Also a couple other incidental changes / improvements:
* Add `EventType` that holds the actual string literals used for event
sending. With type checking, this can prevent subtle bugs resulting from
misspelled event names.
* Fix `HiddenFileTest` by using `bytestring_path()`
## To Do
- [x] ~Documentation.~
- [x] Changelog.
- [x] Tests.
---------
Co-authored-by: J0J0 Todos <jojo@peek-a-boo.at>
Co-authored-by: J0J0 Todos <2733783+JOJ0@users.noreply.github.com>
Adds replace plugin. The plugin allows the user to replace the audio
file of a song, while keeping the tags and file name.
Some music servers keep track of favourite songs via paths and tags. Now
there won't be a need to 'refavourite'. Plus, this skips the
import/merge steps.
## Description
Fixes#5802.
Today, tests fail on most Windows machines because we hard-code `D:` as
the root drive, but most machines use `C:`. This change uses the same
normalization function in the test assertion to ensure the drives match.
## To Do
- [ ] ~~Documentation.~~
- [x] Changelog.
- [x] Tests. (this is a tests change)
## What changed?
* Updated tests to generate the drive name via normalization, instead of
hard-coding `D:`.
* Updated the `Item::destination()` method to document the
`relative_to_libdir` param.
## How tested?
* [x] Tests pass locally.
- Instead of checking for empty `artist` query, use `va_likely`
parameter to determine whether we should query for Various Artists or
not.
- `album` / `title` is always a truthy string - no need to handle empty
criteria case
- `tracks` list always has at least one track - no need to check for
`len(items)`
TIL that `with_suffix` does not simply append the suffix to the filename
- it instead replaces the old/current suffix. Or whatever seems to
look like a suffix, in our case, unfortunately...
These have probably not been run by anyone in ages, better to move the
code to our test suite where it is regularly exercised. In fact, the
latter covers most of the cases already. The only missing tests seem to
be those were exceptions are raised in the first or last stage. Thus,
this adds such tests.
Background
The `_legalize_stage` function was causing issues with Mypy due to
inconsistent type usage between the `path` and `extension` parameters.
This inconsistency stemmed from the `fragment` parameter influencing the
types of these variables.
Key issues
1. `path` was defined as `str`, while `extension` was `bytes`.
2. Depending on `fragment`, `extension` could be either `str` or `bytes`.
3. `path` was sometimes converted to `bytes` within `_legalize_stage`.
Item.destination` method
- The `fragment` parameter determined the output format:
- `False`: Returned absolute path as bytes (default)
- `True`: Returned path relative to library directory as str
Thus
- Rename `fragment` parameter to `relative_to_libdir` for clarity
- Ensure `Item.destination` returns `bytes` in all cases
- Code expecting strings now converts the output to `str`
- Use only `str` type in `_legalize_stage` and `_legalize_path`
functions
- These functions are no longer dependent on `relative_to_libdir`
## Description
This might be a quick one, depending on how you feel about it... It
allows you to pickle DB model objects. I don't think this is used
directly in Beets, but it might be useful in general. For instance, we
encountered an issue where we wanted to quickly pickle an Item or Album.
This sometimes worked and other times failed, which seemed quite
inconsistent.
Some DB model methods and properties have the side effect of attaching
an SQLite connection to self (._db), which prevents serialization. The
fix is quite straightforward, so I thought we might want to integrate
this into beets directly.
## To Do
- [x] Changelog
- [x] Tests
Using the correct function signature for g_file_new_for_path fixes the
tests on s390x.
I do not have the full story on why this failed consistently only on
s390x, but I guess the big endian might have something to play with
this.
Here is how the tests were failing:
```
169s ___________________________ ThumbnailsTest.test_uri ____________________________
169s
169s self = <test.plugins.test_thumbnails.ThumbnailsTest testMethod=test_uri>
169s
169s def test_uri(self):
169s gio = GioURI()
169s if not gio.available:
169s self.skipTest("GIO library not found")
169s
169s > assert gio.uri("/foo") == "file:///" # silent fail
169s E AssertionError: assert '' == 'file:///'
169s E
169s E - file:///
169s
169s test/plugins/test_thumbnails.py:268: AssertionError
```
You can see a full log here [1] and a history of consistent failure
here [2]. Both links are bound to expire at some point, sorry future
archeologist 🤷.
[1]: https://autopkgtest.ubuntu.com/results/autopkgtest-plucky/plucky/s390x/b/beets/20250403_162414_5d1da@/log.gz#S5
[2]: https://autopkgtest.ubuntu.com/packages/beets/plucky/s390x
This was not thought through clearly before. It now behaves as follows
which I suppose is least surprising to a user:
- force is on, keep_existing is on, but the whitelist is DISABLED
- no stage found anything on last.fm
- fall back to the original genre
If in this example the whitelist would be ENABLED, the behaviour
changes: Only if the existing genre passes the whitelist test the
original is kept.
If no album was found the next stage (artist) should be entered, the
original genre kicked out (not whitelisted) and artist genre accepted.
The log message is slightly misleading since it tried to keep existing
genres but they were not whitelisted, thus kicked out. This is expected.
- Rename method from _combine_genres() to _combine_resolve_and_log() to
make clear that it not only combines new and old genres but also
resolves them (which in this plugin's wording means "do the magic" of
canonicalizationm, whitelist checking and reducing to a configured
genre count).
- Clarify in _resolve docstring that a possible outcome might be all
genres being removed.
- Add an additional log message telling which existing genres are taken
into account BEFORE "the magic happens".
- Rename _to_delimited_genre_string() to _format_and_stringify()
- Move count reduction logic to _resolve_genres()
- Fix and rename a test
I found that the translator would sometimes replace the pipe character
with another symbol (maybe it got confused thinking the character is
part of the text?).
Added spaces around the pipe to make it more clear that it's definitely
the separator.