Last active
April 12, 2018 00:21
-
-
Save ergo/40f2dab8b0e52eeb9e7d400fd5b16407 to your computer and use it in GitHub Desktop.
Comparison between Marshmallow and Colander
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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') |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
@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.