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') |
This comment has been minimized.
This comment has been minimized.
@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
This comment has been minimized.