mrkeyoor.com_
Sun 20 Sept 11:46 UTC
PyPIUtilsupdated 20 Sept 2026

marshmallow review

marshmallow 4.3.1 turns declared Schema fields into two directional operations: `load()` validates and deserializes mappings, while `dump()` turns Python objects into JSON-ready values. Schemas stay separate from ORM, dataclass, and domain classes, so create input and public output can use different fields. Version 4.3.1 changes two narrow cases: `Enum(by_value=...)` permits `None` by default when the enum contains a `None` value, and URL validation accepts a fragment after an empty path. Our Python 3.12 sandbox imported this pure Python, typed package in 0.24 seconds.

Verdict

marshmallow 4.3.1 installed in 0.2 seconds and imported in 0.24 seconds in our sandbox, leaving one 1 MB package with 0 audit findings. It fits services that need wire contracts independent of model classes; choose a typed-model alternative when the return type from validation matters more than schema hooks.

We installed it

Lab card: what happened when we installed marshmallowScreenshot of marshmallow documentation
Install✓ · 0.2s1 package on disk · 1 MB
Importimport marshmallow in 0.24s · pure Python · py.typed · requires Python >=3.10
Known vulns0(pip-audit)

Answers from our run

Does marshmallow install cleanly?

Yes. In a fresh container with an empty cache, pip install marshmallow finished in 0.2s, leaving 1 package and 1 MB on disk. pip-audit reported no known vulnerabilities.

What does marshmallow need to run?

Python >=3.10, and nothing compiled: it is pure Python. In our run import marshmallow succeeded in 0.24s, and the package ships py.typed for type checkers.

marshmallow or pydantic: which should you use?

pydantic: Use it when validation should directly create typed model instances. marshmallow 4.3.1 installed in 0.2 seconds and imported in 0.24 seconds in our sandbox, leaving one 1 MB package with 0 audit findings.

When should you not use marshmallow?

load() must produce a statically typed model without custom code. Marshmallow returns plain data unless a post_load hook constructs the object.

API stability3/5The 4.x line keeps Schema, Field, `load()`, `dump()`, nested errors, hooks, and unknown policies familiar, and 4.3.1 changes only two edge cases. The 3.x boundary is substantial: context storage, implicit fields, default argument names, generic Field usage, decorator options, and validator failure behavior changed or disappeared. Old extension packages therefore need explicit 4.x support.
Docs5/5Read the Docs separates quickstart material from field, validator, hook, nesting, custom-field, and upgrade references. It explains the directional difference between load and dump, all three unknown policies, partial loading, nested error shapes, and the `dump_only` plus `unknown=INCLUDE` trap. Changelog entries link narrow fixes to their pull requests and release versions.
Maintenance5/5GitHub shows an unarchived repository pushed on 2026-08-26 with 146 open issues and pull requests. The published changelog lists 4.3.1 after several 2026 releases, and an unreleased 4.4.0 section records work already merged beyond the current package. Recent entries cover URL and IDN validation, typing, nested schemas, processor hooks, and Enum null handling.
Ecosystem5/5The registry figure supplied for this run is 27,509,078 weekly downloads, and GitHub reports 7,242 stars. webargs uses Schema for request parsing, apispec can derive API descriptions, and marshmallow-sqlalchemy connects schema fields with ORM models. This surrounding stack is the clearest selection reason; projects outside it can choose libraries with stronger typed return values.

Use it if

  • One model needs distinct schemas for creation, patching, administration, and public responses.
  • An HTTP API should return a nested map of all field errors instead of stopping after the first failure.
  • Wire names, model attributes, input-only secrets, output-only IDs, partial loads, and processing hooks need explicit control.
  • The service already uses webargs, apispec, or marshmallow-sqlalchemy and benefits from their shared Schema model.
Skip it if

Setup reality

We installed marshmallow 4.3.1 in a clean Python 3.12 Bookworm sandbox in 0.2 seconds. It left one package and 1 MB on disk. pip-audit reported 0 known vulnerabilities. The package measurement lists 2 direct dependencies, requires Python 3.10 or newer, and found pure Python code plus py.typed. import marshmallow worked in 0.24 seconds.

There is no compiler, credential, server, or config file to prepare. The real setup decision is where each wire schema lives and which unknown-field policy it uses. The default raises ValidationError. EXCLUDE drops extra keys, while INCLUDE carries them into loaded data without field validation. That last choice can become a mass-assignment bug if loaded mappings feed model constructors or update calls.

load() validates and deserializes, then returns a dict unless a post_load hook builds something else. dump() serializes without first validating the object. A load_only field never appears in output. A dump_only name is unknown during loading, and with unknown=INCLUDE a client can send that name through unvalidated. Use partial=True for PATCH semantics; a missing field and an explicit None remain different inputs.

A 3.x upgrade needs a deliberate pass. Schema.context moved to Context or Python context variables. missing and default became load_default and dump_default; hook options are keyword-only; pass_many became pass_collection; validators reject by raising ValidationError, not by returning false. Version 4.3.0 added field-level load processors. In 4.3.1, an Enum with a None member can change whether missing null allowance is inferred.

