PYTHON-1111 JSONOptions.document_class requires simplejson in Python2.6

This commit is contained in:
Shane Harvey 2016-07-28 11:13:10 -07:00
parent 78c8a4522d
commit 6e2ecc1817
3 changed files with 69 additions and 7 deletions

View File

@ -70,10 +70,24 @@ but it will be faster as there is less recursion.
import base64
import collections
import datetime
import json
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.
try:
import simplejson as json
except ImportError:
import json
_HAS_OBJECT_PAIRS_HOOK = False
else:
import json
from pymongo.errors import ConfigurationError
import bson
from bson import EPOCH_AWARE, RE_TYPE, SON
from bson.binary import (Binary, JAVA_LEGACY, CSHARP_LEGACY, OLD_UUID_SUBTYPE,
@ -106,6 +120,10 @@ _RE_OPT_TABLE = {
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`).
:Parameters:
- `strict_number_long`: If ``True``, :class:`~bson.int64.Int64` objects
are encoded to MongoDB Extended JSON's *Strict mode* type
@ -145,6 +163,11 @@ class JSONOptions(CodecOptions):
if kwargs["tz_aware"]:
kwargs["tzinfo"] = kwargs.get("tzinfo", utc)
self = super(JSONOptions, cls).__new__(cls, *args, **kwargs)
if not _HAS_OBJECT_PAIRS_HOOK and self.document_class != dict:
raise ConfigurationError(
"Support for JSONOptions.document_class on Python 2.6 "
"requires simplejson "
"(https://pypi.python.org/pypi/simplejson) to be installed.")
self.strict_number_long = strict_number_long
self.strict_date = strict_date
self.strict_uuid = strict_uuid
@ -177,7 +200,7 @@ def dumps(obj, *args, **kwargs):
Recursive function that handles all BSON types including
:class:`~bson.binary.Binary` and :class:`~bson.code.Code`.
Raises :class:`~bson.errors.InvalidDatetime` if `obj` contains a
Raises :exc:`~bson.errors.InvalidDatetime` if `obj` contains a
:class:`datetime.datetime` without a timezone and
`json_options.strict_date` is ``True``.
@ -211,8 +234,11 @@ def loads(s, *args, **kwargs):
Accepts optional parameter `json_options`. See :class:`JSONOptions`.
"""
json_options = kwargs.pop("json_options", DEFAULT_JSON_OPTIONS)
kwargs["object_pairs_hook"] = lambda pairs: object_pairs_hook(pairs,
json_options)
if _HAS_OBJECT_PAIRS_HOOK:
kwargs["object_pairs_hook"] = lambda pairs: object_pairs_hook(
pairs, json_options)
else:
kwargs["object_hook"] = lambda obj: object_hook(obj, json_options)
return json.loads(s, *args, **kwargs)

View File

@ -1,6 +1,25 @@
Changelog
=========
Changes in Version 3.4
----------------------
Version 3.4 implements the new server features introduced in MongoDB 3.4:
Highlights include:
- Finer control over JSON encoding/decoding with
:class:`~bson.json_util.JSONOptions`.
Issues Resolved
...............
See the `PyMongo 3.4 release notes in JIRA`_ for the list of resolved issues
in this release.
.. _PyMongo 3.4 release notes in JIRA: https://jira.mongodb.org/browse/PYTHON/fixforversion/16594
Changes in Version 3.3
----------------------

View File

@ -20,8 +20,16 @@ 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.binary import (Binary, MD5_SUBTYPE, USER_DEFINED_SUBTYPE,
JAVA_LEGACY, CSHARP_LEGACY, STANDARD)
@ -39,6 +47,7 @@ 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):
@ -321,9 +330,17 @@ class TestJsonUtil(unittest.TestCase):
jsn)
def test_loads_document_class(self):
self.assertEqual(SON([("foo", "bar"), ("b", 1)]), json_util.loads(
'{"foo": "bar", "b": 1}',
json_options=json_util.JSONOptions(document_class=SON)))
# document_class dict should always work
self.assertEqual({"foo": "bar"}, json_util.loads(
'{"foo": "bar"}',
json_options=json_util.JSONOptions(document_class=dict)))
if PY26 and not HAS_SIMPLE_JSON:
self.assertRaises(
ConfigurationError, json_util.JSONOptions, document_class=SON)
else:
self.assertEqual(SON([("foo", "bar"), ("b", 1)]), json_util.loads(
'{"foo": "bar", "b": 1}',
json_options=json_util.JSONOptions(document_class=SON)))
class TestJsonUtilRoundtrip(IntegrationTest):