From 806d818123e7b2f19f9ae9456802d9b6a54c4461 Mon Sep 17 00:00:00 2001 From: Richard van der Hoff Date: Wed, 28 Mar 2018 17:44:02 +0100 Subject: [PATCH] Use static JSONEncoders (rather than creating new ones each time we encode an object) --- canonicaljson.py | 53 ++++++++++++++++++++++++++++-------------------- 1 file changed, 31 insertions(+), 22 deletions(-) diff --git a/canonicaljson.py b/canonicaljson.py index ee95b5a..c4e93c7 100644 --- a/canonicaljson.py +++ b/canonicaljson.py @@ -1,6 +1,7 @@ # -*- coding: utf-8 -*- # Copyright 2014 OpenMarket Ltd +# Copyright 2018 New Vector Ltd # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -14,7 +15,8 @@ # See the License for the specific language governing permissions and # limitations under the License. - +# using simplejson rather than regular json gives approximately a 25% +# performance improvement (as measured on python 2.7.12/simplejson 3.13.2) import simplejson as json from frozendict import frozendict @@ -22,6 +24,31 @@ __version__ = '1.0.0' +def _default(obj): + if type(obj) is frozendict: + # fishing the protected dict out of the object is a bit nasty, + # but we don't really want the overhead of copying the dict. + return dict(obj) + raise TypeError('Object of type %s is not JSON serializable' % + obj.__class__.__name__) + + +_canonical_encoder = json.JSONEncoder( + ensure_ascii=False, + separators=(',', ':'), + sort_keys=True, + default=_default, +) + + +_pretty_encoder = json.JSONEncoder( + ensure_ascii=True, + indent=4, + sort_keys=True, + default=_default, +) + + def encode_canonical_json(json_object): """Encodes the shortest UTF-8 JSON encoding with dictionary keys lexicographically sorted by unicode code point. @@ -32,29 +59,11 @@ def encode_canonical_json(json_object): Returns: bytes encoding the JSON object""" - return json.dumps( - json_object, - ensure_ascii=False, - separators=(',', ':'), - sort_keys=True, - cls=FrozenEncoder - ).encode("UTF-8") + s = _canonical_encoder.encode(json_object) + return s.encode("UTF-8") def encode_pretty_printed_json(json_object): """Encodes the JSON object dict as human readable ascii bytes.""" - return json.dumps( - json_object, - ensure_ascii=True, - indent=4, - sort_keys=True, - cls=FrozenEncoder - ).encode("ascii") - - -class FrozenEncoder(json.JSONEncoder): - def default(self, obj): - if type(obj) is frozendict: - return dict(obj) - return json.JSONEncoder.default(self, obj) + return _pretty_encoder.encode(json_object).encode("ascii")