Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
46 changes: 46 additions & 0 deletions bench/test_benchmarks.py
Original file line number Diff line number Diff line change
Expand Up @@ -367,12 +367,58 @@ def test_validate_dataclass_compiled(benchmark: Any) -> None:
assert result.port == 443


@dataclass
class _Reading:
"""A unit/value pair, the nested leaf of a dataclass tree."""

unit: str
value: float


@dataclass
class _Device:
"""A dataclass holding nested dataclasses: the shape an API response has.

Compiling the outer dataclass compiles the nested schemas with it, so this is
where a regression in that recursion shows up; the flat ``_Service`` above
cannot see one.
"""

name: str
power: _Reading
voltage: _Reading
current: _Reading | None = None


DEVICE = probatio.DataclassSchema(_Device, compile=False)
DEVICE_COMPILED = probatio.DataclassSchema(_Device).compile()
DEVICE_PAYLOAD = {
"name": "inverter",
"power": {"unit": "W", "value": 300.0},
"voltage": {"unit": "V", "value": 230.1},
"current": {"unit": "A", "value": 1.3},
}


def test_validate_dataclass_tree(benchmark: Any) -> None:
"""Validate and construct a dataclass with nested dataclasses (interpreted)."""
result = benchmark(DEVICE, DEVICE_PAYLOAD)
assert result.power.value == 300.0


def test_validate_dataclass_tree_compiled(benchmark: Any) -> None:
"""Validate and construct a dataclass tree, every level through generated code."""
result = benchmark(DEVICE_COMPILED, DEVICE_PAYLOAD)
assert result.power.value == 300.0


def test_compiled_benchmarks_use_the_generated_validator() -> None:
"""Guard: every ``*_compiled`` benchmark really measures generated code."""
assert _is_compiled(CONFIG_COMPILED)
assert _is_compiled(LEAF_HEAVY_COMPILED)
assert _is_compiled(NESTED_COMPILED)
assert _is_compiled(SERVICE_COMPILED)
assert _is_compiled(DEVICE_COMPILED)
assert _is_compiled(LIST_COMPILED)


Expand Down
10 changes: 7 additions & 3 deletions docs/src/content/docs/guides/compiled-schemas.md
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,10 @@ schema = Schema({Required("name"): str, Optional("age"): int}).compile()

`DataclassSchema` and `TypedDictSchema` take the same flag, and they are the
clearest win: compiling fuses field validation and object construction into one
function with no intermediate dict.
function with no intermediate dict. A nested dataclass field compiles along with
the dataclass that holds it, so a tree of dataclasses (an API response, say)
generates all the way down, and the flag covers the whole tree: `compile=False`
on the outer dataclass keeps every nested one interpreted too.

```python
from dataclasses import dataclass
Expand Down Expand Up @@ -123,8 +126,9 @@ inlines the validators that show up most: `Coerce` to `int`/`float`, numeric
(`Any("on", "off")`, validated as a membership test), `Maybe(X)` (an optional
validated value), and `All`/`Any` chains of those.

Anything it does not handle is simply left interpreted. A recursive schema (`Self`
or a recursive JSON Schema `$ref`), an exclusive or inclusive group, a multi-element
Anything it does not handle is simply left interpreted. A recursive schema (`Self`,
a recursive JSON Schema `$ref`, or a dataclass or TypedDict on a recursion cycle:
one that holds itself, or two that hold each other), an exclusive or inclusive group, a multi-element
list (an "each item matches one of these" union), a list whose element does not
inline, an exotic key: the schema works exactly as before, just without the speedup.
You never lose correctness by compiling; at worst you do not gain speed.
Expand Down
Loading
Loading