Skip to content

Commit 8b2a71e

Browse files
youtuxolegpidsadnyi
authored andcommitted
Performance improvements (#13)
* Compute argspec for serialisation functions only once * mocked_get_context -> mocked_inspect_getargspec * Bump version to 1.3.2
1 parent 7dfaa74 commit 8b2a71e

File tree

8 files changed

+35
-14
lines changed

8 files changed

+35
-14
lines changed

CHANGES.rst

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,12 @@
11
Changelog
22
=========
33

4+
1.3.2
5+
-----
6+
7+
* Improve serialization performance (youtux)
8+
9+
410
1.3.1
511
-----
612

halogen/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
"""halogen public API."""
22

3-
__version__ = '1.3.1'
3+
__version__ = '1.3.2'
44

55
try:
66
from halogen.schema import Schema, attr, Attr, Link, Curie, Embedded, Accessor

halogen/schema.py

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -26,14 +26,13 @@ def BYPASS(value):
2626
return value
2727

2828

29-
def _get_context(func, kwargs):
29+
def _get_context(argspec, kwargs):
3030
"""Prepare a context for the serialization.
3131
32-
:param func: Function which needs or does not need kwargs.
32+
:param argspec: The argspec of the serialization function.
3333
:param kwargs: Dict with context
3434
:return: Keywords arguments that function can accept.
3535
"""
36-
argspec = inspect.getargspec(func)
3736
if argspec.keywords is not None:
3837
return kwargs
3938
return dict((arg, kwargs[arg]) for arg in argspec.args if arg in kwargs)
@@ -48,6 +47,10 @@ def __init__(self, getter=None, setter=None):
4847
self.getter = getter
4948
self.setter = setter
5049

50+
@cached_property
51+
def _getter_argspec(self):
52+
return inspect.getargspec(self.getter)
53+
5154
def get(self, obj, **kwargs):
5255
"""Get an attribute from a value.
5356
@@ -56,7 +59,7 @@ def get(self, obj, **kwargs):
5659
"""
5760
assert self.getter is not None, "Getter accessor is not specified."
5861
if callable(self.getter):
59-
return self.getter(obj, **_get_context(self.getter, kwargs))
62+
return self.getter(obj, **_get_context(self._getter_argspec, kwargs))
6063

6164
assert isinstance(self.getter, string_types), "Accessor must be a function or a dot-separated string."
6265

@@ -153,6 +156,10 @@ def accessor(self):
153156
attr = self.attr or self.name
154157
return Accessor(getter=attr, setter=attr)
155158

159+
@cached_property
160+
def _attr_type_serialize_argspec(self):
161+
return inspect.getargspec(self.attr_type.serialize)
162+
156163
def serialize(self, value, **kwargs):
157164
"""Serialize the attribute of the input data.
158165
@@ -172,7 +179,7 @@ def serialize(self, value, **kwargs):
172179
raise
173180
value = self.default() if callable(self.default) else self.default
174181

175-
return self.attr_type.serialize(value, **_get_context(self.attr_type.serialize, kwargs))
182+
return self.attr_type.serialize(value, **_get_context(self._attr_type_serialize_argspec, kwargs))
176183

177184
return self.attr_type
178185

setup.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -73,8 +73,10 @@ def run_tests(self):
7373
"Topic :: Software Development :: Libraries",
7474
"Topic :: Utilities",
7575
"Programming Language :: Python :: 2",
76-
"Programming Language :: Python :: 3"
77-
] + [("Programming Language :: Python :: %s" % x) for x in "2.7 3.4".split()],
76+
"Programming Language :: Python :: 2.7",
77+
"Programming Language :: Python :: 3",
78+
"Programming Language :: Python :: 3.4",
79+
],
7880
cmdclass={"test": ToxTestCommand},
7981
packages=["halogen"],
8082
install_requires=install_requires,

tests/conftest.py

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
"""Test configuration."""
2+
import inspect
23

34
import halogen
45
import mock
@@ -7,8 +8,13 @@
78

89

910
@pytest.fixture(scope="session")
10-
def mocked_get_context():
11+
def mocked_inspect_getargspec(request):
1112
"""Mock halogen.schema._get_context for returning empty dict."""
12-
patcher = mock.patch("halogen.schema._get_context")
13+
def f():
14+
return None
15+
16+
patcher = mock.patch("inspect.getargspec")
17+
patcher.return_value = inspect.getargspec(f)
18+
1319
patcher.start()
14-
patcher.return_value = {}
20+
request.addfinalizer(patcher.stop)

tests/unit/schema/accessor/test_get.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ def test_get_object_callable(basic_object_value, basic_object):
3232
assert acc.get(basic_object) == basic_object_value
3333

3434

35-
def test_get_object_callable_called(mocked_get_context, basic_object):
35+
def test_get_object_callable_called(mocked_inspect_getargspec, basic_object):
3636
"""Test if Accessor.get() calls Accessor.getter() if getter is callable."""
3737
acc = Accessor()
3838
acc.getter = mock.Mock()

tests/unit/schema/attr/test_serialize.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
from halogen.types import Type
66

77

8-
def test_serialize_type(mocked_get_context):
8+
def test_serialize_type(mocked_inspect_getargspec):
99
"""Test if serialization of a "type" works.
1010
1111
Test if serialization of an Attr with an attr_type that is a "type" correctly calls the serialization function.

tox.ini

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[tox]
22
distshare = {homedir}/.tox/distshare
3-
envlist = py27,py34
3+
envlist = py27,py34,py35,py36
44

55
[testenv]
66
commands = py.test --junitxml={envlogdir}/junit-{envname}.xml halogen tests

0 commit comments

Comments
 (0)