Patterns

Dump a Python object to primitives define-and-dump-schema

from datetime import date
from marshmallow import Schema, fields

class AlbumSchema(Schema):
    title = fields.Str()
    released = fields.Date()

album = {'title': 'Hunky Dory', 'released': date(1971, 12, 17)}
result = AlbumSchema().dump(album)

`dump()` serializes values such as `date`; it does not run the input validators attached to `load()`.

Return all input errors together load-and-report-errors

from marshmallow import Schema, ValidationError, fields

class UserSchema(Schema):
    email = fields.Email(required=True)
    age = fields.Int(required=True)

try:
    user = UserSchema().load(payload)
except ValidationError as error:
    return {'errors': error.messages}, 422

`ValidationError.messages` is keyed by field and can contain nested mappings or per-item lists.

Reject, drop, or retain unknown keys choose-unknown-policy

from marshmallow import EXCLUDE, Schema, fields

class PublicUserSchema(Schema):
    name = fields.Str(required=True)

    class Meta:
        unknown = EXCLUDE

RAISE is the default. `INCLUDE` keeps undeclared values without validating them, so `EXCLUDE` is safer for many public inputs.

Give a field a wire name rename-wire-keys

class EventSchema(Schema):
    created_at = fields.DateTime(
        data_key='createdAt',
        required=True,
    )

`data_key` controls the external name in both directions. Use `attribute` separately when the Python object uses another attribute.

Separate submitted and returned fields separate-input-output-fields

class AccountSchema(Schema):
    id = fields.Int(dump_only=True)
    email = fields.Email(required=True)
    password = fields.Str(load_only=True, required=True)

A submitted `dump_only` name counts as unknown. With `unknown=INCLUDE`, it can pass through without the field's validation.

Load a list of nested records load-nested-list

class ArtistSchema(Schema):
    name = fields.Str(required=True)

class AlbumSchema(Schema):
    title = fields.Str(required=True)
    artist = fields.Nested(ArtistSchema, required=True)

albums = AlbumSchema(many=True).load(payload)

With `many=True`, failures are indexed by list position and then by the nested field path.

Construct a domain object after load construct-domain-object

from dataclasses import dataclass
from marshmallow import post_load

@dataclass
class User:
    name: str

class UserSchema(Schema):
    name = fields.Str(required=True)

    @post_load
    def make_user(self, data, **kwargs):
        return User(**data)

A `post_load` method replaces the normal dict result. Accept extra keyword arguments because marshmallow passes load context to hooks.

Reject a reserved field value validate-one-field

from marshmallow import ValidationError, validates

class ProductSchema(Schema):
    sku = fields.Str(required=True)

    @validates('sku')
    def reject_reserved_sku(self, value, data_key):
        if value.startswith('SYS-'):
            raise ValidationError('Reserved SKU prefix.')

Marshmallow 4 passes `data_key` to this decorated validator. Rejection requires raising `ValidationError`.

Attach a cross-field error validate-across-fields

from marshmallow import ValidationError, validates_schema

class WindowSchema(Schema):
    start = fields.Date(required=True)
    end = fields.Date(required=True)

    @validates_schema
    def check_order(self, data, **kwargs):
        if data['start'] > data['end']:
            raise ValidationError('End precedes start.', field_name='end')

Supplying `field_name='end'` places the failure under that field; omit it to report under `_schema`.

Validate a partial update load-partial-update

schema = UserSchema()
changes = schema.load(
    {'email': 'new@example.com'},
    partial=True,
)

`partial=True` relaxes required checks. An absent key stays absent and should not be confused with a submitted null.

Set defaults by direction set-directional-defaults

class SettingsSchema(Schema):
    retries = fields.Int(load_default=3)
    theme = fields.Str(
        load_default='light',
        dump_default='light',
    )

Marshmallow 4 uses `load_default` and `dump_default`; the older `missing` and `default` arguments are gone.

Provide request data through Context pass-request-context

from typing import TypedDict
from marshmallow.experimental.context import Context

class RequestContext(TypedDict):
    viewer_id: int

with Context[RequestContext]({'viewer_id': 42}):
    output = PostSchema().dump(post)

`Schema.context` was removed in 4.0. Its typed replacement currently lives under `marshmallow.experimental.context`.

Alternatives

PackageRegistryPick it when
pydanticPyPIUse it when validation should directly create typed model instances.
cattrsPyPIUse it to structure existing dataclasses or attrs classes without parallel Schema classes.
msgspecPyPIUse it for typed JSON or MessagePack decoding where speed matters more than marshmallow hooks.

More utils guides

lru-cache · ajv · type-fest · p-limit · find-up · js-yaml · the whole shelf →

How this guide is made: grounded in the library's documentation, release notes, changelog, and issue history, on a fixed rubric — not a hands-on install of every release. The 50 most-downloaded entries are additionally install-verified in clean containers. Corrections: contact the desk.