ProvenPin Install

orjson + Pydantic v2 model round trip: working versions (tested 2026-10-15)

Serialize Pydantic v2 models to bytes with orjson through a default fallback and option flags, then validate them back offline with TypeAdapter.

passed6 passed · 0 failed2026-10-15 16:03 UTC
Status
passed
Last passed
2026-10-15 16:03 UTC
Tests
6 / 6
Language
Python
Runtime
Python 3.13.16
pinned versions3
orjson@3.13.0pydantic@2.14.1pytest@9.1.1
run it
uv venv -q .venv && uv pip install -q --python .venv/bin/python -r requirements.txt
.venv/bin/python -m pytest -q
test environment
Python 3.13.16
linux 6.17.0-1022-azure
x64
runner: github-actions 38764699834

The code

Exactly the files that passed. Your agent can pull them with get_recipe("orjson-pydantic-v2-roundtrip") (install), or see them on GitHub.

requirements.txt
orjson==3.13.0
pydantic==2.14.1
pytest==9.1.1
codec.py
from decimal import Decimal
from enum import Enum
from typing import Any

import orjson
from pydantic import BaseModel, SecretStr, TypeAdapter


def _default(obj: Any) -> Any:
    if isinstance(obj, BaseModel):
        return obj.model_dump()
    if isinstance(obj, Decimal):
        return str(obj)
    if isinstance(obj, SecretStr):
        return str(obj)
    if isinstance(obj, Enum):
        return obj.value
    raise TypeError(f"Object of type {type(obj).__name__} is not JSON serializable")


def dumps(model: Any, *, sort_keys: bool = False, utc_z: bool = False) -> bytes:
    option = orjson.OPT_NON_STR_KEYS
    if sort_keys:
        option |= orjson.OPT_SORT_KEYS
    if utc_z:
        option |= orjson.OPT_NAIVE_UTC | orjson.OPT_UTC_Z
    return orjson.dumps(model, default=_default, option=option)


def loads(data: bytes | str, model_type: type[BaseModel]) -> BaseModel:
    return TypeAdapter(model_type).validate_python(orjson.loads(data))
tests/test_codec.py
import datetime
import uuid
from decimal import Decimal
from enum import Enum

import orjson
import pytest
from pydantic import BaseModel, SecretStr

from codec import dumps, loads


class Color(str, Enum):
    red = "red"


class Event(BaseModel):
    name: str
    at: datetime.datetime
    price: Decimal
    uid: uuid.UUID
    labels: dict[int, str]
    color: Color


class Account(BaseModel):
    user: str
    token: SecretStr


class ZFirst(BaseModel):
    z: int
    a: int


class AFirst(BaseModel):
    a: int
    z: int


def make_event(**overrides) -> Event:
    data = dict(
        name="deploy",
        at=datetime.datetime(2024, 1, 2, 3, 4, 5),
        price=Decimal("19.90"),
        uid=uuid.UUID(int=1),
        labels={7: "prod"},
        color=Color.red,
    )
    data.update(overrides)
    return Event(**data)


def test_roundtrip_is_lossless_over_bytes():
    event = make_event()
    payload = dumps(event)
    assert isinstance(payload, bytes)
    restored = loads(payload, Event)
    assert restored == event
    assert restored.price == Decimal("19.90")


def test_orjson_needs_a_default_for_models():
    event = make_event()
    with pytest.raises(TypeError):
        orjson.dumps(event)
    assert loads(dumps(event), Event) == event


def test_sort_keys_is_field_order_independent():
    assert dumps(ZFirst(z=1, a=2), sort_keys=True) == dumps(AFirst(a=2, z=1), sort_keys=True)
    assert dumps(ZFirst(z=1, a=2), sort_keys=True) == b'{"a":2,"z":1}'


def test_utc_z_option_makes_naive_datetimes_aware():
    event = make_event(at=datetime.datetime(2024, 1, 2, 3, 4, 5))
    raw = orjson.loads(dumps(event, utc_z=True))
    assert raw["at"] == "2024-01-02T03:04:05Z"
    restored = loads(dumps(event, utc_z=True), Event)
    assert restored.at.utcoffset() == datetime.timedelta(0)
    assert restored != event


def test_secretstr_is_masked_and_roundtrip_is_lossy():
    account = Account(user="ada", token="s3cr3t")
    assert orjson.loads(dumps(account)) == {"user": "ada", "token": "**********"}
    restored = loads(dumps(account), Account)
    assert restored.token.get_secret_value() == "**********"
    assert restored != account


def test_non_str_keys_are_stringified_for_json():
    raw = orjson.loads(dumps(make_event(labels={7: "prod"})))
    assert raw["labels"] == {"7": "prod"}
    with pytest.raises(TypeError):
        orjson.dumps({7: "prod"})
pytest.ini
[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.

terminal
claude mcp add --transport http provenpin https://mcp.provenpin.com/mcp

Any other MCP client: Streamable HTTP at https://mcp.provenpin.com/mcp. No API key, no signup.