Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
Comparison between Marshmallow and Colander
import timeit
import uuid
import colander
import marshmallow
class BarList(colander.SequenceSchema):
item = colander.SchemaNode(
colander.Integer(), validator=colander.Range(min=1))
class SignupSchema(colander.MappingSchema):
@staticmethod
def schema_type():
return colander.Mapping(unknown='preserve')
username = colander.SchemaNode(colander.String())
foo = colander.SchemaNode(
colander.Integer(), validator=colander.Range(min=1))
bar = BarList()
@colander.deferred
def def_uuid(node, kw):
return str(uuid.uuid4())
@colander.deferred
def def_validator(node, kw):
return colander.Function(lambda v: kw['bound'] == v)
class SignupSchemaWithDeferred(SignupSchema):
uuid = colander.SchemaNode(colander.String(), missing=def_uuid)
bound = colander.SchemaNode(colander.String(), validator=def_validator)
class SignupSchemaM(marshmallow.Schema):
class Meta:
strict = True
ordered = True
preserve = True
username = marshmallow.fields.String(required=True)
foo = marshmallow.fields.Integer(
required=True,
validate=[marshmallow.validate.Range(min=1)])
bar = marshmallow.fields.List(marshmallow.fields.Integer(
validate=[marshmallow.validate.Range(min=1)]), many=True)
@marshmallow.post_load(pass_original=True)
def _add_unknown(self, data, original):
"""Preserve unknown keys during deserialization."""
for key, val in original.items():
if key not in self.fields:
data[key] = val
return data
class SignupSchemaContextM(SignupSchemaM):
uuid = marshmallow.fields.String(missing=lambda: str(uuid.uuid4()))
bound = marshmallow.fields.String()
@marshmallow.validates('bound')
def validate_bound(self, value):
if self.context['bound'] != value:
raise marshmallow.ValidationError('WRONG!')
data = {'foo': 1, 'bar': [1, 2, 3],
'bound': 'YYYY',
'baz': {'a': 'test',
'b': ['a', 'b', 'c']},
'username': 'ergo'}
def test_colander():
schema = SignupSchema()
return schema.deserialize(data)
to_bind_schema = SignupSchemaWithDeferred()
def test_colander_bind():
schema = to_bind_schema.bind(bound='YYYY')
return schema.deserialize(data)
def test_marshmallow():
schema = SignupSchemaM()
return schema.load(data).data
def test_marshmallow_context():
schema = SignupSchemaContextM()
schema.context.setdefault('bound', 'YYYY')
return schema.load(data).data
print(test_colander_bind())
print(test_marshmallow_context())
c_result = timeit.timeit(test_colander, number=10000)
m_result = timeit.timeit(test_marshmallow, number=10000)
print(f'Colander took {c_result:.4f}')
print(f'Marshmallow took {m_result:.4f}')
print('-----------------------')
print(f'Marshmallow took {m_result/c_result:.2f}x of Colander execution time')
print('\nNow testing with binds/context\n')
c_result = timeit.timeit(test_colander_bind, number=10000)
m_result = timeit.timeit(test_marshmallow_context, number=10000)
print(f'Colander w/bind took {c_result:.4f}')
print(f'Marshmallow w/context took {m_result:.4f}')
print('-----------------------')
print(f'Marshmallow w/context took {m_result/c_result:.2f}x of Colander w/bind execution time')
@ergo
Copy link
Author

ergo commented Apr 9, 2018

Marshmallow took 1.0638
-----------------------
Marshmallow took 3.96x of Colander execution time

Now testing with binds/context

Colander w/bind took 4.0173
Marshmallow w/context took 1.5313
-----------------------
Marshmallow w/context took 0.38x of Colander w/bind execution time

Process finished with exit code 0

@sloria
Copy link

sloria commented Apr 12, 2018

@ergo I updated the script to separate instantiation from the benchmark functions: https://gist.github.com/sloria/b84cd0118337bb3b6d001d959d95a922 .

I also posted the results for both marshmallow 2 and marshmallow 3 on my fork.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment