Skip to content

feat: filter & service corrections#431

Merged
cofin merged 10 commits intomainfrom
feat/list-filter-correctness
Apr 27, 2026
Merged

feat: filter & service corrections#431
cofin merged 10 commits intomainfrom
feat/list-filter-correctness

Conversation

@cofin
Copy link
Copy Markdown
Member

@cofin cofin commented Apr 27, 2026

Summary

Closes a cluster of filter-correctness issues filed 2026-04-26 and adds the
first-party service base classes that downstream consumers have been
re-implementing.

cofin added 8 commits April 27, 2026 15:37
#429

Wraps up the cluster of issues filed alongside this branch:

- #425: SearchFilter/NotInSearchFilter now raise TypeError for unsupported
  field_name types (was silently dropping the WHERE clause). The same
  dispatch path now also accepts exp.Expression, finishing the type-widening
  promised in #427 for the LIKE-bearing filters.
- #426: QueryBuilder.order_by(sql.raw("COL DESC")) emits ORDER BY ... DESC
  by unwrapping a trailing exp.Alias whose alias name is asc/desc into an
  exp.Ordered with the right direction.
- #428: Adds SearchFilter.escape_like_value() static helper for safely
  escaping %, _, and \ in user-supplied LIKE input (opt-in; like_pattern
  itself stays unchanged for backwards compatibility).
- #429: Fleshes out SQLSpecAsyncService / SQLSpecSyncService with
  get_or_404 (raising sqlspec.exceptions.NotFoundError), explicit
  begin/commit/rollback methods, and a begin_transaction async/sync
  context manager. paginate now uses driver.find_filter to recover
  limit/offset, matching the docs recipe. Both classes are re-exported
  from sqlspec.extensions.{litestar,fastapi,starlette,sanic} (flask
  re-exports the sync class only).
…Flask

- _common.py: switch `import abc` to `from collections import abc` so the
  `abc.Sequence` annotation actually resolves (mypy CI failure).
- core/filters.py: annotate `parsed = exp.maybe_parse(...)` so mypy can
  narrow the union (was reported as `var-annotated`).
- service.py: import `StatementParameters` from `sqlspec.typing` (the real
  home) rather than the non-existent `sqlspec.core.parameters`. Add a
  `Yields:` section to the begin_transaction docstrings on both bases.
- extensions/{litestar,fastapi}/providers.py: type the SearchFilter
  field-name set as `set[str | exp.Expression]` so the variance widening
  introduced by issue #427 lines up with the providers.
- extensions/flask: re-export `SQLSpecAsyncService` alongside the sync
  base. Flask's plugin can portal an async driver, so async-service
  consumers need the symbol from the framework namespace they already
  import from.
The previous push only ran the type-checkers against `sqlspec/`, missing
tests/, where most of the failures lived. Running `make lint` (the
project's canonical pre-merge gate) surfaced 17 pyright errors and 5 mypy
errors. This commit closes them all:

- tests/unit/core/test_filters.py: hoist `from sqlglot import exp` and
  `from typing import Any` to module scope; type
  `set[str | exp.Expression]` for the multi-field SearchFilter test;
  switch the expression-mode tests from `exp.func("LOWER", ...)` (whose
  `Func` return type is reported as not assignable to `Expression`) to the
  concrete `exp.Lower` / `exp.Coalesce` / `exp.Upper` constructors;
  annotate the invalid-type fixtures with `Any` so the deliberate
  TypeError-trigger calls type-check.
- tests/unit/builder/test_select_builder.py: `cast` `sql.raw(...)` to
  `exp.Ordered` (the contract `order_by` accepts) and add `expr is not
  None` guards before reading the optional builder expression.
- tests/integration/extensions/litestar/test_in_fields_filters.py:
  guard `paths["/ordered"].get` (Operation | None) and switch the
  parameter lookup to `getattr(p, "name", None)` so the OpenAPI Reference
  branch of `Parameter | Reference` doesn't trip pyright.
@cofin cofin force-pushed the feat/list-filter-correctness branch from ec2c743 to 8b8cdf3 Compare April 27, 2026 15:41
@cofin cofin changed the title feat: filter correctness — qualified columns, expression sort/search, like_pattern, service base feat: filter & service corrections Apr 27, 2026
…otFoundError 404 handler in Litestar

SQLSpecAsyncService / SQLSpecSyncService were typed against an unbounded DriverT,
forcing `session: Any = self.session` escape hatches in 12 places. Bound the
TypeVars to AsyncDriverAdapterBase / SyncDriverAdapterBase with PEP 696 defaults
so driver methods (find_filter, select_with_total, select_one_or_none,
begin/commit/rollback) type-check directly.

Renamed `get_or_404` to `get_one` (hard rename — never released). The `_404`
suffix promised HTTP semantics the framework-agnostic core could not deliver:
nothing translated NotFoundError to a 404 response, even on Litestar.

Restored the implied behavior in the Litestar plugin only: on_app_init now
setdefaults a NotFoundError -> NotFoundException(detail=str(exc)) handler on
app_config.exception_handlers, so user-supplied handlers win and the standard
Litestar exception-handler chain (including any RFC 7807 customization)
renders the 404. Added unit tests covering the registration, override
precedence, and end-to-end 404 response.

Bumps version to 0.45.0.
@cofin cofin merged commit d85ace0 into main Apr 27, 2026
14 checks passed
@cofin cofin deleted the feat/list-filter-correctness branch April 27, 2026 16:38
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment