Skip to content

Commit

Permalink
python: Serial JSON via Python's json lib.
Browse files Browse the repository at this point in the history
There is no particularly good reason to use our own Python JSON
serialization implementation when serialization can be done faster
with Python's built-in JSON library.

A few tests were changed due to Python's default JSON library
returning slightly more precise floating point numbers.

Signed-off-by: Terry Wilson <[email protected]>
Signed-off-by: Ben Pfaff <[email protected]>
  • Loading branch information
otherwiseguy authored and blp committed Jul 26, 2016
1 parent 9364ae6 commit 622749d
Show file tree
Hide file tree
Showing 2 changed files with 30 additions and 102 deletions.
106 changes: 10 additions & 96 deletions python/ovs/json.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,13 @@
# See the License for the specific language governing permissions and
# limitations under the License.

from __future__ import absolute_import
import functools
import json
import re
import sys

import six
from six.moves import range

try:
import ovs._json
Expand All @@ -25,112 +27,24 @@

__pychecker__ = 'no-stringiter'

escapes = {ord('"'): u"\\\"",
ord("\\"): u"\\\\",
ord("\b"): u"\\b",
ord("\f"): u"\\f",
ord("\n"): u"\\n",
ord("\r"): u"\\r",
ord("\t"): u"\\t"}
for esc in range(32):
if esc not in escapes:
escapes[esc] = u"\\u%04x" % esc

SPACES_PER_LEVEL = 2


class _Serializer(object):
def __init__(self, stream, pretty, sort_keys):
self.stream = stream
self.pretty = pretty
self.sort_keys = sort_keys
self.depth = 0

def __serialize_string(self, s):
self.stream.write(u'"%s"' % ''.join(escapes.get(ord(c), c) for c in s))

def __indent_line(self):
if self.pretty:
self.stream.write('\n')
self.stream.write(' ' * (SPACES_PER_LEVEL * self.depth))

def serialize(self, obj):
if obj is None:
self.stream.write(u"null")
elif obj is False:
self.stream.write(u"false")
elif obj is True:
self.stream.write(u"true")
elif isinstance(obj, six.integer_types):
self.stream.write(u"%d" % obj)
elif isinstance(obj, float):
self.stream.write("%.15g" % obj)
elif isinstance(obj, six.text_type):
# unicode() on Python 2, or str() in Python 3 (always unicode)
self.__serialize_string(obj)
elif isinstance(obj, str):
# This is for Python 2, where this comes out to unicode(str()).
# For Python 3, it's str(str()), but it's harmless.
self.__serialize_string(six.text_type(obj))
elif isinstance(obj, dict):
self.stream.write(u"{")

self.depth += 1
self.__indent_line()

if self.sort_keys:
items = sorted(obj.items())
else:
items = six.iteritems(obj)
for i, (key, value) in enumerate(items):
if i > 0:
self.stream.write(u",")
self.__indent_line()
self.__serialize_string(six.text_type(key))
self.stream.write(u":")
if self.pretty:
self.stream.write(u' ')
self.serialize(value)

self.stream.write(u"}")
self.depth -= 1
elif isinstance(obj, (list, tuple)):
self.stream.write(u"[")
self.depth += 1

if obj:
self.__indent_line()

for i, value in enumerate(obj):
if i > 0:
self.stream.write(u",")
self.__indent_line()
self.serialize(value)

self.depth -= 1
self.stream.write(u"]")
else:
raise Exception("can't serialize %s as JSON" % obj)
dumper = functools.partial(json.dumps, separators=(",", ":"),
ensure_ascii=False)


def to_stream(obj, stream, pretty=False, sort_keys=True):
_Serializer(stream, pretty, sort_keys).serialize(obj)
stream.write(dumper(obj, indent=SPACES_PER_LEVEL if pretty else None,
sort_keys=sort_keys))


