Skip to content

Commit 6e2ecc1

Browse files
committed
PYTHON-1111 JSONOptions.document_class requires simplejson in Python2.6
1 parent 78c8a45 commit 6e2ecc1

File tree

3 files changed

+69
-7
lines changed

3 files changed

+69
-7
lines changed

bson/json_util.py

Lines changed: 30 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -70,10 +70,24 @@
7070
import base64
7171
import collections
7272
import datetime
73-
import json
7473
import re
74+
import sys
7575
import uuid
7676

77+
_HAS_OBJECT_PAIRS_HOOK = True
78+
if sys.version_info[:2] == (2, 6):
79+
# In Python 2.6, json does not include object_pairs_hook. Use simplejson
80+
# instead.
81+
try:
82+
import simplejson as json
83+
except ImportError:
84+
import json
85+
_HAS_OBJECT_PAIRS_HOOK = False
86+
else:
87+
import json
88+
89+
from pymongo.errors import ConfigurationError
90+
7791
import bson
7892
from bson import EPOCH_AWARE, RE_TYPE, SON
7993
from bson.binary import (Binary, JAVA_LEGACY, CSHARP_LEGACY, OLD_UUID_SUBTYPE,
@@ -106,6 +120,10 @@
106120
class JSONOptions(CodecOptions):
107121
"""Encapsulates JSON options for :func:`dumps` and :func:`loads`.
108122
123+
Raises :exc:`~pymongo.errors.ConfigurationError` on Python 2.6 if
124+
`simplejson <https://pypi.python.org/pypi/simplejson>`_ is not installed
125+
and document_class is not the default (:class:`dict`).
126+
109127
:Parameters:
110128
- `strict_number_long`: If ``True``, :class:`~bson.int64.Int64` objects
111129
are encoded to MongoDB Extended JSON's *Strict mode* type
@@ -145,6 +163,11 @@ def __new__(cls, strict_number_long=False, strict_date=False,
145163
if kwargs["tz_aware"]:
146164
kwargs["tzinfo"] = kwargs.get("tzinfo", utc)
147165
self = super(JSONOptions, cls).__new__(cls, *args, **kwargs)
166+
if not _HAS_OBJECT_PAIRS_HOOK and self.document_class != dict:
167+
raise ConfigurationError(
168+
"Support for JSONOptions.document_class on Python 2.6 "
169+
"requires simplejson "
170+
"(https://pypi.python.org/pypi/simplejson) to be installed.")
148171
self.strict_number_long = strict_number_long
149172
self.strict_date = strict_date
150173
self.strict_uuid = strict_uuid
@@ -177,7 +200,7 @@ def dumps(obj, *args, **kwargs):
177200
Recursive function that handles all BSON types including
178201
:class:`~bson.binary.Binary` and :class:`~bson.code.Code`.
179202
180-
Raises :class:`~bson.errors.InvalidDatetime` if `obj` contains a
203+
Raises :exc:`~bson.errors.InvalidDatetime` if `obj` contains a
181204
:class:`datetime.datetime` without a timezone and
182205
`json_options.strict_date` is ``True``.
183206
@@ -211,8 +234,11 @@ def loads(s, *args, **kwargs):
211234
Accepts optional parameter `json_options`. See :class:`JSONOptions`.
212235
"""
213236
json_options = kwargs.pop("json_options", DEFAULT_JSON_OPTIONS)
214-
kwargs["object_pairs_hook"] = lambda pairs: object_pairs_hook(pairs,
215-
json_options)
237+
if _HAS_OBJECT_PAIRS_HOOK:
238+
kwargs["object_pairs_hook"] = lambda pairs: object_pairs_hook(
239+
pairs, json_options)
240+
else:
241+
kwargs["object_hook"] = lambda obj: object_hook(obj, json_options)
216242
return json.loads(s, *args, **kwargs)
217243

218244

doc/changelog.rst

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,25 @@
11
Changelog
22
=========
33

4+
Changes in Version 3.4
5+
----------------------
6+
7+
Version 3.4 implements the new server features introduced in MongoDB 3.4:
8+
9+
Highlights include:
10+
11+
- Finer control over JSON encoding/decoding with
12+
:class:`~bson.json_util.JSONOptions`.
13+
14+
15+
Issues Resolved
16+
...............
17+
18+
See the `PyMongo 3.4 release notes in JIRA`_ for the list of resolved issues
19+
in this release.
20+
21+
.. _PyMongo 3.4 release notes in JIRA: https://jira.mongodb.org/browse/PYTHON/fixforversion/16594
22+
423
Changes in Version 3.3
524
----------------------
625

test/test_json_util.py

Lines changed: 20 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,16 @@
2020
import sys
2121
import uuid
2222

23+
try:
24+
import simplejson
25+
HAS_SIMPLE_JSON = True
26+
except ImportError:
27+
HAS_SIMPLE_JSON = False
28+
2329
sys.path[0:0] = [""]
2430

31+
from pymongo.errors import ConfigurationError
32+
2533
from bson import json_util, EPOCH_AWARE, EPOCH_NAIVE, SON
2634
from bson.binary import (Binary, MD5_SUBTYPE, USER_DEFINED_SUBTYPE,
2735
JAVA_LEGACY, CSHARP_LEGACY, STANDARD)
@@ -39,6 +47,7 @@
3947
from test import unittest, IntegrationTest
4048

4149
PY3 = sys.version_info[0] == 3
50+
PY26 = sys.version_info[:2] == (2, 6)
4251

4352

4453
class TestJsonUtil(unittest.TestCase):
@@ -321,9 +330,17 @@ def test_numberlong(self):
321330
jsn)
322331

323332
def test_loads_document_class(self):
324-
self.assertEqual(SON([("foo", "bar"), ("b", 1)]), json_util.loads(
325-
'{"foo": "bar", "b": 1}',
326-
json_options=json_util.JSONOptions(document_class=SON)))
333+
# document_class dict should always work
334+
self.assertEqual({"foo": "bar"}, json_util.loads(
335+
'{"foo": "bar"}',
336+
json_options=json_util.JSONOptions(document_class=dict)))
337+
if PY26 and not HAS_SIMPLE_JSON:
338+
self.assertRaises(
339+
ConfigurationError, json_util.JSONOptions, document_class=SON)
340+
else:
341+
self.assertEqual(SON([("foo", "bar"), ("b", 1)]), json_util.loads(
342+
'{"foo": "bar", "b": 1}',
343+
json_options=json_util.JSONOptions(document_class=SON)))
327344

328345

329346
class TestJsonUtilRoundtrip(IntegrationTest):

0 commit comments

Comments
 (0)