Files
beets/beetsplug/limit.py
T
Šarūnas Nejus 98051a234d Add ability to filter flexible attributes through the Query
For a flexible attribute query, replace the `col_name` property with
a function call that extracts that attribute from the `field_attrs`
field introduced in the earlier commit.

Additionally, for boolean, numeric and date queries CAST the value to
NUMERIC SQLite affinity to ensure that our queries like 'flex:1..5' and
'flex:true' continue working fine.

This removes the concept of 'slow query', since every query for any
field now has an SQL clause.
2026-08-27 02:56:48 +01:00

93 lines
2.5 KiB
Python

"""Adds head/tail functionality to list/ls.
1. Implemented as `lslimit` command with `--head` and `--tail` options. This is
the idiomatic way to use this plugin.
2. Implemented as query prefix `<` for head functionality only. This is the
composable way to use the plugin (plays nicely with anything that uses the
query language).
"""
from __future__ import annotations
from collections import deque
from itertools import islice
from typing import TYPE_CHECKING, Protocol
from beets.dbcore import FieldQuery
from beets.plugins import BeetsPlugin
from beets.ui import Subcommand, print_
if TYPE_CHECKING:
from collections.abc import Iterable
from beets.library import LibModel, Library
class LsLimitCLIOpts(Protocol):
album: bool
head: int | None
tail: int | None
def lslimit(lib: Library, opts: LsLimitCLIOpts, args: list[str]) -> None:
"""Query command with head/tail."""
if (opts.head is not None) and (opts.tail is not None):
raise ValueError("Only use one of --head and --tail")
if (opts.head or opts.tail or 0) < 0:
raise ValueError("Limit value must be non-negative")
objs: Iterable[LibModel]
if opts.album:
objs = lib.albums(args)
else:
objs = lib.items(args)
if opts.head is not None:
objs = islice(objs, opts.head)
elif opts.tail is not None:
objs = deque(objs, opts.tail)
for obj in objs:
print_(format(obj))
lslimit_cmd = Subcommand("lslimit", help="query with optional head or tail")
lslimit_cmd.parser.add_option(
"--head", action="store", type="int", default=None
)
lslimit_cmd.parser.add_option(
"--tail", action="store", type="int", default=None
)
lslimit_cmd.parser.add_all_common_options()
lslimit_cmd.func = lslimit
class LimitPlugin(BeetsPlugin):
"""Query limit functionality via command and query prefix."""
def commands(self):
"""Expose `lslimit` subcommand."""
return [lslimit_cmd]
def queries(self):
class HeadQuery(FieldQuery):
"""This inner class pattern allows the query to track state."""
n = 0
N = None
@classmethod
def value_match(cls, pattern, value):
if cls.N is None:
cls.N = int(pattern)
if cls.N < 0:
raise ValueError("Limit value must be non-negative")
cls.n += 1
return cls.n <= cls.N
return {"<": HeadQuery}