An extension to marshmallow to support fast schema (de)multiplexing.
marshmallow is a fantastic library for serialization and deserialization of data. For more on that project see its GitHub page or its Documentation.
This library adds a special kind of schema that actually multiplexes other schemas
based on object type. When serializing values, it uses get_obj_type() method
to get object type name. Then it uses type_schemas name-to-Schema mapping
to get schema for that particular object type, serializes object using that
schema and adds an extra field with name of object type. Deserialization is reverse.
This fork is designed to be a drop‑in replacement for marshmallow-oneofschema.
Using uv:
$ uv add marshmallow-fastoneofschema
Using poetry:
$ poetry add marshmallow-fastoneofschema
pip:
$ pip install marshmallow-fastoneofschema
Note: If marshmallow-oneofschema remains installed alongside this fork, the library logs an error and uses the fast fork. For reliability, uninstall the upstream package to avoid any packaging/order surprises.
This wheel also includes a small startup hook (.pth) that redirects imports of marshmallow_oneofschema to the fast fork, even if the upstream was installed later. You can disable this behavior by setting FASTONEOFSCHEMA_DISABLE_PTH=1.
The code below demonstrates how to set up a polymorphic schema. For the full context check out the tests. Once setup the schema should act like any other schema. If it does not then please file an Issue.
import marshmallow
import marshmallow.fields
from marshmallow_fastoneofschema import OneOfSchema
class Foo:
def __init__(self, foo):
self.foo = foo
class Bar:
def __init__(self, bar):
self.bar = bar
class FooSchema(marshmallow.Schema):
foo = marshmallow.fields.String(required=True)
@marshmallow.post_load
def make_foo(self, data, **kwargs):
return Foo(**data)
class BarSchema(marshmallow.Schema):
bar = marshmallow.fields.Integer(required=True)
@marshmallow.post_load
def make_bar(self, data, **kwargs):
return Bar(**data)
class MyUberSchema(OneOfSchema):
type_schemas = {"foo": FooSchema, "bar": BarSchema}
def get_obj_type(self, obj):
if isinstance(obj, Foo):
return "foo"
elif isinstance(obj, Bar):
return "bar"
else:
raise Exception("Unknown object type: {}".format(obj.__class__.__name__))
MyUberSchema().dump([Foo(foo="hello"), Bar(bar=123)], many=True)
# => [{'type': 'foo', 'foo': 'hello'}, {'type': 'bar', 'bar': 123}]
MyUberSchema().load(
[{"type": "foo", "foo": "hello"}, {"type": "bar", "bar": 123}], many=True
)
# => [Foo('hello'), Bar(123)]By default get_obj_type() returns obj.__class__.__name__, so you can just reuse that to save some typing:
class MyUberSchema(OneOfSchema):
type_schemas = {"Foo": FooSchema, "Bar": BarSchema}You can customize type field with type_field class property:
class MyUberSchema(OneOfSchema):
type_field = "object_type"
type_schemas = {"Foo": FooSchema, "Bar": BarSchema}
MyUberSchema().dump([Foo(foo="hello"), Bar(bar=123)], many=True)
# => [{'object_type': 'Foo', 'foo': 'hello'}, {'object_type': 'Bar', 'bar': 123}]You can use resulting schema everywhere marshmallow.Schema can be used, e.g.
import marshmallow as m
import marshmallow.fields as f
class MyOtherSchema(m.Schema):
items = f.List(f.Nested(MyUberSchema))MIT licensed. See the bundled LICENSE file for more details.
- This fork preserves the public API and error shapes of the upstream package.
- Optimizations avoid unnecessary copies when
type_field_removeisFalseand reduce overhead formany=Truein default configurations. - Aggressive instance caching can be disabled via
FOO_DISABLE_AGGRESSIVE_MODE=1if needed. - Supported Python versions: 3.11+.
This fork ships a DFM plugin that can enable JIT inlining for Nested(OneOfSchema) fields.
- Discovery: via entry point group
deepfriedmarshmallow.pluginsor envDFM_PLUGINS. - Initial scope: Only engages when
get_data_type/get_obj_typeare default and alltype_schemaskeys are strings. - Fallback: If conditions aren't met, DFM falls back to its standard generation.
Basic usage stays the same (plug‑and‑play). For projects that want to migrate explicitly to the new names:
- Replace dependency
marshmallow-oneofschemawith this fork. - Optionally switch imports to the new package/class names:
-
from marshmallow_fastoneofschema import FastOneOfSchema- Or keepfrom marshmallow_oneofschema import OneOfSchema(alias provided). - Existing code continues to work; new names are recommended for clarity. - Env flags:
-
FOO_DISABLE_AGGRESSIVE_MODE=1: disable instance caching. -FOO_CONTEXT_ISOLATION=1: enable per-task schema instance caching (ContextVars). - Per-schema overrides via
class Meta: fastoneof = {...}: -aggressive_mode: bool-context_isolation: bool - Per-request overrides via ContextVars (advanced):
-
OneOfSchema._foo_aggressive_ctx.set(True|False)-OneOfSchema._foo_isolation_ctx.set(True|False)