Support bytes in JSON output.

This commit is contained in:
Itamar Turner-Trauring 2020-10-05 11:01:11 -04:00
parent 963f9ba94b
commit 96231fab5f
4 changed files with 72 additions and 1 deletions

View File

@ -13,16 +13,19 @@ if PY2:
import six import six
import os, time, sys import os, time, sys
import yaml import yaml
import json
from twisted.trial import unittest from twisted.trial import unittest
from allmydata.util import idlib, mathutil from allmydata.util import idlib, mathutil
from allmydata.util import fileutil from allmydata.util import fileutil
from allmydata.util import jsonbytes
from allmydata.util import pollmixin from allmydata.util import pollmixin
from allmydata.util import yamlutil from allmydata.util import yamlutil
from allmydata.util.fileutil import EncryptedTemporaryFile from allmydata.util.fileutil import EncryptedTemporaryFile
from allmydata.test.common_util import ReallyEqualMixin from allmydata.test.common_util import ReallyEqualMixin
if six.PY3: if six.PY3:
long = int long = int
@ -469,3 +472,19 @@ class YAML(unittest.TestCase):
self.assertIsInstance(back[0], str) self.assertIsInstance(back[0], str)
self.assertIsInstance(back[1], str) self.assertIsInstance(back[1], str)
self.assertIsInstance(back[2], str) self.assertIsInstance(back[2], str)
class JSONBytes(unittest.TestCase):
"""Tests for BytesJSONEncoder."""
def test_encode_bytes(self):
"""BytesJSONEncoder can encode bytes."""
data = {
b"hello": [1, b"cd"],
}
expected = {
u"hello": [1, u"cd"],
}
encoded = jsonbytes.dumps(data)
self.assertEqual(json.loads(encoded), expected)
self.assertEqual(jsonbytes.loads(encoded), expected)

View File

@ -72,6 +72,7 @@ PORTED_MODULES = [
"allmydata.util.hashutil", "allmydata.util.hashutil",
"allmydata.util.humanreadable", "allmydata.util.humanreadable",
"allmydata.util.iputil", "allmydata.util.iputil",
"allmydata.util.jsonbytes",
"allmydata.util.log", "allmydata.util.log",
"allmydata.util.mathutil", "allmydata.util.mathutil",
"allmydata.util.namespace", "allmydata.util.namespace",

View File

@ -0,0 +1,51 @@
"""
A JSON encoder than can serialize bytes.
Ported to Python 3.
"""
from __future__ import unicode_literals
from __future__ import absolute_import
from __future__ import division
from __future__ import print_function
from future.utils import PY2
if PY2:
from future.builtins import filter, map, zip, ascii, chr, hex, input, next, oct, open, pow, round, super, bytes, dict, list, object, range, str, max, min # noqa: F401
import json
class BytesJSONEncoder(json.JSONEncoder):
"""
A JSON encoder than can also encode bytes.
The bytes are assumed to be UTF-8 encoded Unicode strings.
"""
def default(self, o):
if isinstance(o, bytes):
return o.decode("utf-8")
return json.JSONEncoder.default(self, o)
def dumps(obj, *args, **kwargs):
"""Encode to JSON, supporting bytes as keys or values.
The bytes are assumed to be UTF-8 encoded Unicode strings.
"""
if isinstance(obj, dict):
new_obj = {}
for k, v in obj.items():
if isinstance(k, bytes):
k = k.decode("utf-8")
new_obj[k] = v
obj = new_obj
return json.dumps(obj, cls=BytesJSONEncoder, *args, **kwargs)
# To make this module drop-in compatible with json module:
loads = json.loads
__all__ = ["dumps", "loads"]

View File

@ -1,7 +1,6 @@
from future.builtins import str from future.builtins import str
import time import time
import json
from twisted.web import ( from twisted.web import (
http, http,
@ -32,6 +31,7 @@ from allmydata.interfaces import (
from allmydata.util import ( from allmydata.util import (
base32, base32,
dictutil, dictutil,
jsonbytes as json, # Supporting dumping bytes
) )