PYTHON-1387 - Improve detection of object_pairs_hook support
This commit is contained in:
parent
da8fabc49a
commit
410027c057
@ -112,7 +112,6 @@ import re
|
||||
import sys
|
||||
import uuid
|
||||
|
||||
_HAS_OBJECT_PAIRS_HOOK = True
|
||||
if sys.version_info[:2] == (2, 6):
|
||||
# In Python 2.6, json does not include object_pairs_hook. Use simplejson
|
||||
# instead.
|
||||
@ -120,7 +119,6 @@ if sys.version_info[:2] == (2, 6):
|
||||
import simplejson as json
|
||||
except ImportError:
|
||||
import json
|
||||
_HAS_OBJECT_PAIRS_HOOK = False
|
||||
else:
|
||||
import json
|
||||
|
||||
@ -145,6 +143,13 @@ from bson.timestamp import Timestamp
|
||||
from bson.tz_util import utc
|
||||
|
||||
|
||||
try:
|
||||
json.loads("{}", object_pairs_hook=dict)
|
||||
_HAS_OBJECT_PAIRS_HOOK = True
|
||||
except TypeError:
|
||||
_HAS_OBJECT_PAIRS_HOOK = False
|
||||
|
||||
|
||||
_RE_OPT_TABLE = {
|
||||
"i": re.I,
|
||||
"l": re.L,
|
||||
@ -241,8 +246,8 @@ class JSONOptions(CodecOptions):
|
||||
"""Encapsulates JSON options for :func:`dumps` and :func:`loads`.
|
||||
|
||||
Raises :exc:`~pymongo.errors.ConfigurationError` on Python 2.6 if
|
||||
`simplejson <https://pypi.python.org/pypi/simplejson>`_ is not installed
|
||||
and document_class is not the default (:class:`dict`).
|
||||
`simplejson >= 2.1.0 <https://pypi.python.org/pypi/simplejson>`_ is not
|
||||
installed and document_class is not the default (:class:`dict`).
|
||||
|
||||
:Parameters:
|
||||
- `strict_number_long`: If ``True``, :class:`~bson.int64.Int64` objects
|
||||
@ -299,7 +304,7 @@ class JSONOptions(CodecOptions):
|
||||
if not _HAS_OBJECT_PAIRS_HOOK and self.document_class != dict:
|
||||
raise ConfigurationError(
|
||||
"Support for JSONOptions.document_class on Python 2.6 "
|
||||
"requires simplejson "
|
||||
"requires simplejson >= 2.1.0"
|
||||
"(https://pypi.python.org/pypi/simplejson) to be installed.")
|
||||
if json_mode not in (JSONMode.LEGACY,
|
||||
JSONMode.RELAXED,
|
||||
|
||||
@ -20,19 +20,14 @@ import re
|
||||
import sys
|
||||
import uuid
|
||||
|
||||
try:
|
||||
import simplejson
|
||||
HAS_SIMPLE_JSON = True
|
||||
except ImportError:
|
||||
HAS_SIMPLE_JSON = False
|
||||
|
||||
sys.path[0:0] = [""]
|
||||
|
||||
from pymongo.errors import ConfigurationError
|
||||
|
||||
from bson import json_util, EPOCH_AWARE, EPOCH_NAIVE, SON
|
||||
from bson.json_util import (DatetimeRepresentation,
|
||||
STRICT_JSON_OPTIONS)
|
||||
STRICT_JSON_OPTIONS,
|
||||
_HAS_OBJECT_PAIRS_HOOK)
|
||||
from bson.binary import (ALL_UUID_REPRESENTATIONS, Binary, MD5_SUBTYPE,
|
||||
USER_DEFINED_SUBTYPE, JAVA_LEGACY, CSHARP_LEGACY,
|
||||
STANDARD)
|
||||
@ -49,7 +44,6 @@ from bson.tz_util import FixedOffset, utc
|
||||
from test import unittest, IntegrationTest
|
||||
|
||||
PY3 = sys.version_info[0] == 3
|
||||
PY26 = sys.version_info[:2] == (2, 6)
|
||||
|
||||
|
||||
class TestJsonUtil(unittest.TestCase):
|
||||
@ -70,10 +64,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 _HAS_OBJECT_PAIRS_HOOK:
|
||||
# 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
|
||||
@ -231,14 +226,15 @@ class TestJsonUtil(unittest.TestCase):
|
||||
json_util.loads(
|
||||
'{"r": {"$regex": ".*", "$options": "ilm"}}')['r'])
|
||||
|
||||
# Check order.
|
||||
self.assertEqual(
|
||||
'{"$regex": ".*", "$options": "mx"}',
|
||||
json_util.dumps(Regex('.*', re.M | re.X)))
|
||||
if _HAS_OBJECT_PAIRS_HOOK:
|
||||
# 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()})
|
||||
@ -249,34 +245,45 @@ class TestJsonUtil(unittest.TestCase):
|
||||
def test_timestamp(self):
|
||||
dct = {"ts": Timestamp(4, 13)}
|
||||
res = json_util.dumps(dct, default=json_util.default)
|
||||
self.assertEqual('{"ts": {"$timestamp": {"t": 4, "i": 13}}}', res)
|
||||
|
||||
rtdct = json_util.loads(res)
|
||||
self.assertEqual(dct, rtdct)
|
||||
|
||||
if _HAS_OBJECT_PAIRS_HOOK:
|
||||
self.assertEqual('{"ts": {"$timestamp": {"t": 4, "i": 13}}}', res)
|
||||
|
||||
def test_uuid(self):
|
||||
doc = {'uuid': uuid.UUID('f47ac10b-58cc-4372-a567-0e02b2c3d479')}
|
||||
self.round_trip(doc)
|
||||
self.assertEqual(
|
||||
'{"uuid": {"$uuid": "f47ac10b58cc4372a5670e02b2c3d479"}}',
|
||||
json_util.dumps(doc))
|
||||
self.assertEqual(
|
||||
'{"uuid": {"$binary": "9HrBC1jMQ3KlZw4CssPUeQ==", "$type": "03"}}',
|
||||
json_util.dumps(doc, json_options=json_util.STRICT_JSON_OPTIONS))
|
||||
self.assertEqual(
|
||||
'{"uuid": {"$binary": "9HrBC1jMQ3KlZw4CssPUeQ==", "$type": "04"}}',
|
||||
json_util.dumps(doc, json_options=json_util.JSONOptions(
|
||||
strict_uuid=True, uuid_representation=STANDARD)))
|
||||
self.assertEqual(doc, json_util.loads(
|
||||
'{"uuid": {"$binary": "9HrBC1jMQ3KlZw4CssPUeQ==", "$type": "03"}}'))
|
||||
|
||||
if _HAS_OBJECT_PAIRS_HOOK:
|
||||
self.assertEqual(
|
||||
'{"uuid": '
|
||||
'{"$binary": "9HrBC1jMQ3KlZw4CssPUeQ==", "$type": "03"}}',
|
||||
json_util.dumps(
|
||||
doc, json_options=json_util.STRICT_JSON_OPTIONS))
|
||||
self.assertEqual(
|
||||
'{"uuid": '
|
||||
'{"$binary": "9HrBC1jMQ3KlZw4CssPUeQ==", "$type": "04"}}',
|
||||
json_util.dumps(
|
||||
doc, json_options=json_util.JSONOptions(
|
||||
strict_uuid=True, uuid_representation=STANDARD)))
|
||||
self.assertEqual(
|
||||
doc, json_util.loads(
|
||||
'{"uuid": '
|
||||
'{"$binary": "9HrBC1jMQ3KlZw4CssPUeQ==", "$type": "03"}}'))
|
||||
for uuid_representation in ALL_UUID_REPRESENTATIONS:
|
||||
options = json_util.JSONOptions(
|
||||
strict_uuid=True, uuid_representation=uuid_representation)
|
||||
self.round_trip(doc, json_options=options)
|
||||
# Ignore UUID representation when decoding BSON binary subtype 4.
|
||||
self.assertEqual(doc, json_util.loads(
|
||||
'{"uuid": {"$binary": "9HrBC1jMQ3KlZw4CssPUeQ==", "$type": '
|
||||
'"04"}}', json_options=options))
|
||||
if _HAS_OBJECT_PAIRS_HOOK:
|
||||
self.assertEqual(doc, json_util.loads(
|
||||
'{"uuid": '
|
||||
'{"$binary": "9HrBC1jMQ3KlZw4CssPUeQ==", "$type": "04"}}',
|
||||
json_options=options))
|
||||
|
||||
def test_binary(self):
|
||||
if PY3:
|
||||
@ -306,22 +313,23 @@ class TestJsonUtil(unittest.TestCase):
|
||||
self.assertEqual(bin_type_dict,
|
||||
json_util.loads('{"bin": {"$type": 0, "$binary": "AAECAwQ="}}'))
|
||||
|
||||
json_bin_dump = json_util.dumps(md5_type_dict)
|
||||
# Check order.
|
||||
self.assertEqual(
|
||||
'{"md5": {"$binary": "IG43GK8JL9HRL4DK53HMrA==",'
|
||||
+ ' "$type": "05"}}',
|
||||
json_bin_dump)
|
||||
if _HAS_OBJECT_PAIRS_HOOK:
|
||||
json_bin_dump = json_util.dumps(md5_type_dict)
|
||||
# Check order.
|
||||
self.assertEqual(
|
||||
'{"md5": {"$binary": "IG43GK8JL9HRL4DK53HMrA==",'
|
||||
+ ' "$type": "05"}}',
|
||||
json_bin_dump)
|
||||
|
||||
self.assertEqual(md5_type_dict,
|
||||
json_util.loads('{"md5": {"$type": 5, "$binary":'
|
||||
' "IG43GK8JL9HRL4DK53HMrA=="}}'))
|
||||
self.assertEqual(md5_type_dict,
|
||||
json_util.loads('{"md5": {"$type": 5, "$binary":'
|
||||
' "IG43GK8JL9HRL4DK53HMrA=="}}'))
|
||||
|
||||
json_bin_dump = json_util.dumps(custom_type_dict)
|
||||
self.assertTrue('"$type": "80"' in json_bin_dump)
|
||||
self.assertEqual(custom_type_dict,
|
||||
json_util.loads('{"custom": {"$type": 128, "$binary":'
|
||||
' "aGVsbG8="}}'))
|
||||
json_bin_dump = json_util.dumps(custom_type_dict)
|
||||
self.assertTrue('"$type": "80"' in json_bin_dump)
|
||||
self.assertEqual(custom_type_dict,
|
||||
json_util.loads('{"custom": {"$type": 128, "$binary":'
|
||||
' "aGVsbG8="}}'))
|
||||
|
||||
# Handle mongoexport where subtype >= 128
|
||||
self.assertEqual(128,
|
||||
@ -339,12 +347,13 @@ 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 _HAS_OBJECT_PAIRS_HOOK:
|
||||
# Check order.
|
||||
self.assertEqual('{"$code": "return z", "$scope": {"z": 2}}', res)
|
||||
|
||||
no_scope = Code('function() {}')
|
||||
self.assertEqual(
|
||||
'{"$code": "function() {}"}', json_util.dumps(no_scope))
|
||||
no_scope = Code('function() {}')
|
||||
self.assertEqual(
|
||||
'{"$code": "function() {}"}', json_util.dumps(no_scope))
|
||||
|
||||
def test_undefined(self):
|
||||
jsn = '{"name": {"$undefined": true}}'
|
||||
@ -366,7 +375,7 @@ class TestJsonUtil(unittest.TestCase):
|
||||
self.assertEqual({"foo": "bar"}, json_util.loads(
|
||||
'{"foo": "bar"}',
|
||||
json_options=json_util.JSONOptions(document_class=dict)))
|
||||
if PY26 and not HAS_SIMPLE_JSON:
|
||||
if not _HAS_OBJECT_PAIRS_HOOK:
|
||||
self.assertRaises(
|
||||
ConfigurationError, json_util.JSONOptions, document_class=SON)
|
||||
else:
|
||||
|
||||
Loading…
Reference in New Issue
Block a user