From 53a8e5c4c68ff117776667c7cae3926c05b33512 Mon Sep 17 00:00:00 2001 From: Matthew Jones Date: Wed, 6 Aug 2014 15:37:54 -0400 Subject: [PATCH] Upgrade simplejson to 3.6.0 --- awx/lib/site-packages/README | 2 +- awx/lib/site-packages/simplejson/__init__.py | 47 +++++++---- awx/lib/site-packages/simplejson/decoder.py | 11 +++ awx/lib/site-packages/simplejson/encoder.py | 68 +++++++++------ awx/lib/site-packages/simplejson/scanner.py | 5 ++ .../simplejson/tests/__init__.py | 83 ++++++++++--------- .../simplejson/tests/test_bigint_as_string.py | 79 ++++++++++-------- .../simplejson/tests/test_decode.py | 11 +++ .../simplejson/tests/test_speedups.py | 35 ++++++-- .../simplejson/tests/test_unicode.py | 10 ++- 10 files changed, 228 insertions(+), 123 deletions(-) diff --git a/awx/lib/site-packages/README b/awx/lib/site-packages/README index 82d8483625..fe943c0fef 100644 --- a/awx/lib/site-packages/README +++ b/awx/lib/site-packages/README @@ -54,6 +54,6 @@ rax-default-network-flags-python-novaclient-ext==0.2.3 (rax_default_network_flag rax-scheduled-images-python-novaclient-ext==0.2.1 (rax_scheduled_images_python_novaclient_ext/*) requests==2.3.0 (requests/*) setuptools==2.2 (setuptools/*, _markerlib/*, pkg_resources.py, easy_install.py, excluded bin/easy_install*) -simplejson==3.3.3 (simplejson/*, excluded simplejson/_speedups.so) +simplejson==3.6.0 (simplejson/*, excluded simplejson/_speedups.so) six==1.6.1 (six.py) South==0.8.4 (south/*) diff --git a/awx/lib/site-packages/simplejson/__init__.py b/awx/lib/site-packages/simplejson/__init__.py index f76ce22f18..0a92914f75 100644 --- a/awx/lib/site-packages/simplejson/__init__.py +++ b/awx/lib/site-packages/simplejson/__init__.py @@ -98,7 +98,7 @@ Using simplejson.tool from the shell to validate and pretty-print:: Expecting property name: line 1 column 3 (char 2) """ from __future__ import absolute_import -__version__ = '3.3.3' +__version__ = '3.6.0' __all__ = [ 'dump', 'dumps', 'load', 'loads', 'JSONDecoder', 'JSONDecodeError', 'JSONEncoder', @@ -144,14 +144,15 @@ _default_encoder = JSONEncoder( item_sort_key=None, for_json=False, ignore_nan=False, + int_as_string_bitcount=None, ) def dump(obj, fp, skipkeys=False, ensure_ascii=True, check_circular=True, - allow_nan=True, cls=None, indent=None, separators=None, - encoding='utf-8', default=None, use_decimal=True, - namedtuple_as_object=True, tuple_as_array=True, - bigint_as_string=False, sort_keys=False, item_sort_key=None, - for_json=False, ignore_nan=False, **kw): + allow_nan=True, cls=None, indent=None, separators=None, + encoding='utf-8', default=None, use_decimal=True, + namedtuple_as_object=True, tuple_as_array=True, + bigint_as_string=False, sort_keys=False, item_sort_key=None, + for_json=False, ignore_nan=False, int_as_string_bitcount=None, **kw): """Serialize ``obj`` as a JSON formatted stream to ``fp`` (a ``.write()``-supporting file-like object). @@ -209,6 +210,10 @@ def dump(obj, fp, skipkeys=False, ensure_ascii=True, check_circular=True, lossy operation that will not round-trip correctly and should be used sparingly. + If *int_as_string_bitcount* is a positive number (n), then int of size + greater than or equal to 2**n or lower than or equal to -2**n will be + encoded as strings. + If specified, *item_sort_key* is a callable used to sort the items in each dictionary. This is useful if you want to sort items other than in alphabetical order by key. This option takes precedence over @@ -238,8 +243,8 @@ def dump(obj, fp, skipkeys=False, ensure_ascii=True, check_circular=True, cls is None and indent is None and separators is None and encoding == 'utf-8' and default is None and use_decimal and namedtuple_as_object and tuple_as_array - and not bigint_as_string and not item_sort_key - and not for_json and not ignore_nan and not kw): + and not bigint_as_string and int_as_string_bitcount is None + and not item_sort_key and not for_json and not ignore_nan and not kw): iterable = _default_encoder.iterencode(obj) else: if cls is None: @@ -255,6 +260,7 @@ def dump(obj, fp, skipkeys=False, ensure_ascii=True, check_circular=True, item_sort_key=item_sort_key, for_json=for_json, ignore_nan=ignore_nan, + int_as_string_bitcount=int_as_string_bitcount, **kw).iterencode(obj) # could accelerate with writelines in some versions of Python, at # a debuggability cost @@ -263,11 +269,11 @@ def dump(obj, fp, skipkeys=False, ensure_ascii=True, check_circular=True, def dumps(obj, skipkeys=False, ensure_ascii=True, check_circular=True, - allow_nan=True, cls=None, indent=None, separators=None, - encoding='utf-8', default=None, use_decimal=True, - namedtuple_as_object=True, tuple_as_array=True, - bigint_as_string=False, sort_keys=False, item_sort_key=None, - for_json=False, ignore_nan=False, **kw): + allow_nan=True, cls=None, indent=None, separators=None, + encoding='utf-8', default=None, use_decimal=True, + namedtuple_as_object=True, tuple_as_array=True, + bigint_as_string=False, sort_keys=False, item_sort_key=None, + for_json=False, ignore_nan=False, int_as_string_bitcount=None, **kw): """Serialize ``obj`` to a JSON formatted ``str``. If ``skipkeys`` is false then ``dict`` keys that are not basic types @@ -319,6 +325,10 @@ def dumps(obj, skipkeys=False, ensure_ascii=True, check_circular=True, or lower than -2**53 will be encoded as strings. This is to avoid the rounding that happens in Javascript otherwise. + If *int_as_string_bitcount* is a positive number (n), then int of size + greater than or equal to 2**n or lower than or equal to -2**n will be + encoded as strings. + If specified, *item_sort_key* is a callable used to sort the items in each dictionary. This is useful if you want to sort items other than in alphabetical order by key. This option takes precendence over @@ -343,14 +353,16 @@ def dumps(obj, skipkeys=False, ensure_ascii=True, check_circular=True, """ # cached encoder - if (not skipkeys and ensure_ascii and + if ( + not skipkeys and ensure_ascii and check_circular and allow_nan and cls is None and indent is None and separators is None and encoding == 'utf-8' and default is None and use_decimal and namedtuple_as_object and tuple_as_array - and not bigint_as_string and not sort_keys - and not item_sort_key and not for_json - and not ignore_nan and not kw): + and not bigint_as_string and int_as_string_bitcount is None + and not sort_keys and not item_sort_key and not for_json + and not ignore_nan and not kw + ): return _default_encoder.encode(obj) if cls is None: cls = JSONEncoder @@ -366,6 +378,7 @@ def dumps(obj, skipkeys=False, ensure_ascii=True, check_circular=True, item_sort_key=item_sort_key, for_json=for_json, ignore_nan=ignore_nan, + int_as_string_bitcount=int_as_string_bitcount, **kw).encode(obj) diff --git a/awx/lib/site-packages/simplejson/decoder.py b/awx/lib/site-packages/simplejson/decoder.py index 38cb027aca..545e65877b 100644 --- a/awx/lib/site-packages/simplejson/decoder.py +++ b/awx/lib/site-packages/simplejson/decoder.py @@ -384,6 +384,17 @@ class JSONDecoder(object): have extraneous data at the end. """ + if idx < 0: + # Ensure that raw_decode bails on negative indexes, the regex + # would otherwise mask this behavior. #98 + raise JSONDecodeError('Expecting value', s, idx) if _PY3 and not isinstance(s, text_type): raise TypeError("Input string must be text, not bytes") + # strip UTF-8 bom + if len(s) > idx: + ord0 = ord(s[idx]) + if ord0 == 0xfeff: + idx += 1 + elif ord0 == 0xef and s[idx:idx + 3] == '\xef\xbb\xbf': + idx += 3 return self.scan_once(s, idx=_w(s, idx).end()) diff --git a/awx/lib/site-packages/simplejson/encoder.py b/awx/lib/site-packages/simplejson/encoder.py index 9815ee5210..db18244ecb 100644 --- a/awx/lib/site-packages/simplejson/encoder.py +++ b/awx/lib/site-packages/simplejson/encoder.py @@ -116,12 +116,14 @@ class JSONEncoder(object): """ item_separator = ', ' key_separator = ': ' + def __init__(self, skipkeys=False, ensure_ascii=True, - check_circular=True, allow_nan=True, sort_keys=False, - indent=None, separators=None, encoding='utf-8', default=None, - use_decimal=True, namedtuple_as_object=True, - tuple_as_array=True, bigint_as_string=False, - item_sort_key=None, for_json=False, ignore_nan=False): + check_circular=True, allow_nan=True, sort_keys=False, + indent=None, separators=None, encoding='utf-8', default=None, + use_decimal=True, namedtuple_as_object=True, + tuple_as_array=True, bigint_as_string=False, + item_sort_key=None, for_json=False, ignore_nan=False, + int_as_string_bitcount=None): """Constructor for JSONEncoder, with sensible defaults. If skipkeys is false, then it is a TypeError to attempt @@ -180,6 +182,10 @@ class JSONEncoder(object): or lower than -2**53 will be encoded as strings. This is to avoid the rounding that happens in Javascript otherwise. + If int_as_string_bitcount is a positive number (n), then int of size + greater than or equal to 2**n or lower than or equal to -2**n will be + encoded as strings. + If specified, item_sort_key is a callable used to sort the items in each dictionary. This is useful if you want to sort items other than in alphabetical order by key. @@ -207,6 +213,7 @@ class JSONEncoder(object): self.item_sort_key = item_sort_key self.for_json = for_json self.ignore_nan = ignore_nan + self.int_as_string_bitcount = int_as_string_bitcount if indent is not None and not isinstance(indent, string_types): indent = indent * ' ' self.indent = indent @@ -315,8 +322,9 @@ class JSONEncoder(object): return text - key_memo = {} + int_as_string_bitcount = ( + 53 if self.bigint_as_string else self.int_as_string_bitcount) if (_one_shot and c_make_encoder is not None and self.indent is None): _iterencode = c_make_encoder( @@ -324,17 +332,17 @@ class JSONEncoder(object): self.key_separator, self.item_separator, self.sort_keys, self.skipkeys, self.allow_nan, key_memo, self.use_decimal, self.namedtuple_as_object, self.tuple_as_array, - self.bigint_as_string, self.item_sort_key, - self.encoding, self.for_json, self.ignore_nan, - Decimal) + int_as_string_bitcount, + self.item_sort_key, self.encoding, self.for_json, + self.ignore_nan, Decimal) else: _iterencode = _make_iterencode( markers, self.default, _encoder, self.indent, floatstr, self.key_separator, self.item_separator, self.sort_keys, self.skipkeys, _one_shot, self.use_decimal, self.namedtuple_as_object, self.tuple_as_array, - self.bigint_as_string, self.item_sort_key, - self.encoding, self.for_json, + int_as_string_bitcount, + self.item_sort_key, self.encoding, self.for_json, Decimal=Decimal) try: return _iterencode(o, 0) @@ -372,7 +380,8 @@ class JSONEncoderForHTML(JSONEncoder): def _make_iterencode(markers, _default, _encoder, _indent, _floatstr, _key_separator, _item_separator, _sort_keys, _skipkeys, _one_shot, _use_decimal, _namedtuple_as_object, _tuple_as_array, - _bigint_as_string, _item_sort_key, _encoding, _for_json, + _int_as_string_bitcount, _item_sort_key, + _encoding,_for_json, ## HACK: hand-optimized bytecode; turn globals into locals _PY3=PY3, ValueError=ValueError, @@ -392,6 +401,26 @@ def _make_iterencode(markers, _default, _encoder, _indent, _floatstr, elif _sort_keys and not _item_sort_key: _item_sort_key = itemgetter(0) + if (_int_as_string_bitcount is not None and + (_int_as_string_bitcount <= 0 or + not isinstance(_int_as_string_bitcount, integer_types))): + raise TypeError("int_as_string_bitcount must be a positive integer") + + def _encode_int(value): + skip_quoting = ( + _int_as_string_bitcount is None + or + _int_as_string_bitcount < 1 + ) + if ( + skip_quoting or + (-1 << _int_as_string_bitcount) + < value < + (1 << _int_as_string_bitcount) + ): + return str(value) + return '"' + str(value) + '"' + def _iterencode_list(lst, _current_indent_level): if not lst: yield '[]' @@ -426,10 +455,7 @@ def _make_iterencode(markers, _default, _encoder, _indent, _floatstr, elif value is False: yield buf + 'false' elif isinstance(value, integer_types): - yield ((buf + str(value)) - if (not _bigint_as_string or - (-1 << 53) < value < (1 << 53)) - else (buf + '"' + str(value) + '"')) + yield buf + _encode_int(value) elif isinstance(value, float): yield buf + _floatstr(value) elif _use_decimal and isinstance(value, Decimal): @@ -540,10 +566,7 @@ def _make_iterencode(markers, _default, _encoder, _indent, _floatstr, elif value is False: yield 'false' elif isinstance(value, integer_types): - yield (str(value) - if (not _bigint_as_string or - (-1 << 53) < value < (1 << 53)) - else ('"' + str(value) + '"')) + yield _encode_int(value) elif isinstance(value, float): yield _floatstr(value) elif _use_decimal and isinstance(value, Decimal): @@ -585,10 +608,7 @@ def _make_iterencode(markers, _default, _encoder, _indent, _floatstr, elif o is False: yield 'false' elif isinstance(o, integer_types): - yield (str(o) - if (not _bigint_as_string or - (-1 << 53) < o < (1 << 53)) - else ('"' + str(o) + '"')) + yield _encode_int(o) elif isinstance(o, float): yield _floatstr(o) else: diff --git a/awx/lib/site-packages/simplejson/scanner.py b/awx/lib/site-packages/simplejson/scanner.py index b7918b3765..5abed357b3 100644 --- a/awx/lib/site-packages/simplejson/scanner.py +++ b/awx/lib/site-packages/simplejson/scanner.py @@ -118,6 +118,11 @@ def py_make_scanner(context): raise JSONDecodeError(errmsg, string, idx) def scan_once(string, idx): + if idx < 0: + # Ensure the same behavior as the C speedup, otherwise + # this would work for *some* negative string indices due + # to the behavior of __getitem__ for strings. #98 + raise JSONDecodeError('Expecting value', string, idx) try: return _scan_once(string, idx) finally: diff --git a/awx/lib/site-packages/simplejson/tests/__init__.py b/awx/lib/site-packages/simplejson/tests/__init__.py index c01dfcb915..c7551e820c 100644 --- a/awx/lib/site-packages/simplejson/tests/__init__.py +++ b/awx/lib/site-packages/simplejson/tests/__init__.py @@ -3,19 +3,16 @@ import unittest import doctest import sys -class OptionalExtensionTestSuite(unittest.TestSuite): + +class NoExtensionTestSuite(unittest.TestSuite): def run(self, result): import simplejson - run = unittest.TestSuite.run - run(self, result) - if simplejson._import_c_make_encoder() is None: - TestMissingSpeedups().run(result) - else: - simplejson._toggle_speedups(False) - run(self, result) - simplejson._toggle_speedups(True) + simplejson._toggle_speedups(False) + result = unittest.TestSuite.run(self, result) + simplejson._toggle_speedups(True) return result + class TestMissingSpeedups(unittest.TestCase): def runTest(self): if hasattr(sys, 'pypy_translation_info'): @@ -23,6 +20,7 @@ class TestMissingSpeedups(unittest.TestCase): elif hasattr(self, 'skipTest'): self.skipTest('_speedups.so is missing!') + def additional_tests(suite=None): import simplejson import simplejson.encoder @@ -36,34 +34,45 @@ def additional_tests(suite=None): def all_tests_suite(): - suite = unittest.TestLoader().loadTestsFromNames([ - 'simplejson.tests.test_bigint_as_string', - 'simplejson.tests.test_check_circular', - 'simplejson.tests.test_decode', - 'simplejson.tests.test_default', - 'simplejson.tests.test_dump', - 'simplejson.tests.test_encode_basestring_ascii', - 'simplejson.tests.test_encode_for_html', - 'simplejson.tests.test_errors', - 'simplejson.tests.test_fail', - 'simplejson.tests.test_float', - 'simplejson.tests.test_indent', - 'simplejson.tests.test_pass1', - 'simplejson.tests.test_pass2', - 'simplejson.tests.test_pass3', - 'simplejson.tests.test_recursion', - 'simplejson.tests.test_scanstring', - 'simplejson.tests.test_separators', - 'simplejson.tests.test_speedups', - 'simplejson.tests.test_unicode', - 'simplejson.tests.test_decimal', - 'simplejson.tests.test_tuple', - 'simplejson.tests.test_namedtuple', - 'simplejson.tests.test_tool', - 'simplejson.tests.test_for_json', - ]) - suite = additional_tests(suite) - return OptionalExtensionTestSuite([suite]) + def get_suite(): + return additional_tests( + unittest.TestLoader().loadTestsFromNames([ + 'simplejson.tests.test_bitsize_int_as_string', + 'simplejson.tests.test_bigint_as_string', + 'simplejson.tests.test_check_circular', + 'simplejson.tests.test_decode', + 'simplejson.tests.test_default', + 'simplejson.tests.test_dump', + 'simplejson.tests.test_encode_basestring_ascii', + 'simplejson.tests.test_encode_for_html', + 'simplejson.tests.test_errors', + 'simplejson.tests.test_fail', + 'simplejson.tests.test_float', + 'simplejson.tests.test_indent', + 'simplejson.tests.test_pass1', + 'simplejson.tests.test_pass2', + 'simplejson.tests.test_pass3', + 'simplejson.tests.test_recursion', + 'simplejson.tests.test_scanstring', + 'simplejson.tests.test_separators', + 'simplejson.tests.test_speedups', + 'simplejson.tests.test_unicode', + 'simplejson.tests.test_decimal', + 'simplejson.tests.test_tuple', + 'simplejson.tests.test_namedtuple', + 'simplejson.tests.test_tool', + 'simplejson.tests.test_for_json', + ])) + suite = get_suite() + import simplejson + if simplejson._import_c_make_encoder() is None: + suite.addTest(TestMissingSpeedups()) + else: + suite = unittest.TestSuite([ + suite, + NoExtensionTestSuite([get_suite()]), + ]) + return suite def main(): diff --git a/awx/lib/site-packages/simplejson/tests/test_bigint_as_string.py b/awx/lib/site-packages/simplejson/tests/test_bigint_as_string.py index 20ea64ce3d..2cf2cc2966 100644 --- a/awx/lib/site-packages/simplejson/tests/test_bigint_as_string.py +++ b/awx/lib/site-packages/simplejson/tests/test_bigint_as_string.py @@ -1,7 +1,7 @@ from unittest import TestCase import simplejson as json -from simplejson.compat import long_type + class TestBigintAsString(TestCase): # Python 2.5, at least the one that ships on Mac OS X, calculates @@ -15,44 +15,53 @@ class TestBigintAsString(TestCase): ((-1 << 53) - 1, '-9007199254740993'), ((-1 << 53) + 1, -9007199254740991)] + options = ( + {"bigint_as_string": True}, + {"int_as_string_bitcount": 53} + ) + def test_ints(self): - for val, expect in self.values: - self.assertEqual( - val, - json.loads(json.dumps(val))) - self.assertEqual( - expect, - json.loads(json.dumps(val, bigint_as_string=True))) + for opts in self.options: + for val, expect in self.values: + self.assertEqual( + val, + json.loads(json.dumps(val))) + self.assertEqual( + expect, + json.loads(json.dumps(val, **opts))) def test_lists(self): - for val, expect in self.values: - val = [val, val] - expect = [expect, expect] - self.assertEqual( - val, - json.loads(json.dumps(val))) - self.assertEqual( - expect, - json.loads(json.dumps(val, bigint_as_string=True))) + for opts in self.options: + for val, expect in self.values: + val = [val, val] + expect = [expect, expect] + self.assertEqual( + val, + json.loads(json.dumps(val))) + self.assertEqual( + expect, + json.loads(json.dumps(val, **opts))) def test_dicts(self): - for val, expect in self.values: - val = {'k': val} - expect = {'k': expect} - self.assertEqual( - val, - json.loads(json.dumps(val))) - self.assertEqual( - expect, - json.loads(json.dumps(val, bigint_as_string=True))) + for opts in self.options: + for val, expect in self.values: + val = {'k': val} + expect = {'k': expect} + self.assertEqual( + val, + json.loads(json.dumps(val))) + self.assertEqual( + expect, + json.loads(json.dumps(val, **opts))) def test_dict_keys(self): - for val, _ in self.values: - expect = {str(val): 'value'} - val = {val: 'value'} - self.assertEqual( - expect, - json.loads(json.dumps(val))) - self.assertEqual( - expect, - json.loads(json.dumps(val, bigint_as_string=True))) + for opts in self.options: + for val, _ in self.values: + expect = {str(val): 'value'} + val = {val: 'value'} + self.assertEqual( + expect, + json.loads(json.dumps(val))) + self.assertEqual( + expect, + json.loads(json.dumps(val, **opts))) diff --git a/awx/lib/site-packages/simplejson/tests/test_decode.py b/awx/lib/site-packages/simplejson/tests/test_decode.py index ea5c90afd1..30b692af28 100644 --- a/awx/lib/site-packages/simplejson/tests/test_decode.py +++ b/awx/lib/site-packages/simplejson/tests/test_decode.py @@ -86,3 +86,14 @@ class TestDecode(TestCase): self.assertEqual( ({'a': {}}, 11), cls().raw_decode(" \n{\"a\": {}}")) + + def test_bounds_checking(self): + # https://github.com/simplejson/simplejson/issues/98 + j = json.decoder.JSONDecoder() + for i in [4, 5, 6, -1, -2, -3, -4, -5, -6]: + self.assertRaises(ValueError, j.scan_once, '1234', i) + self.assertRaises(ValueError, j.raw_decode, '1234', i) + x, y = sorted(['128931233', '472389423'], key=id) + diff = id(x) - id(y) + self.assertRaises(ValueError, j.scan_once, y, diff) + self.assertRaises(ValueError, j.raw_decode, y, i) diff --git a/awx/lib/site-packages/simplejson/tests/test_speedups.py b/awx/lib/site-packages/simplejson/tests/test_speedups.py index 825ecf26f9..0a2b63bff4 100644 --- a/awx/lib/site-packages/simplejson/tests/test_speedups.py +++ b/awx/lib/site-packages/simplejson/tests/test_speedups.py @@ -1,20 +1,39 @@ +import sys +import unittest from unittest import TestCase from simplejson import encoder, scanner + def has_speedups(): return encoder.c_make_encoder is not None -class TestDecode(TestCase): - def test_make_scanner(self): + +def skip_if_speedups_missing(func): + def wrapper(*args, **kwargs): if not has_speedups(): - return + if hasattr(unittest, 'SkipTest'): + raise unittest.SkipTest("C Extension not available") + else: + sys.stdout.write("C Extension not available") + return + return func(*args, **kwargs) + + return wrapper + + +class TestDecode(TestCase): + @skip_if_speedups_missing + def test_make_scanner(self): self.assertRaises(AttributeError, scanner.c_make_scanner, 1) + @skip_if_speedups_missing def test_make_encoder(self): - if not has_speedups(): - return - self.assertRaises(TypeError, encoder.c_make_encoder, + self.assertRaises( + TypeError, + encoder.c_make_encoder, None, - "\xCD\x7D\x3D\x4E\x12\x4C\xF9\x79\xD7\x52\xBA\x82\xF2\x27\x4A\x7D\xA0\xCA\x75", - None) + ("\xCD\x7D\x3D\x4E\x12\x4C\xF9\x79\xD7" + "\x52\xBA\x82\xF2\x27\x4A\x7D\xA0\xCA\x75"), + None + ) diff --git a/awx/lib/site-packages/simplejson/tests/test_unicode.py b/awx/lib/site-packages/simplejson/tests/test_unicode.py index f04cc5c0ba..3b37f6599f 100644 --- a/awx/lib/site-packages/simplejson/tests/test_unicode.py +++ b/awx/lib/site-packages/simplejson/tests/test_unicode.py @@ -1,8 +1,9 @@ import sys +import codecs from unittest import TestCase import simplejson as json -from simplejson.compat import unichr, text_type, b, u +from simplejson.compat import unichr, text_type, b, u, BytesIO class TestUnicode(TestCase): def test_encoding1(self): @@ -143,3 +144,10 @@ class TestUnicode(TestCase): self.assertEqual( json.dumps(c, ensure_ascii=False), '"' + c + '"') + + def test_strip_bom(self): + content = u"\u3053\u3093\u306b\u3061\u308f" + json_doc = codecs.BOM_UTF8 + b(json.dumps(content)) + self.assertEqual(json.load(BytesIO(json_doc)), content) + for doc in json_doc, json_doc.decode('utf8'): + self.assertEqual(json.loads(doc), content)