ProvenPin Install

marshmallow-oneofschema polymorphic nested schemas: working versions (tested 2026-10-16)

A OneOfSchema discriminated union over nested dataclass schemas that loads and dumps polymorphic payloads, with tests covering type dispatch and its sharp edges.

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 versions3
marshmallow@4.3.1marshmallow-oneofschema@3.2.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("marshmallow-oneofschema-polymorphic") (install), or see them on GitHub.

requirements.txt
marshmallow==4.3.1
marshmallow-oneofschema==3.2.0
pytest==9.1.1
schemas.py
from dataclasses import dataclass

import marshmallow as ma
from marshmallow_oneofschema import OneOfSchema


@dataclass
class EmailTarget:
    address: str


@dataclass
class SmsTarget:
    number: str


class EmailTargetSchema(ma.Schema):
    address = ma.fields.Email(required=True)

    @ma.post_load
    def make_target(self, data, **kwargs):
        return EmailTarget(**data)


class SmsTargetSchema(ma.Schema):
    number = ma.fields.Str(required=True)

    @ma.post_load
    def make_target(self, data, **kwargs):
        return SmsTarget(**data)


class TargetSchema(OneOfSchema):
    type_field = "kind"
    type_schemas = {"email": EmailTargetSchema, "sms": SmsTargetSchema}

    def get_obj_type(self, obj):
        return {EmailTarget: "email", SmsTarget: "sms"}[type(obj)]


class NotificationSchema(ma.Schema):
    id = ma.fields.Int(required=True)
    target = ma.fields.Nested(TargetSchema, required=True)
tests/test_schemas.py
import marshmallow as ma
import pytest
from marshmallow_oneofschema import OneOfSchema

from schemas import (
    EmailTarget,
    EmailTargetSchema,
    NotificationSchema,
    SmsTarget,
    TargetSchema,
)


def test_load_dispatches_on_kind():
    loaded = NotificationSchema().load(
        {"id": 1, "target": {"kind": "email", "address": "[email protected]"}}
    )
    assert loaded["target"] == EmailTarget("[email protected]")


def test_dump_adds_kind_and_round_trips():
    dumped = NotificationSchema().dump({"id": 2, "target": SmsTarget(number="+15555550100")})
    assert dumped["target"] == {"number": "+15555550100", "kind": "sms"}
    assert NotificationSchema().load(dumped)["target"] == SmsTarget("+15555550100")


def test_unknown_kind_is_reported_on_type_field():
    with pytest.raises(ma.ValidationError) as exc:
        TargetSchema().load({"kind": "carrier-pigeon", "address": "[email protected]"})
    assert exc.value.messages == {"kind": ["Unsupported value: carrier-pigeon"]}


def test_default_get_obj_type_must_match_type_schemas_keys():
    class NoGetObjType(OneOfSchema):
        type_field = "kind"
        type_schemas = TargetSchema.type_schemas

    # the default get_obj_type returns the class name ("EmailTarget"), not the
    # "email" key, and dump silently returns an error tuple instead of raising
    result = NoGetObjType().dump(EmailTarget("[email protected]"))
    assert result == (None, {"_schema": "Unsupported object type: EmailTarget"})


def test_type_field_remove_false_leaks_type_into_subschema():
    class KeepsTypeField(OneOfSchema):
        type_field = "kind"
        type_field_remove = False
        type_schemas = {"email": EmailTargetSchema}

    with pytest.raises(ma.ValidationError) as exc:
        KeepsTypeField().load({"kind": "email", "address": "[email protected]"})
    assert exc.value.messages == {"kind": ["Unknown field."]}


def test_parent_unknown_option_does_not_reach_nested_schema():
    data = {"id": 3, "target": {"kind": "email", "address": "[email protected]", "extra": 1}}
    # unknown=EXCLUDE on the parent is not passed through fields.Nested
    with pytest.raises(ma.ValidationError):
        NotificationSchema().load(data, unknown=ma.EXCLUDE)

    class Tolerant(ma.Schema):
        id = ma.fields.Int(required=True)
        target = ma.fields.Nested(TargetSchema, required=True, unknown=ma.EXCLUDE)

    assert Tolerant().load(data)["target"] == EmailTarget("[email protected]")
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.