msgpack binary round-trip for pydantic v2 models: working versions (tested 2026-10-15)
Pack and unpack pydantic v2 models to compact MessagePack bytes, handling Decimal, UUID, datetime, int map keys and bytes safely.
passed5 passed · 0 failed2026-10-15 18:09 UTC- Status
- passed
- Last passed
- 2026-10-15 18:09 UTC
- Tests
- 5 / 5
- 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 38783276983
The code
Exactly the files that passed. Your agent can pull them with get_recipe("msgpack-pydantic-binary-roundtrip") (install), or see them on GitHub.
msgpack==1.2.3 pydantic==2.14.1 pytest==9.1.1
from datetime import datetime
from decimal import Decimal
from uuid import UUID
import msgpack
from pydantic import BaseModel
class Item(BaseModel):
sku: str
qty: int
price: Decimal
class Order(BaseModel):
id: UUID
placed_at: datetime
items: list[Item]
attrs: dict[int, str] = {}
def pack_model(model: BaseModel) -> bytes:
# mode="json" turns Decimal/UUID/datetime into msgpack-friendly scalars
return msgpack.packb(model.model_dump(mode="json"), use_bin_type=True)
def unpack_model(cls: type[BaseModel], data: bytes) -> BaseModel:
return cls.model_validate(
msgpack.unpackb(data, raw=False, strict_map_key=True)
)
from datetime import datetime, timezone
from decimal import Decimal
from uuid import UUID
import msgpack
import pytest
from pydantic import BaseModel, ValidationError
from codec import Item, Order, pack_model, unpack_model
@pytest.fixture
def order() -> Order:
return Order(
id=UUID("12345678-1234-5678-1234-567812345678"),
placed_at=datetime(2024, 1, 2, 3, 4, 5),
items=[Item(sku="a", qty=2, price=Decimal("19.99"))],
attrs={1: "one", 2: "two"},
)
def test_roundtrip_preserves_rich_types(order):
back = unpack_model(Order, pack_model(order))
assert back == order
assert isinstance(back.id, UUID)
assert isinstance(back.placed_at, datetime)
assert back.items[0].price == Decimal("19.99")
assert back.attrs == {1: "one", 2: "two"}
def test_python_mode_leaks_scalars_msgpack_cannot_pack(order):
with pytest.raises(TypeError):
msgpack.packb(order.model_dump())
class Stamp(BaseModel):
at: datetime
def test_timestamp_ext_needs_timestamp_arg_to_rebuild_datetime():
at = datetime(2024, 1, 2, 3, 4, 5, tzinfo=timezone.utc)
raw = msgpack.packb({"at": msgpack.Timestamp(int(at.timestamp()))})
default = msgpack.unpackb(raw)
assert isinstance(default["at"], msgpack.Timestamp)
with pytest.raises(ValidationError):
Stamp.model_validate(default)
assert Stamp.model_validate(msgpack.unpackb(raw, timestamp=3)).at == at
class Counts(BaseModel):
by_id: dict[int, str]
def test_non_string_map_keys_need_strict_map_key_false():
raw = msgpack.packb(Counts(by_id={1: "one"}).model_dump())
with pytest.raises(ValueError):
msgpack.unpackb(raw)
assert msgpack.unpackb(raw, strict_map_key=False)["by_id"] == {1: "one"}
def test_bytes_survive_only_with_use_bin_type():
payload = {"blob": b"\x00\x01"}
intact = msgpack.unpackb(msgpack.packb(payload), raw=False)
assert intact["blob"] == b"\x00\x01"
corrupted = msgpack.unpackb(msgpack.packb(payload, use_bin_type=False), raw=False)
assert corrupted["blob"] == "\x00\x01"
[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
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.
FastAPI background tasks + dependency overrides
FastAPI BackgroundTasks for post-response work, with dependency_overrides to swap services in tests.
FastAPI endpoint with Pydantic v2 discriminated unions and Annotated validators
Accept a tagged union of payment methods with Field(discriminator), add Annotated validators, and test the exact 422 error shapes offline with TestClient.
feedparser XML fixtures into pydantic v2 models
Convert RSS and Atom feeds parsed by feedparser, including struct_time dates and tags, into validated pydantic v2 models with offline XML fixtures.