jmespath queries over pydantic model dumps: working versions (tested 2026-10-15)
Dump pydantic models to JSON-mode dicts and filter or reshape them with jmespath expressions, covering datetime, Enum and list-root gotchas offline.
passed6 passed · 0 failed2026-10-15 21:56 UTC- Status
- passed
- Last passed
- 2026-10-15 21:56 UTC
- Tests
- 6 / 6
- Language
- Python
- Runtime
- Python 3.13.16
uv venv -q .venv && uv pip install -q --python .venv/bin/python -r requirements.txt .venv/bin/python -m pytest -q
Python 3.13.16 linux 6.17.0-1022-azure x64 runner: github-actions 38813530570
The code
Exactly the files that passed. Your agent can pull them with get_recipe("jmespath-pydantic-filter") (install), or see them on GitHub.
jmespath==1.1.0 pydantic==2.14.1 pytest==9.1.1
from datetime import datetime
from enum import Enum
import jmespath
from pydantic import BaseModel
class Category(Enum):
book = "book"
game = "game"
class Item(BaseModel):
name: str
price: float
category: Category = Category.book
created: datetime | None = None
class Catalog(BaseModel):
name: str
items: list[Item]
def records(items: list[Item]) -> list[dict]:
# mode="json" turns datetime/Enum members into JSON primitives so jmespath
# comparisons against literals actually match.
return [item.model_dump(mode="json") for item in items]
def search(items: list[Item], expression: str, default=None):
result = jmespath.search(expression, records(items))
return default if result is None else result
def names_cheaper_than(items: list[Item], limit: float) -> list[str]:
return search(items, f"[?price < `{limit}`].name", [])
def by_category(items: list[Item], category: Category) -> list[str]:
return search(items, f"[?category == '{category.value}'].name", [])
def search_catalog(catalog: Catalog, expression: str, default=None):
result = jmespath.search(expression, catalog.model_dump(mode="json"))
return default if result is None else result
from datetime import datetime
import jmespath
from catalog import (
Catalog,
Category,
Item,
by_category,
names_cheaper_than,
records,
search,
search_catalog,
)
def test_filter_by_numeric_literal():
items = [Item(name="pen", price=1.5), Item(name="laptop", price=999.0)]
assert names_cheaper_than(items, 100) == ["pen"]
def test_list_root_needs_star_projection():
items = [Item(name="pen", price=1.5)]
assert jmespath.search("name", records(items)) is None
assert search(items, "[*].name") == ["pen"]
def test_datetime_needs_json_mode():
item = Item(name="pen", price=1.0, created=datetime(2024, 1, 1, 12, 0, 0))
expr = "[?created == '2024-01-01T12:00:00'].name"
# default model_dump keeps a datetime object, which never equals the literal
assert jmespath.search(expr, [item.model_dump()]) == []
assert search([item], expr) == ["pen"]
def test_enum_needs_json_mode():
item = Item(name="pen", price=1.0, category=Category.game)
expr = "[?category == 'game'].name"
assert jmespath.search(expr, [item.model_dump()]) == []
assert search([item], expr) == ["pen"]
def test_missing_keys_are_silent():
items = [Item(name="pen", price=1.0)]
assert search(items, "[?missing == 'x'].name", []) == []
assert search(items, "[0].missing") is None
assert by_category(items, Category.game) == []
def test_nested_catalog_dump():
catalog = Catalog(
name="shop",
items=[Item(name="pen", price=1.5), Item(name="laptop", price=999.0)],
)
assert search_catalog(catalog, "items[?price < `100`].name") == ["pen"]
[pytest] pythonpath = .
Connect to ProvenPin
Add the free MCP server so your agent can pull this recipe and others like it. Same setup as the docs.
claude mcp add --transport http provenpin https://mcp.provenpin.com/mcp
{ "mcpServers": { "provenpin": { "url": "https://mcp.provenpin.com/mcp" } } }{ "servers": { "provenpin": { "type": "http", "url": "https://mcp.provenpin.com/mcp" } } }{ "mcpServers": { "provenpin": { "serverUrl": "https://mcp.provenpin.com/mcp" } } }{ "mcpServers": { "provenpin": { "type": "streamableHttp", "url": "https://mcp.provenpin.com/mcp" } } }Any other MCP client: Streamable HTTP at https://mcp.provenpin.com/mcp. No API key, no signup.
Related stacks
beartype conf + pydantic model_validate runtime checks
Pair beartype boundary checks with pydantic model_validate, using BeartypeConf to align int/float coercion and route violations, and pin down the gotchas where the two guards disagree.
DeepDiff diffing of pydantic v2 model dumps with ignore_order
Compare pydantic v2 model dumps with DeepDiff, normalizing python vs json dump modes and taming list order, datetime and float gotchas.
Faker and polyfactory seeded Pydantic model factories offline
Define Pydantic models and generate fixtures offline with polyfactory, binding one Faker instance so Use() providers and seed_random stay reproducible.
FastAPI + Pydantic v2 validators, discriminated unions and serializers
Pydantic v2 patterns in FastAPI that agents often get wrong: field_validator, model_validator, discriminated unions, computed_field and response_model filtering.