ProvenPin Install

marshmallow-dataclass schemas: nested load, dump and validation: working versions (tested 2026-10-15)

Generate marshmallow schemas from dataclasses to load, dump and validate nested records, including typed nested objects, field renaming and path-aware ValidationErrors.

passed6 passed · 0 failed2026-10-15 16:02 UTC
Status
passed
Last passed
2026-10-15 16:02 UTC
Tests
6 / 6
Language
Python
Runtime
Python 3.13.16
pinned versions3
marshmallow-dataclass@8.7.1marshmallow@4.3.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("marshmallow-dataclass-schema") (install), or see them on GitHub.

requirements.txt
marshmallow-dataclass==8.7.1
marshmallow==4.3.1
pytest==9.1.1
schemas.py
from dataclasses import dataclass, field
from datetime import date
from typing import List, Optional

import marshmallow as ma
import marshmallow_dataclass


@dataclass
class Address:
    street: str
    city: str


@dataclass
class Contact:
    email: str = field(metadata={"validate": ma.validate.Email()})
    phone: Optional[str] = None


@dataclass
class User:
    id: int = field(metadata={"data_key": "user_id"})
    name: str = field(metadata={"validate": ma.validate.Length(min=1)})
    joined: date
    address: Address
    contacts: List[Contact] = field(default_factory=list)
    nickname: Optional[str] = None


@dataclass
class UserPatch:
    name: Optional[str] = None
    nickname: Optional[str] = None


class ExcludeUnknown(ma.Schema):
    class Meta:
        unknown = ma.EXCLUDE


UserSchema = marshmallow_dataclass.class_schema(User)
LenientUserSchema = marshmallow_dataclass.class_schema(User, base_schema=ExcludeUnknown)
PatchSchema = marshmallow_dataclass.class_schema(UserPatch)
tests/test_schemas.py
from datetime import date

import pytest
from marshmallow import ValidationError

from schemas import (
    Address,
    Contact,
    LenientUserSchema,
    PatchSchema,
    User,
    UserPatch,
    UserSchema,
)

RAW = {
    "user_id": 7,
    "name": "Ada",
    "joined": "2024-01-02",
    "address": {"street": "1 Main", "city": "Springfield"},
    "contacts": [{"email": "[email protected]", "phone": "555"}],
}


def test_load_builds_nested_dataclasses():
    user = UserSchema().load(RAW)
    assert isinstance(user, User)
    assert isinstance(user.address, Address)
    assert isinstance(user.contacts[0], Contact)
    assert user.joined == date(2024, 1, 2)


def test_dump_round_trips_to_primitives():
    user = UserSchema().load(RAW)
    dumped = UserSchema().dump(user)
    assert dumped == {**RAW, "nickname": None}
    assert UserSchema().load(dumped) == user


def test_validation_error_exposes_nested_paths():
    bad = {**RAW, "contacts": [{"email": "not-an-email"}]}
    with pytest.raises(ValidationError) as exc:
        UserSchema().load(bad)
    assert exc.value.messages == {"contacts": {0: {"email": ["Not a valid email address."]}}}


def test_missing_nested_record_is_required():
    with pytest.raises(ValidationError) as exc:
        UserSchema().load({k: v for k, v in RAW.items() if k != "address"})
    assert exc.value.messages == {"address": ["Missing data for required field."]}


def test_unknown_fields_are_rejected_by_default():
    with pytest.raises(ValidationError) as exc:
        UserSchema().load({**RAW, "bogus": True})
    assert exc.value.messages == {"bogus": ["Unknown field."]}
    assert LenientUserSchema().load({**RAW, "bogus": True}).id == 7


def test_partial_load_requires_optional_fields():
    with pytest.raises(TypeError):
        UserSchema(partial=True).load({"user_id": 7, "name": "Ada"})
    assert PatchSchema().load({"name": "Ada"}) == UserPatch(name="Ada")
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.