ProvenPin Install

msgspec tagged union Structs over MessagePack and JSON: working versions (tested 2026-10-16)

Model a tagged union of msgspec Structs, round-trip it through MessagePack and JSON, and verify tag selection and strict-field validation under pytest.

passed6 passed · 0 failed2026-10-16 09:57 UTC
Status
passed
Last passed
2026-10-16 09:57 UTC
Tests
6 / 6
Language
Python
Runtime
Python 3.13.16
pinned versions2
msgspec@0.22.0pytest@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 38883899161

The code

Exactly the files that passed. Your agent can pull them with get_recipe("msgspec-msgpack-tagged-unions") (install), or see them on GitHub.

requirements.txt
msgspec==0.22.0
pytest==9.1.1
models.py
from typing import Union

import msgspec


class Created(msgspec.Struct, tag="created", forbid_unknown_fields=True):
    id: int
    name: str


class Deleted(msgspec.Struct, tag="deleted", forbid_unknown_fields=True):
    id: int
    reason: str


Event = Union[Created, Deleted]

DECODER = msgspec.json.Decoder(Event)


def to_msgpack(event: Event) -> bytes:
    return msgspec.msgpack.encode(event)


def from_msgpack(data: bytes) -> Event:
    return msgspec.msgpack.decode(data, type=Event)


def to_json(event: Event) -> bytes:
    return msgspec.json.encode(event)


def from_json(data: bytes) -> Event:
    return msgspec.json.decode(data, type=Event)
tests/test_models.py
import msgspec
import pytest
from models import (
    DECODER,
    Created,
    Deleted,
    from_json,
    from_msgpack,
    to_json,
    to_msgpack,
)


def test_msgpack_roundtrip_picks_subtype():
    data = to_msgpack(Deleted(2, "spam"))
    assert b"deleted" in data
    event = from_msgpack(data)
    assert isinstance(event, Deleted) and event.reason == "spam"


def test_json_carries_the_tag_field():
    assert to_json(Created(1, "a")) == b'{"type":"created","id":1,"name":"a"}'
    assert from_json(b'{"type":"deleted","id":9,"reason":"z"}') == Deleted(9, "z")


def test_unknown_tag_is_rejected():
    with pytest.raises(msgspec.ValidationError):
        from_json(b'{"type":"archived","id":1}')


def test_missing_tag_is_rejected():
    with pytest.raises(msgspec.ValidationError):
        from_msgpack(msgspec.msgpack.encode({"id": 1, "name": "a"}))


def test_strict_fields_reject_extras():
    with pytest.raises(msgspec.ValidationError):
        from_json(b'{"type":"created","id":1,"name":"a","extra":true}')


def test_msgpack_is_compact_and_decoder_is_reusable():
    event = Created(1, "a")
    assert len(to_msgpack(event)) < len(to_json(event))
    assert DECODER.decode(b'{"type":"created","id":1,"name":"a"}') == event
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.