marshmallow

An ORM/framework-agnostic Python library for converting complex objects to and from native datatypes, with built-in validation.

Library
PyPI
v4.3.1
7,238stars
MIT License

Repository Health

Pre-computed score based on development activity, maintenance, community, maturity, and trend momentum.How we score it →
74/100Good
Development Activity84
Maintenance40
Community72
Maturity60
Momentum40

Technical Analysis

AI-assessed by reading the actual repository — architecture, code quality, innovation, and documentation.How we score it →
84/100Excellent
Architecture85
Code Quality92
Innovation82
Learning Curve75

marshmallow is a Python library for converting complex datatypes, such as objects, to and from native Python datatypes. It lets you define schemas declaratively — as classes with typed field attributes — and use them to serialize (dump) application objects into primitives like dicts and JSON, and to deserialize (load) incoming data back into validated Python objects, raising structured errors when the data doesn’t fit.

Because marshmallow makes no assumptions about your ORM, web framework, or database layer, it’s commonly used as the serialization/validation layer in front of Flask, Django, or any custom data model. Nested schemas, custom fields, and pre/post-processing hooks (pre_load, post_dump, validates_schema, etc.) let it handle everything from flat API payloads to deeply nested, cross-field-validated documents.

What You Get

  • Declarative Schema classes with a rich built-in field library (Str, Int, Date, DateTime, Nested, List, Email, URL, UUID, and more)
  • Two-way conversion: dump() for object-to-primitive serialization, load() for primitive-to-object deserialization with validation
  • Structured ValidationError reporting that maps errors back to the exact field/path that failed
  • Pre/post-processing hooks (pre_load, post_load, pre_dump, post_dump) and schema-level and field-level validators (validates, validates_schema)
  • Nested and list-of-nested schema composition for modeling arbitrarily deep object graphs
  • A named schema registry (class_registry) so nested schemas can reference each other by string name instead of import order

Common Use Cases

  • Validating and deserializing incoming JSON request bodies in a Flask or other WSGI API before touching the database
  • Serializing SQLAlchemy or other ORM model instances into JSON-safe dicts for an HTTP response
  • Defining a single schema shared between an API’s request validation and its response serialization
  • Building configuration-file loaders that validate and coerce raw YAML/JSON into typed Python objects
  • Normalizing and validating third-party API responses before they enter application code

Under The Hood

Architecture Schema definitions flow through a custom ABCMeta-based metaclass in schema.py: _get_fields walks a class’s attributes at class-creation time, collects any Field instances (raising a clear TypeError if a bare Field class was declared instead of an instance), and attaches them as _declared_fields on the resulting Schema subclass. At call time, dump()/load() iterate those fields, applying pre_load/post_load/pre_dump/post_dump hooks registered via decorators in decorators.py, routing per-field and per-schema validators (validates, validates_schema) through error_store.py’s ErrorStore, which accumulates failures into a single field-path-indexed ValidationError rather than failing on the first bad field. Nested schemas resolve either by direct instance or, to avoid circular imports across large schema graphs, by string name via class_registry.py. The core abstraction that would break the most code if changed is the field/schema separation itself — fields own type conversion and per-value validation, schemas own hook ordering and structural validation, and fields.Nested is the only bridge between the two layers.

Tech Stack Pure Python 3.10+ with effectively zero required runtime dependencies — pyproject.toml lists only conditional backports (typing-extensions, backports-datetime-fromisoformat) needed for Python versions below 3.11, everything else is stdlib (datetime, decimal, ipaddress, uuid, json). The package builds via flit_core rather than setuptools, and the dev workflow runs on uv/tox/tox-uv with dependency-groups in pyproject.toml splitting docs/tests/lint/mypy tooling. Docs are built with Sphinx (furo theme, sphinx-issues, sphinxext-opengraph) and published to Read the Docs.

Code Quality The tests/ directory is organized by concern (test_serialization.py, test_deserialization.py, test_fields.py, test_validate.py, test_decorators.py, test_schema.py, plus a dedicated mypy_test_cases/ directory), run with pytest. ruff is configured with select = ["ALL"] and an explicit, individually-commented ignore list rather than a loose default rule set, and mypy runs as its own CI/tox job ({ name: "mypy", tox: mypy }) against a fully-typed codebase carrying a py.typed marker. pre-commit enforces both on every commit. No swallowed exceptions were found in the core modules — validation failures are always surfaced through ValidationError/ErrorStore rather than silently dropped.

API Design The declarative class-based schema style (fields as class attributes, a metaclass doing the collection) mirrors the ergonomics of Django’s ORM and WTForms, which is a large part of why it became the standard serialization layer paired with plain Flask apps. The API surface is deliberately narrow — dump/load/dumps/loads plus a handful of decorators — and the same schema instance serves both serialization and deserialization, so API authors don’t maintain two parallel definitions of the same shape. Getting started requires defining one schema class and calling .dump()/.load(); no base model class, ORM, or web framework integration is required to use it standalone.

Join founders buildingwith open source

Opinionated takes, migration guides, cost-saving tips, and insights from the open source ecosystem.

Subscribe on Substack
Join 750+ subscribers

Search