- Move query and sort construction into `ModelQuery` classmethods and parse
tokenized input once.
- Switch call sites to consume `parse_query(...).query` and
`parse_query(...).sort` instead of tuple unpacking.
- Remove legacy `dbcore` re-exports and obsolete helper functions in
`queryparse`.
- Update query/sort tests to match the new parsing behavior and simplified
query object shapes.
- Add ModelQuery in dbcore.queryparse and expose LibModel.parse_query as
the single parsing entrypoint for string and sequence query inputs.
- Route library fetch, path format matching, and plugin query parsing
through the model-level API, and deprecate
beets.library.parse_query_string and parse_query_parts.
- Update tests to cover invalid query parsing via ModelQuery and align
smartplaylist sort assertions with parsed sort behavior.
- Replace parse_query_part with a QueryTerm parser that captures negate, field,
prefix, and pattern in one pass.
- Resolve query class selection through QueryTerm so prefix and field-based
dispatch are handled consistently.
- Move implicit path query normalization into dbcore parsing, simplifying
library-level query parsing.
- Tighten plugin query typing and update dbcore tests for path behavior and
related fixture assertions.
Move sort case-sensitivity config lookup into `FieldSort` as a cached
class property instead of threading a `case_insensitive` flag through
query parsing helpers.
This centralizes sort behavior at the sort implementation boundary and
simplifies parse/orchestration call paths while preserving configurable
case-sensitive ordering.
Sorting by a field whose declared type is nullable (its null value is
`None`, e.g. `NullInteger`/`NullFloat`) raised `TypeError: '<' not
supported between instances of ...` when the field was present on some
objects but missing on others, because `FieldSort.sort` compared `None`
against real values.
Group missing values together in the sort key so they are never compared
against present ones: they are ordered first when sorting ascending and
last when descending, matching SQLite's default ordering of NULLs used by
the fast `FixedFieldSort` path.
Fixes#3461.
Co-Authored-By: Claude <noreply@anthropic.com>
`requests_mock` was configured with a fallback matcher to return an
exception on any request. The intent was to have tests fail if any
requests are attempted in tests without matching mocks. This is the
default behavior of `requests_mock`, so no fallback matcher is
necessary. The side-effect of the fallback matcher, removed by this
commit, was that 1) the `NoMockAddress` exception was raised to the
function under test rather than to the test harness and 2) the
`NoMockAddress` exception was not able to be constructed because the
`exc` parameter to `requests_mock.register_uri` does not support
exceptions that require arguments. This behavior is visible in the
following log snippet, taken from an incorrectly-passing test:
```
DEBUG beets.fetchart:logging.py:163 downloading image: https://images.amazon.com/images/P/xxxx.01.LZZZZZZZ.jpg
DEBUG requests_mock.adapter:adapter.py:256 GET https://images.amazon.com/images/P/xxxx.01.LZZZZZZZ.jpg 200
DEBUG beets.fetchart:logging.py:163 not a supported image: text/html
DEBUG beets.fetchart:logging.py:163 downloading image: https://images.amazon.com/images/P/xxxx.02.LZZZZZZZ.jpg
DEBUG beets.fetchart:logging.py:163 error fetching art: NoMockAddress.__init__() missing 1 required positional argument: 'request'
```
The test that emitted these logs passed because the `FetchArt` plugin is
written to handle exceptions raised by art sources, skipping and logging
an error. For the exception to cause a failing test, the default
behavior of `requests_mock` of raising an exception to the test harness
when no matching mock requests can be found must be allowed.
Signed-off-by: Ross Williams <ross@ross-williams.net>
Test whether fetchart is run during the import phase and, specifically,
whether the `fetch_for_asis` setting is honored. Mock boundary is
between `FetchArtPlugin` and `ArtSource`, an already-existing internal
API boundary.
- Removing the coverage omit configuration exposed unused branches, helpers, and
fixtures across the test suite.
- Delete that redundant logic and simplify affected tests while preserving their
behavior.
When `formats` includes `link`, importfeeds creates a symlink per imported
item. A failed symlink (lacking privilege on Windows, a read-only directory, or
a filesystem without symlink support) raised beets.util.FilesystemError out of
the import pipeline and aborted the whole `beet import` run, even though the
tracks were already imported.
Catch FilesystemError around the link() call, log a per-item warning, and
continue with the remaining items. Add regression tests for the
warn-and-continue behaviour, that only FilesystemError is caught, and that a
successful link still creates the symlink. Add a changelog entry.
Fixes#840.