Note that we can't preserve key order in json_util.dumps in Python 2.4, PYTHON-602.

This commit is contained in:
A. Jesse Jiryu Davis 2013-12-14 12:30:06 -05:00
parent c672b8c3cb
commit 2f67eff07e
2 changed files with 42 additions and 21 deletions

View File

@ -47,6 +47,10 @@ It won't handle :class:`~bson.binary.Binary` and :class:`~bson.code.Code`
instances (as they are extended strings you can't provide custom defaults),
but it will be faster as there is less recursion.
.. versionchanged:: 2.7
Preserves order when rendering SON, Timestamp, Code, Binary, and DBRef
instances. (But not in Python 2.4.)
.. versionchanged:: 2.3
Added dumps and loads helpers to automatically handle conversion to and
from json and supports :class:`~bson.binary.Binary` and
@ -114,7 +118,7 @@ def dumps(obj, *args, **kwargs):
.. versionchanged:: 2.7
Preserves order when rendering SON, Timestamp, Code, Binary, and DBRef
instances.
instances. (But not in Python 2.4.)
"""
if not json_lib:
raise Exception("No json library available")
@ -193,6 +197,14 @@ def object_hook(dct, compile_re=True):
def default(obj):
# We preserve key order when rendering SON, DBRef, etc. as JSON by
# returning a SON for those types instead of a dict. This works with
# the "json" standard library in Python 2.6+ and with simplejson
# 2.1.0+ in Python 2.5+, because those libraries iterate the SON
# using PyIter_Next. Python 2.4 must use simplejson 2.0.9 or older,
# and those versions of simplejson use the lower-level PyDict_Next,
# which bypasses SON's order-preserving iteration, so we lose key
# order in Python 2.4.
if isinstance(obj, ObjectId):
return {"$oid": str(obj)}
if isinstance(obj, DBRef):

View File

@ -40,6 +40,7 @@ from bson.tz_util import utc
from test.test_client import get_client
PY3 = sys.version_info[0] == 3
PY24 = sys.version_info[:2] == (2, 4)
class TestJsonUtil(unittest.TestCase):
@ -70,10 +71,11 @@ class TestJsonUtil(unittest.TestCase):
self.round_trip({"ref": DBRef("foo", 5, "db")})
self.round_trip({"ref": DBRef("foo", ObjectId())})
# Check order.
self.assertEqual(
'{"$ref": "collection", "$id": 1, "$db": "db"}',
json_util.dumps(DBRef('collection', 1, 'db')))
if not PY24:
# Check order.
self.assertEqual(
'{"$ref": "collection", "$id": 1, "$db": "db"}',
json_util.dumps(DBRef('collection', 1, 'db')))
def test_datetime(self):
# only millis, not micros
@ -130,14 +132,15 @@ class TestJsonUtil(unittest.TestCase):
'{"r": {"$regex": ".*", "$options": "ilm"}}',
compile_re=False)['r'])
# Check order.
self.assertEqual(
'{"$regex": ".*", "$options": "mx"}',
json_util.dumps(Regex('.*', re.M | re.X)))
if not PY24:
# Check order.
self.assertEqual(
'{"$regex": ".*", "$options": "mx"}',
json_util.dumps(Regex('.*', re.M | re.X)))
self.assertEqual(
'{"$regex": ".*", "$options": "mx"}',
json_util.dumps(re.compile(b('.*'), re.M | re.X)))
self.assertEqual(
'{"$regex": ".*", "$options": "mx"}',
json_util.dumps(re.compile(b('.*'), re.M | re.X)))
def test_minkey(self):
self.round_trip({"m": MinKey()})
@ -146,10 +149,12 @@ class TestJsonUtil(unittest.TestCase):
self.round_trip({"m": MaxKey()})
def test_timestamp(self):
res = json_util.json.dumps({"ts": Timestamp(4, 13)},
default=json_util.default)
self.assertEqual('{"ts": {"t": 4, "i": 13}}', res)
dct = json_util.json.loads(res)
res = json_util.dumps({"ts": Timestamp(4, 13)}, default=json_util.default)
if not PY24:
# Check order.
self.assertEqual('{"ts": {"t": 4, "i": 13}}', res)
dct = json_util.loads(res)
self.assertEqual(dct['ts']['t'], 4)
self.assertEqual(dct['ts']['i'], 13)
@ -178,9 +183,12 @@ class TestJsonUtil(unittest.TestCase):
json_util.loads('{"bin": {"$type": 0, "$binary": "AAECAwQ="}}'))
json_bin_dump = json_util.dumps(md5_type_dict)
self.assertEqual(
'{"md5": {"$binary": "IG43GK8JL9HRL4DK53HMrA==", "$type": "05"}}',
json_bin_dump)
if not PY24:
# Check order.
self.assertEqual(
'{"md5": {"$binary": "IG43GK8JL9HRL4DK53HMrA==",'
+ ' "$type": "05"}}',
json_bin_dump)
self.assertEqual(md5_type_dict,
json_util.loads('{"md5": {"$type": 5, "$binary":'
@ -208,8 +216,9 @@ class TestJsonUtil(unittest.TestCase):
res = json_util.dumps(code)
self.assertEqual(code, json_util.loads(res))
# Check order.
self.assertEqual('{"$code": "return z", "$scope": {"z": 2}}', res)
if not PY24:
# Check order.
self.assertEqual('{"$code": "return z", "$scope": {"z": 2}}', res)
def test_cursor(self):
db = self.db