def to_file(obj, name, pretty=False, sort_keys=True):
stream = open(name, "w")
try:
with open(name, "w") as stream:
to_stream(obj, stream, pretty, sort_keys)
finally:
stream.close()


def to_string(obj, pretty=False, sort_keys=True):
output = six.StringIO()
to_stream(obj, output, pretty, sort_keys)
s = output.getvalue()
output.close()
return s
return dumper(obj, indent=SPACES_PER_LEVEL if pretty else None,
sort_keys=sort_keys)


def from_stream(stream):
Expand Down
26 changes: 20 additions & 6 deletions tests/json.at
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
m4_define([JSON_CHECK_POSITIVE_C],
m4_define([JSON_CHECK_POSITIVE_C],
[AT_SETUP([$1])
AT_KEYWORDS([json positive])
AT_CHECK([printf %s "AS_ESCAPE([$2])" > input])
Expand All @@ -11,7 +11,7 @@ m4_define([JSON_CHECK_POSITIVE_C],
# JSON_CHECK_POSITIVE_PY(TITLE, INPUT, OUTPUT, TEST-JSON-ARGS,
# PYTHON-CHCEK, PYTHON-BIN)
#
m4_define([JSON_CHECK_POSITIVE_PY],
m4_define([JSON_CHECK_POSITIVE_PY],
[AT_SETUP([$1])
AT_KEYWORDS([json positive Python])
AT_SKIP_IF([test $5 = no])
Expand Down Expand Up @@ -41,6 +41,12 @@ m4_define([JSON_CHECK_POSITIVE],
JSON_CHECK_POSITIVE_PY([$1 - Python3], [$2], [$3], [$4],
[$HAVE_PYTHON3], [$PYTHON3])])

m4_define([JSON_CHECK_POSITIVE_PY23],
[JSON_CHECK_POSITIVE_PY([$1 - Python2], [$2], [$3], [$4],
[$HAVE_PYTHON], [$PYTHON])
JSON_CHECK_POSITIVE_PY([$1 - Python3], [$2], [$3], [$4],
[$HAVE_PYTHON3], [$PYTHON3])])

m4_define([JSON_CHECK_NEGATIVE_C],
[AT_SETUP([$1])
AT_KEYWORDS([json negative])
Expand Down Expand Up @@ -216,10 +222,14 @@ JSON_CHECK_POSITIVE(
# It seems likely that the following test will fail on some system that
# rounds slightly differently in arithmetic or in printf, but I'd like
# to keep it this way until we run into such a system.
JSON_CHECK_POSITIVE(
[large integers that overflow to reals],
JSON_CHECK_POSITIVE_C(
[C - large integers that overflow to reals],
[[[9223372036854775807000, -92233720368547758080000]]],
[[[9.22337203685478e+21,-9.22337203685478e+22]]])
JSON_CHECK_POSITIVE_PY23(
[large integers that overflow to reals],
[[[9223372036854775807000, -92233720368547758080000]]],
[[[9.223372036854776e+21,-9.223372036854776e+22]]])

JSON_CHECK_POSITIVE(
[negative zero],
Expand All @@ -237,10 +247,14 @@ JSON_CHECK_POSITIVE(
# It seems likely that the following test will fail on some system that
# rounds slightly differently in arithmetic or in printf, but I'd like
# to keep it this way until we run into such a system.
JSON_CHECK_POSITIVE(
[+/- DBL_MAX],
JSON_CHECK_POSITIVE_C(
[C - +/- DBL_MAX],
[[[1.7976931348623157e+308, -1.7976931348623157e+308]]],
[[[1.79769313486232e+308,-1.79769313486232e+308]]])
JSON_CHECK_POSITIVE_PY23(
[+/- DBL_MAX],
[[[1.7976931348623157e+308, -1.7976931348623157e+308]]],
[[[1.7976931348623157e+308,-1.7976931348623157e+308]]])

JSON_CHECK_POSITIVE(
[negative reals],
Expand Down

0 comments on commit 622749d

Please sign in to comment.