From cec6bd5a59547dc97fe98975c570fc27a1e970be Mon Sep 17 00:00:00 2001 From: Aymeric Augustin Date: Sun, 29 Apr 2012 19:58:00 +0200 Subject: [PATCH] Fixed #18023 -- Removed bundled simplejson. And started the deprecation path for django.utils.simplejson. Thanks Alex Ogier, Clueless, and other contributors for their work on the patch. --- MANIFEST.in | 1 - .../formtools/wizard/storage/cookie.py | 3 +- django/contrib/gis/geometry/test_data.py | 4 +- django/contrib/messages/storage/cookie.py | 3 +- django/contrib/messages/tests/cookie.py | 3 +- django/core/serializers/json.py | 13 +- django/core/signing.py | 9 +- django/test/testcases.py | 7 +- django/utils/simplejson.py | 31 ++ django/utils/simplejson/LICENSE.txt | 19 - django/utils/simplejson/__init__.py | 354 -------------- django/utils/simplejson/decoder.py | 345 -------------- django/utils/simplejson/encoder.py | 430 ------------------ django/utils/simplejson/scanner.py | 65 --- django/utils/simplejson/tool.py | 35 -- docs/_ext/djangodocs.py | 14 +- docs/internals/deprecation.txt | 3 + docs/releases/1.5.txt | 13 +- docs/topics/class-based-views.txt | 2 +- docs/topics/serialization.txt | 23 +- tests/modeltests/field_subclassing/fields.py | 3 +- tests/modeltests/serializers/tests.py | 9 +- tests/regressiontests/file_uploads/tests.py | 19 +- tests/regressiontests/file_uploads/views.py | 8 +- .../test_client_regress/views.py | 6 +- tests/regressiontests/test_utils/tests.py | 4 +- 26 files changed, 105 insertions(+), 1321 deletions(-) create mode 100644 django/utils/simplejson.py delete mode 100644 django/utils/simplejson/LICENSE.txt delete mode 100644 django/utils/simplejson/__init__.py delete mode 100644 django/utils/simplejson/decoder.py delete mode 100644 django/utils/simplejson/encoder.py delete mode 100644 django/utils/simplejson/scanner.py delete mode 100644 django/utils/simplejson/tool.py diff --git a/MANIFEST.in b/MANIFEST.in index f94886506cc..2dde740b06f 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -6,7 +6,6 @@ include MANIFEST.in include django/contrib/gis/gdal/LICENSE include django/contrib/gis/geos/LICENSE include django/dispatch/license.txt -include django/utils/simplejson/LICENSE.txt recursive-include docs * recursive-include scripts * recursive-include extras * diff --git a/django/contrib/formtools/wizard/storage/cookie.py b/django/contrib/formtools/wizard/storage/cookie.py index d1776de89b4..e80361042f4 100644 --- a/django/contrib/formtools/wizard/storage/cookie.py +++ b/django/contrib/formtools/wizard/storage/cookie.py @@ -1,6 +1,7 @@ +import json + from django.core.exceptions import SuspiciousOperation from django.core.signing import BadSignature -from django.utils import simplejson as json from django.contrib.formtools.wizard import storage diff --git a/django/contrib/gis/geometry/test_data.py b/django/contrib/gis/geometry/test_data.py index 4e073487a5d..f92740248e5 100644 --- a/django/contrib/gis/geometry/test_data.py +++ b/django/contrib/gis/geometry/test_data.py @@ -3,10 +3,10 @@ This module has the mock object definitions used to hold reference geometry for the GEOS and GDAL tests. """ import gzip +import json import os from django.contrib import gis -from django.utils import simplejson # This global used to store reference geometry data. @@ -100,6 +100,6 @@ class TestDataMixin(object): if GEOMETRIES is None: # Load up the test geometry data from fixture into global. gzf = gzip.GzipFile(os.path.join(TEST_DATA, 'geometries.json.gz')) - geometries = simplejson.loads(gzf.read()) + geometries = json.loads(gzf.read()) GEOMETRIES = TestGeomSet(**strconvert(geometries)) return GEOMETRIES diff --git a/django/contrib/messages/storage/cookie.py b/django/contrib/messages/storage/cookie.py index c45dff4e439..07620050c7b 100644 --- a/django/contrib/messages/storage/cookie.py +++ b/django/contrib/messages/storage/cookie.py @@ -1,7 +1,8 @@ +import json + from django.conf import settings from django.contrib.messages.storage.base import BaseStorage, Message from django.http import SimpleCookie -from django.utils import simplejson as json from django.utils.crypto import salted_hmac, constant_time_compare diff --git a/django/contrib/messages/tests/cookie.py b/django/contrib/messages/tests/cookie.py index 3cba692286e..19d0e083849 100644 --- a/django/contrib/messages/tests/cookie.py +++ b/django/contrib/messages/tests/cookie.py @@ -1,10 +1,11 @@ +import json + from django.contrib.messages import constants from django.contrib.messages.tests.base import BaseTest from django.contrib.messages.storage.cookie import (CookieStorage, MessageEncoder, MessageDecoder) from django.contrib.messages.storage.base import Message from django.test.utils import override_settings -from django.utils import simplejson as json def set_cookie_data(storage, messages, invalid=False, encode_empty=False): diff --git a/django/core/serializers/json.py b/django/core/serializers/json.py index 91af84e9ca6..ab0cd0e5908 100644 --- a/django/core/serializers/json.py +++ b/django/core/serializers/json.py @@ -2,14 +2,17 @@ Serialize data to/from JSON """ +# Avoid shadowing the standard library json module +from __future__ import absolute_import + import datetime import decimal +import json from StringIO import StringIO from django.core.serializers.base import DeserializationError from django.core.serializers.python import Serializer as PythonSerializer from django.core.serializers.python import Deserializer as PythonDeserializer -from django.utils import simplejson from django.utils.timezone import is_aware class Serializer(PythonSerializer): @@ -19,10 +22,10 @@ class Serializer(PythonSerializer): internal_use_only = False def end_serialization(self): - if simplejson.__version__.split('.') >= ['2', '1', '3']: + if json.__version__.split('.') >= ['2', '1', '3']: # Use JS strings to represent Python Decimal instances (ticket #16850) self.options.update({'use_decimal': False}) - simplejson.dump(self.objects, self.stream, cls=DjangoJSONEncoder, **self.options) + json.dump(self.objects, self.stream, cls=DjangoJSONEncoder, **self.options) def getvalue(self): if callable(getattr(self.stream, 'getvalue', None)): @@ -38,7 +41,7 @@ def Deserializer(stream_or_string, **options): else: stream = stream_or_string try: - for obj in PythonDeserializer(simplejson.load(stream), **options): + for obj in PythonDeserializer(json.load(stream), **options): yield obj except GeneratorExit: raise @@ -47,7 +50,7 @@ def Deserializer(stream_or_string, **options): raise DeserializationError(e) -class DjangoJSONEncoder(simplejson.JSONEncoder): +class DjangoJSONEncoder(json.JSONEncoder): """ JSONEncoder subclass that knows how to encode date/time and decimal types. """ diff --git a/django/core/signing.py b/django/core/signing.py index 7f92d610985..f2c79de706e 100644 --- a/django/core/signing.py +++ b/django/core/signing.py @@ -33,12 +33,13 @@ There are 65 url-safe characters: the 64 used by url-safe base64 and the ':'. These functions make use of all of them. """ import base64 +import json import time import zlib from django.conf import settings from django.core.exceptions import ImproperlyConfigured -from django.utils import baseconv, simplejson +from django.utils import baseconv from django.utils.crypto import constant_time_compare, salted_hmac from django.utils.encoding import force_unicode, smart_str from django.utils.importlib import import_module @@ -89,14 +90,14 @@ def get_cookie_signer(salt='django.core.signing.get_cookie_signer'): class JSONSerializer(object): """ - Simple wrapper around simplejson to be used in signing.dumps and + Simple wrapper around json to be used in signing.dumps and signing.loads. """ def dumps(self, obj): - return simplejson.dumps(obj, separators=(',', ':')) + return json.dumps(obj, separators=(',', ':')) def loads(self, data): - return simplejson.loads(data) + return json.loads(data) def dumps(obj, key=None, salt='django.core.signing', serializer=JSONSerializer, compress=False): diff --git a/django/test/testcases.py b/django/test/testcases.py index d9048be98bc..b923bde1393 100644 --- a/django/test/testcases.py +++ b/django/test/testcases.py @@ -1,4 +1,5 @@ import difflib +import json import os import re import sys @@ -33,7 +34,7 @@ from django.test.signals import template_rendered from django.test.utils import (get_warnings_state, restore_warnings_state, override_settings) from django.test.utils import ContextList -from django.utils import simplejson, unittest as ut2 +from django.utils import unittest as ut2 from django.utils.encoding import smart_str, force_unicode from django.utils.unittest.util import safe_repr from django.views.static import serve @@ -189,8 +190,8 @@ class OutputChecker(doctest.OutputChecker): """ want, got = self._strip_quotes(want, got) try: - want_json = simplejson.loads(want) - got_json = simplejson.loads(got) + want_json = json.loads(want) + got_json = json.loads(got) except Exception: return False return want_json == got_json diff --git a/django/utils/simplejson.py b/django/utils/simplejson.py new file mode 100644 index 00000000000..a851a4f8ef8 --- /dev/null +++ b/django/utils/simplejson.py @@ -0,0 +1,31 @@ +# Django 1.5 only supports Python >= 2.6, where the standard library includes +# the json module. Previous version of Django shipped a copy for Python < 2.6. + +# For backwards compatibility, we're keeping an importable json module +# at this location, with the same lookup sequence. + +# Avoid shadowing the simplejson module +from __future__ import absolute_import + +import warnings +warnings.warn("django.utils.simplejson is deprecated; use json instead.", + PendingDeprecationWarning) + +try: + import simplejson +except ImportError: + use_simplejson = False +else: + # The system-installed version has priority providing it is either not an + # earlier version or it contains the C speedups. + from json import __version__ as stdlib_json_version + use_simplejson = (hasattr(simplejson, '_speedups') or + simplejson.__version__.split('.') >= stdlib_json_version.split('.')) + +# Make sure we copy over the version. See #17071 +if use_simplejson: + from simplejson import * + from simplejson import __version__ +else: + from json import * + from json import __version__ diff --git a/django/utils/simplejson/LICENSE.txt b/django/utils/simplejson/LICENSE.txt deleted file mode 100644 index ad95f29c172..00000000000 --- a/django/utils/simplejson/LICENSE.txt +++ /dev/null @@ -1,19 +0,0 @@ -Copyright (c) 2006 Bob Ippolito - -Permission is hereby granted, free of charge, to any person obtaining a copy of -this software and associated documentation files (the "Software"), to deal in -the Software without restriction, including without limitation the rights to -use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies -of the Software, and to permit persons to whom the Software is furnished to do -so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. diff --git a/django/utils/simplejson/__init__.py b/django/utils/simplejson/__init__.py deleted file mode 100644 index 2fb3733d115..00000000000 --- a/django/utils/simplejson/__init__.py +++ /dev/null @@ -1,354 +0,0 @@ -r"""JSON (JavaScript Object Notation) is a subset of -JavaScript syntax (ECMA-262 3rd edition) used as a lightweight data -interchange format. - -:mod:`simplejson` exposes an API familiar to users of the standard library -:mod:`marshal` and :mod:`pickle` modules. It is the externally maintained -version of the :mod:`json` library contained in Python 2.6, but maintains -compatibility with Python 2.4 and Python 2.5 and (currently) has -significant performance advantages, even without using the optional C -extension for speedups. - -Encoding basic Python object hierarchies:: - - >>> import simplejson as json - >>> json.dumps(['foo', {'bar': ('baz', None, 1.0, 2)}]) - '["foo", {"bar": ["baz", null, 1.0, 2]}]' - >>> print json.dumps("\"foo\bar") - "\"foo\bar" - >>> print json.dumps(u'\u1234') - "\u1234" - >>> print json.dumps('\\') - "\\" - >>> print json.dumps({"c": 0, "b": 0, "a": 0}, sort_keys=True) - {"a": 0, "b": 0, "c": 0} - >>> from StringIO import StringIO - >>> io = StringIO() - >>> json.dump(['streaming API'], io) - >>> io.getvalue() - '["streaming API"]' - -Compact encoding:: - - >>> import simplejson as json - >>> json.dumps([1,2,3,{'4': 5, '6': 7}], separators=(',',':')) - '[1,2,3,{"4":5,"6":7}]' - -Pretty printing:: - - >>> import simplejson as json - >>> s = json.dumps({'4': 5, '6': 7}, sort_keys=True, indent=4) - >>> print '\n'.join([l.rstrip() for l in s.splitlines()]) - { - "4": 5, - "6": 7 - } - -Decoding JSON:: - - >>> import simplejson as json - >>> obj = [u'foo', {u'bar': [u'baz', None, 1.0, 2]}] - >>> json.loads('["foo", {"bar":["baz", null, 1.0, 2]}]') == obj - True - >>> json.loads('"\\"foo\\bar"') == u'"foo\x08ar' - True - >>> from StringIO import StringIO - >>> io = StringIO('["streaming API"]') - >>> json.load(io)[0] == 'streaming API' - True - -Specializing JSON object decoding:: - - >>> import simplejson as json - >>> def as_complex(dct): - ... if '__complex__' in dct: - ... return complex(dct['real'], dct['imag']) - ... return dct - ... - >>> json.loads('{"__complex__": true, "real": 1, "imag": 2}', - ... object_hook=as_complex) - (1+2j) - >>> import decimal - >>> json.loads('1.1', parse_float=decimal.Decimal) == decimal.Decimal('1.1') - True - -Specializing JSON object encoding:: - - >>> import simplejson as json - >>> def encode_complex(obj): - ... if isinstance(obj, complex): - ... return [obj.real, obj.imag] - ... raise TypeError("%r is not JSON serializable" % (o,)) - ... - >>> json.dumps(2 + 1j, default=encode_complex) - '[2.0, 1.0]' - >>> json.JSONEncoder(default=encode_complex).encode(2 + 1j) - '[2.0, 1.0]' - >>> ''.join(json.JSONEncoder(default=encode_complex).iterencode(2 + 1j)) - '[2.0, 1.0]' - - -Using simplejson.tool from the shell to validate and pretty-print:: - - $ echo '{"json":"obj"}' | python -msimplejson.tool - { - "json": "obj" - } - $ echo '{ 1.2:3.4}' | python -msimplejson.tool - Expecting property name: line 1 column 2 (char 2) -""" - -# Django modification: try to use the system version first, providing it's -# either of a later version of has the C speedups in place. Otherwise, fall -# back to our local copy. - -__version__ = '2.0.7' - -use_system_version = False -try: - # The system-installed version has priority providing it is either not an - # earlier version or it contains the C speedups. - import simplejson - if (simplejson.__version__.split('.') >= __version__.split('.') or - hasattr(simplejson, '_speedups')): - from simplejson import * - use_system_version = True - # Make sure we copy over the version. See #17071 - __version__ = simplejson.__version__ -except ImportError: - pass - -if not use_system_version: - try: - from json import * # Python 2.6 preferred over local copy. - - # There is a "json" package around that is not Python's "json", so we - # check for something that is only in the namespace of the version we - # want. - JSONDecoder - - use_system_version = True - # Make sure we copy over the version. See #17071 - from json import __version__ as json_version - __version__ = json_version - except (ImportError, NameError): - pass - -# If all else fails, we have a bundled version that can be used. -if not use_system_version: - __all__ = [ - 'dump', 'dumps', 'load', 'loads', - 'JSONDecoder', 'JSONEncoder', - ] - - from django.utils.simplejson.decoder import JSONDecoder - from django.utils.simplejson.encoder import JSONEncoder - - _default_encoder = JSONEncoder( - skipkeys=False, - ensure_ascii=True, - check_circular=True, - allow_nan=True, - indent=None, - separators=None, - encoding='utf-8', - default=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, **kw): - """Serialize ``obj`` as a JSON formatted stream to ``fp`` (a - ``.write()``-supporting file-like object). - - If ``skipkeys`` is ``True`` then ``dict`` keys that are not basic types - (``str``, ``unicode``, ``int``, ``long``, ``float``, ``bool``, ``None``) - will be skipped instead of raising a ``TypeError``. - - If ``ensure_ascii`` is ``False``, then the some chunks written to ``fp`` - may be ``unicode`` instances, subject to normal Python ``str`` to - ``unicode`` coercion rules. Unless ``fp.write()`` explicitly - understands ``unicode`` (as in ``codecs.getwriter()``) this is likely - to cause an error. - - If ``check_circular`` is ``False``, then the circular reference check - for container types will be skipped and a circular reference will - result in an ``OverflowError`` (or worse). - - If ``allow_nan`` is ``False``, then it will be a ``ValueError`` to - serialize out of range ``float`` values (``nan``, ``inf``, ``-inf``) - in strict compliance of the JSON specification, instead of using the - JavaScript equivalents (``NaN``, ``Infinity``, ``-Infinity``). - - If ``indent`` is a non-negative integer, then JSON array elements and object - members will be pretty-printed with that indent level. An indent level - of 0 will only insert newlines. ``None`` is the most compact representation. - - If ``separators`` is an ``(item_separator, dict_separator)`` tuple - then it will be used instead of the default ``(', ', ': ')`` separators. - ``(',', ':')`` is the most compact JSON representation. - - ``encoding`` is the character encoding for str instances, default is UTF-8. - - ``default(obj)`` is a function that should return a serializable version - of obj or raise TypeError. The default simply raises TypeError. - - To use a custom ``JSONEncoder`` subclass (e.g. one that overrides the - ``.default()`` method to serialize additional types), specify it with - the ``cls`` kwarg. - - """ - # cached encoder - if (skipkeys is False and ensure_ascii is True and - check_circular is True and allow_nan is True and - cls is None and indent is None and separators is None and - encoding == 'utf-8' and default is None and not kw): - iterable = _default_encoder.iterencode(obj) - else: - if cls is None: - cls = JSONEncoder - iterable = cls(skipkeys=skipkeys, ensure_ascii=ensure_ascii, - check_circular=check_circular, allow_nan=allow_nan, indent=indent, - separators=separators, encoding=encoding, - default=default, **kw).iterencode(obj) - # could accelerate with writelines in some versions of Python, at - # a debuggability cost - for chunk in iterable: - fp.write(chunk) - - - 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, **kw): - """Serialize ``obj`` to a JSON formatted ``str``. - - If ``skipkeys`` is ``True`` then ``dict`` keys that are not basic types - (``str``, ``unicode``, ``int``, ``long``, ``float``, ``bool``, ``None``) - will be skipped instead of raising a ``TypeError``. - - If ``ensure_ascii`` is ``False``, then the return value will be a - ``unicode`` instance subject to normal Python ``str`` to ``unicode`` - coercion rules instead of being escaped to an ASCII ``str``. - - If ``check_circular`` is ``False``, then the circular reference check - for container types will be skipped and a circular reference will - result in an ``OverflowError`` (or worse). - - If ``allow_nan`` is ``False``, then it will be a ``ValueError`` to - serialize out of range ``float`` values (``nan``, ``inf``, ``-inf``) in - strict compliance of the JSON specification, instead of using the - JavaScript equivalents (``NaN``, ``Infinity``, ``-Infinity``). - - If ``indent`` is a non-negative integer, then JSON array elements and - object members will be pretty-printed with that indent level. An indent - level of 0 will only insert newlines. ``None`` is the most compact - representation. - - If ``separators`` is an ``(item_separator, dict_separator)`` tuple - then it will be used instead of the default ``(', ', ': ')`` separators. - ``(',', ':')`` is the most compact JSON representation. - - ``encoding`` is the character encoding for str instances, default is UTF-8. - - ``default(obj)`` is a function that should return a serializable version - of obj or raise TypeError. The default simply raises TypeError. - - To use a custom ``JSONEncoder`` subclass (e.g. one that overrides the - ``.default()`` method to serialize additional types), specify it with - the ``cls`` kwarg. - - """ - # cached encoder - if (skipkeys is False and ensure_ascii is True and - check_circular is True and allow_nan is True and - cls is None and indent is None and separators is None and - encoding == 'utf-8' and default is None and not kw): - return _default_encoder.encode(obj) - if cls is None: - cls = JSONEncoder - return cls( - skipkeys=skipkeys, ensure_ascii=ensure_ascii, - check_circular=check_circular, allow_nan=allow_nan, indent=indent, - separators=separators, encoding=encoding, default=default, - **kw).encode(obj) - - - _default_decoder = JSONDecoder(encoding=None, object_hook=None) - - - def load(fp, encoding=None, cls=None, object_hook=None, parse_float=None, - parse_int=None, parse_constant=None, **kw): - """Deserialize ``fp`` (a ``.read()``-supporting file-like object containing - a JSON document) to a Python object. - - If the contents of ``fp`` is encoded with an ASCII based encoding other - than utf-8 (e.g. latin-1), then an appropriate ``encoding`` name must - be specified. Encodings that are not ASCII based (such as UCS-2) are - not allowed, and should be wrapped with - ``codecs.getreader(fp)(encoding)``, or simply decoded to a ``unicode`` - object and passed to ``loads()`` - - ``object_hook`` is an optional function that will be called with the - result of any object literal decode (a ``dict``). The return value of - ``object_hook`` will be used instead of the ``dict``. This feature - can be used to implement custom decoders (e.g. JSON-RPC class hinting). - - To use a custom ``JSONDecoder`` subclass, specify it with the ``cls`` - kwarg. - - """ - return loads(fp.read(), - encoding=encoding, cls=cls, object_hook=object_hook, - parse_float=parse_float, parse_int=parse_int, - parse_constant=parse_constant, **kw) - - - def loads(s, encoding=None, cls=None, object_hook=None, parse_float=None, - parse_int=None, parse_constant=None, **kw): - """Deserialize ``s`` (a ``str`` or ``unicode`` instance containing a JSON - document) to a Python object. - - If ``s`` is a ``str`` instance and is encoded with an ASCII based encoding - other than utf-8 (e.g. latin-1) then an appropriate ``encoding`` name - must be specified. Encodings that are not ASCII based (such as UCS-2) - are not allowed and should be decoded to ``unicode`` first. - - ``object_hook`` is an optional function that will be called with the - result of any object literal decode (a ``dict``). The return value of - ``object_hook`` will be used instead of the ``dict``. This feature - can be used to implement custom decoders (e.g. JSON-RPC class hinting). - - ``parse_float``, if specified, will be called with the string - of every JSON float to be decoded. By default this is equivalent to - float(num_str). This can be used to use another datatype or parser - for JSON floats (e.g. decimal.Decimal). - - ``parse_int``, if specified, will be called with the string - of every JSON int to be decoded. By default this is equivalent to - int(num_str). This can be used to use another datatype or parser - for JSON integers (e.g. float). - - ``parse_constant``, if specified, will be called with one of the - following strings: -Infinity, Infinity, NaN, null, true, false. - This can be used to raise an exception if invalid JSON numbers - are encountered. - - To use a custom ``JSONDecoder`` subclass, specify it with the ``cls`` - kwarg. - - """ - if (cls is None and encoding is None and object_hook is None and - parse_int is None and parse_float is None and - parse_constant is None and not kw): - return _default_decoder.decode(s) - if cls is None: - cls = JSONDecoder - if object_hook is not None: - kw['object_hook'] = object_hook - if parse_float is not None: - kw['parse_float'] = parse_float - if parse_int is not None: - kw['parse_int'] = parse_int - if parse_constant is not None: - kw['parse_constant'] = parse_constant - return cls(encoding=encoding, **kw).decode(s) diff --git a/django/utils/simplejson/decoder.py b/django/utils/simplejson/decoder.py deleted file mode 100644 index 5a845ccfdc7..00000000000 --- a/django/utils/simplejson/decoder.py +++ /dev/null @@ -1,345 +0,0 @@ -"""Implementation of JSONDecoder -""" -import re -import sys -import struct - -from django.utils.simplejson.scanner import make_scanner -c_scanstring = None - -__all__ = ['JSONDecoder'] - -FLAGS = re.VERBOSE | re.MULTILINE | re.DOTALL - -def _floatconstants(): - _BYTES = '7FF80000000000007FF0000000000000'.decode('hex') - if sys.byteorder != 'big': - _BYTES = _BYTES[:8][::-1] + _BYTES[8:][::-1] - nan, inf = struct.unpack('dd', _BYTES) - return nan, inf, -inf - -NaN, PosInf, NegInf = _floatconstants() - - -def linecol(doc, pos): - lineno = doc.count('\n', 0, pos) + 1 - if lineno == 1: - colno = pos - else: - colno = pos - doc.rindex('\n', 0, pos) - return lineno, colno - - -def errmsg(msg, doc, pos, end=None): - # Note that this function is called from _speedups - lineno, colno = linecol(doc, pos) - if end is None: - return '%s: line %d column %d (char %d)' % (msg, lineno, colno, pos) - endlineno, endcolno = linecol(doc, end) - return '%s: line %d column %d - line %d column %d (char %d - %d)' % ( - msg, lineno, colno, endlineno, endcolno, pos, end) - - -_CONSTANTS = { - '-Infinity': NegInf, - 'Infinity': PosInf, - 'NaN': NaN, -} - -STRINGCHUNK = re.compile(r'(.*?)(["\\\x00-\x1f])', FLAGS) -BACKSLASH = { - '"': u'"', '\\': u'\\', '/': u'/', - 'b': u'\b', 'f': u'\f', 'n': u'\n', 'r': u'\r', 't': u'\t', -} - -DEFAULT_ENCODING = "utf-8" - -def py_scanstring(s, end, encoding=None, strict=True, _b=BACKSLASH, _m=STRINGCHUNK.match): - """Scan the string s for a JSON string. End is the index of the - character in s after the quote that started the JSON string. - Unescapes all valid JSON string escape sequences and raises ValueError - on attempt to decode an invalid string. If strict is False then literal - control characters are allowed in the string. - - Returns a tuple of the decoded string and the index of the character in s - after the end quote.""" - if encoding is None: - encoding = DEFAULT_ENCODING - chunks = [] - _append = chunks.append - begin = end - 1 - while 1: - chunk = _m(s, end) - if chunk is None: - raise ValueError( - errmsg("Unterminated string starting at", s, begin)) - end = chunk.end() - content, terminator = chunk.groups() - # Content is contains zero or more unescaped string characters - if content: - if not isinstance(content, unicode): - content = unicode(content, encoding) - _append(content) - # Terminator is the end of string, a literal control character, - # or a backslash denoting that an escape sequence follows - if terminator == '"': - break - elif terminator != '\\': - if strict: - msg = "Invalid control character %r at" % (terminator,) - raise ValueError(msg, s, end) - else: - _append(terminator) - continue - try: - esc = s[end] - except IndexError: - raise ValueError( - errmsg("Unterminated string starting at", s, begin)) - # If not a unicode escape sequence, must be in the lookup table - if esc != 'u': - try: - char = _b[esc] - except KeyError: - raise ValueError( - errmsg("Invalid \\escape: %r" % (esc,), s, end)) - end += 1 - else: - # Unicode escape sequence - esc = s[end + 1:end + 5] - next_end = end + 5 - if len(esc) != 4: - msg = "Invalid \\uXXXX escape" - raise ValueError(errmsg(msg, s, end)) - uni = int(esc, 16) - # Check for surrogate pair on UCS-4 systems - if 0xd800 <= uni <= 0xdbff and sys.maxunicode > 65535: - msg = "Invalid \\uXXXX\\uXXXX surrogate pair" - if not s[end + 5:end + 7] == '\\u': - raise ValueError(errmsg(msg, s, end)) - esc2 = s[end + 7:end + 11] - if len(esc2) != 4: - raise ValueError(errmsg(msg, s, end)) - uni2 = int(esc2, 16) - uni = 0x10000 + (((uni - 0xd800) << 10) | (uni2 - 0xdc00)) - next_end += 6 - char = unichr(uni) - end = next_end - # Append the unescaped character - _append(char) - return u''.join(chunks), end - - -# Use speedup if available -scanstring = c_scanstring or py_scanstring - -WHITESPACE = re.compile(r'[ \t\n\r]*', FLAGS) -WHITESPACE_STR = ' \t\n\r' - -def JSONObject((s, end), encoding, strict, scan_once, object_hook, _w=WHITESPACE.match, _ws=WHITESPACE_STR): - pairs = {} - # Use a slice to prevent IndexError from being raised, the following - # check will raise a more specific ValueError if the string is empty - nextchar = s[end:end + 1] - # Normally we expect nextchar == '"' - if nextchar != '"': - if nextchar in _ws: - end = _w(s, end).end() - nextchar = s[end:end + 1] - # Trivial empty object - if nextchar == '}': - return pairs, end + 1 - elif nextchar != '"': - raise ValueError(errmsg("Expecting property name", s, end)) - end += 1 - while True: - key, end = scanstring(s, end, encoding, strict) - - # To skip some function call overhead we optimize the fast paths where - # the JSON key separator is ": " or just ":". - if s[end:end + 1] != ':': - end = _w(s, end).end() - if s[end:end + 1] != ':': - raise ValueError(errmsg("Expecting : delimiter", s, end)) - - end += 1 - - try: - if s[end] in _ws: - end += 1 - if s[end] in _ws: - end = _w(s, end + 1).end() - except IndexError: - pass - - try: - value, end = scan_once(s, end) - except StopIteration: - raise ValueError(errmsg("Expecting object", s, end)) - pairs[key] = value - - try: - nextchar = s[end] - if nextchar in _ws: - end = _w(s, end + 1).end() - nextchar = s[end] - except IndexError: - nextchar = '' - end += 1 - - if nextchar == '}': - break - elif nextchar != ',': - raise ValueError(errmsg("Expecting , delimiter", s, end - 1)) - - try: - nextchar = s[end] - if nextchar in _ws: - end += 1 - nextchar = s[end] - if nextchar in _ws: - end = _w(s, end + 1).end() - nextchar = s[end] - except IndexError: - nextchar = '' - - end += 1 - if nextchar != '"': - raise ValueError(errmsg("Expecting property name", s, end - 1)) - - if object_hook is not None: - pairs = object_hook(pairs) - return pairs, end - -def JSONArray((s, end), scan_once, _w=WHITESPACE.match, _ws=WHITESPACE_STR): - values = [] - nextchar = s[end:end + 1] - if nextchar in _ws: - end = _w(s, end + 1).end() - nextchar = s[end:end + 1] - # Look-ahead for trivial empty array - if nextchar == ']': - return values, end + 1 - _append = values.append - while True: - try: - value, end = scan_once(s, end) - except StopIteration: - raise ValueError(errmsg("Expecting object", s, end)) - _append(value) - nextchar = s[end:end + 1] - if nextchar in _ws: - end = _w(s, end + 1).end() - nextchar = s[end:end + 1] - end += 1 - if nextchar == ']': - break - elif nextchar != ',': - raise ValueError(errmsg("Expecting , delimiter", s, end)) - - try: - if s[end] in _ws: - end += 1 - if s[end] in _ws: - end = _w(s, end + 1).end() - except IndexError: - pass - - return values, end - -class JSONDecoder(object): - """Simple JSON decoder - - Performs the following translations in decoding by default: - - +---------------+-------------------+ - | JSON | Python | - +===============+===================+ - | object | dict | - +---------------+-------------------+ - | array | list | - +---------------+-------------------+ - | string | unicode | - +---------------+-------------------+ - | number (int) | int, long | - +---------------+-------------------+ - | number (real) | float | - +---------------+-------------------+ - | true | True | - +---------------+-------------------+ - | false | False | - +---------------+-------------------+ - | null | None | - +---------------+-------------------+ - - It also understands ``NaN``, ``Infinity``, and ``-Infinity`` as - their corresponding ``float`` values, which is outside the JSON spec. - - """ - - def __init__(self, encoding=None, object_hook=None, parse_float=None, - parse_int=None, parse_constant=None, strict=True): - """``encoding`` determines the encoding used to interpret any ``str`` - objects decoded by this instance (utf-8 by default). It has no - effect when decoding ``unicode`` objects. - - Note that currently only encodings that are a superset of ASCII work, - strings of other encodings should be passed in as ``unicode``. - - ``object_hook``, if specified, will be called with the result - of every JSON object decoded and its return value will be used in - place of the given ``dict``. This can be used to provide custom - deserializations (e.g. to support JSON-RPC class hinting). - - ``parse_float``, if specified, will be called with the string - of every JSON float to be decoded. By default this is equivalent to - float(num_str). This can be used to use another datatype or parser - for JSON floats (e.g. decimal.Decimal). - - ``parse_int``, if specified, will be called with the string - of every JSON int to be decoded. By default this is equivalent to - int(num_str). This can be used to use another datatype or parser - for JSON integers (e.g. float). - - ``parse_constant``, if specified, will be called with one of the - following strings: -Infinity, Infinity, NaN. - This can be used to raise an exception if invalid JSON numbers - are encountered. - - """ - self.encoding = encoding - self.object_hook = object_hook - self.parse_float = parse_float or float - self.parse_int = parse_int or int - self.parse_constant = parse_constant or _CONSTANTS.__getitem__ - self.strict = strict - self.parse_object = JSONObject - self.parse_array = JSONArray - self.parse_string = scanstring - self.scan_once = make_scanner(self) - - def decode(self, s, _w=WHITESPACE.match): - """Return the Python representation of ``s`` (a ``str`` or ``unicode`` - instance containing a JSON document) - - """ - obj, end = self.raw_decode(s, idx=_w(s, 0).end()) - end = _w(s, end).end() - if end != len(s): - raise ValueError(errmsg("Extra data", s, end, len(s))) - return obj - - def raw_decode(self, s, idx=0): - """Decode a JSON document from ``s`` (a ``str`` or ``unicode`` beginning - with a JSON document) and return a 2-tuple of the Python - representation and the index in ``s`` where the document ended. - - This can be used to decode a JSON document from a string that may - have extraneous data at the end. - - """ - try: - obj, end = self.scan_once(s, idx) - except StopIteration: - raise ValueError("No JSON object could be decoded") - return obj, end diff --git a/django/utils/simplejson/encoder.py b/django/utils/simplejson/encoder.py deleted file mode 100644 index 06ebe62a3c2..00000000000 --- a/django/utils/simplejson/encoder.py +++ /dev/null @@ -1,430 +0,0 @@ -"""Implementation of JSONEncoder -""" -import re - -c_encode_basestring_ascii = None -c_make_encoder = None - -ESCAPE = re.compile(r'[\x00-\x1f\\"\b\f\n\r\t]') -ESCAPE_ASCII = re.compile(r'([\\"]|[^\ -~])') -HAS_UTF8 = re.compile(r'[\x80-\xff]') -ESCAPE_DCT = { - '\\': '\\\\', - '"': '\\"', - '\b': '\\b', - '\f': '\\f', - '\n': '\\n', - '\r': '\\r', - '\t': '\\t', -} -for i in range(0x20): - ESCAPE_DCT.setdefault(chr(i), '\\u%04x' % (i,)) - -# Assume this produces an infinity on all machines (probably not guaranteed) -INFINITY = float('1e66666') -FLOAT_REPR = repr - -def encode_basestring(s): - """Return a JSON representation of a Python string - - """ - def replace(match): - return ESCAPE_DCT[match.group(0)] - return '"' + ESCAPE.sub(replace, s) + '"' - - -def py_encode_basestring_ascii(s): - """Return an ASCII-only JSON representation of a Python string - - """ - if isinstance(s, str) and HAS_UTF8.search(s) is not None: - s = s.decode('utf-8') - def replace(match): - s = match.group(0) - try: - return ESCAPE_DCT[s] - except KeyError: - n = ord(s) - if n < 0x10000: - return '\\u%04x' % (n,) - else: - # surrogate pair - n -= 0x10000 - s1 = 0xd800 | ((n >> 10) & 0x3ff) - s2 = 0xdc00 | (n & 0x3ff) - return '\\u%04x\\u%04x' % (s1, s2) - return '"' + str(ESCAPE_ASCII.sub(replace, s)) + '"' - - -encode_basestring_ascii = c_encode_basestring_ascii or py_encode_basestring_ascii - -class JSONEncoder(object): - """Extensible JSON encoder for Python data structures. - - Supports the following objects and types by default: - - +-------------------+---------------+ - | Python | JSON | - +===================+===============+ - | dict | object | - +-------------------+---------------+ - | list, tuple | array | - +-------------------+---------------+ - | str, unicode | string | - +-------------------+---------------+ - | int, long, float | number | - +-------------------+---------------+ - | True | true | - +-------------------+---------------+ - | False | false | - +-------------------+---------------+ - | None | null | - +-------------------+---------------+ - - To extend this to recognize other objects, subclass and implement a - ``.default()`` method with another method that returns a serializable - object for ``o`` if possible, otherwise it should call the superclass - implementation (to raise ``TypeError``). - - """ - 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): - """Constructor for JSONEncoder, with sensible defaults. - - If skipkeys is False, then it is a TypeError to attempt - encoding of keys that are not str, int, long, float or None. If - skipkeys is True, such items are simply skipped. - - If ensure_ascii is True, the output is guaranteed to be str - objects with all incoming unicode characters escaped. If - ensure_ascii is false, the output will be unicode object. - - If check_circular is True, then lists, dicts, and custom encoded - objects will be checked for circular references during encoding to - prevent an infinite recursion (which would cause an OverflowError). - Otherwise, no such check takes place. - - If allow_nan is True, then NaN, Infinity, and -Infinity will be - encoded as such. This behavior is not JSON specification compliant, - but is consistent with most JavaScript based encoders and decoders. - Otherwise, it will be a ValueError to encode such floats. - - If sort_keys is True, then the output of dictionaries will be - sorted by key; this is useful for regression tests to ensure - that JSON serializations can be compared on a day-to-day basis. - - If indent is a non-negative integer, then JSON array - elements and object members will be pretty-printed with that - indent level. An indent level of 0 will only insert newlines. - None is the most compact representation. - - If specified, separators should be a (item_separator, key_separator) - tuple. The default is (', ', ': '). To get the most compact JSON - representation you should specify (',', ':') to eliminate whitespace. - - If specified, default is a function that gets called for objects - that can't otherwise be serialized. It should return a JSON encodable - version of the object or raise a ``TypeError``. - - If encoding is not None, then all input strings will be - transformed into unicode using that encoding prior to JSON-encoding. - The default is UTF-8. - - """ - - self.skipkeys = skipkeys - self.ensure_ascii = ensure_ascii - self.check_circular = check_circular - self.allow_nan = allow_nan - self.sort_keys = sort_keys - self.indent = indent - if separators is not None: - self.item_separator, self.key_separator = separators - if default is not None: - self.default = default - self.encoding = encoding - - def default(self, o): - """Implement this method in a subclass such that it returns - a serializable object for ``o``, or calls the base implementation - (to raise a ``TypeError``). - - For example, to support arbitrary iterators, you could - implement default like this:: - - def default(self, o): - try: - iterable = iter(o) - except TypeError: - pass - else: - return list(iterable) - return JSONEncoder.default(self, o) - - """ - raise TypeError("%r is not JSON serializable" % (o,)) - - def encode(self, o): - """Return a JSON string representation of a Python data structure. - - >>> JSONEncoder().encode({"foo": ["bar", "baz"]}) - '{"foo": ["bar", "baz"]}' - - """ - # This is for extremely simple cases and benchmarks. - if isinstance(o, basestring): - if isinstance(o, str): - _encoding = self.encoding - if (_encoding is not None - and not (_encoding == 'utf-8')): - o = o.decode(_encoding) - if self.ensure_ascii: - return encode_basestring_ascii(o) - else: - return encode_basestring(o) - # This doesn't pass the iterator directly to ''.join() because the - # exceptions aren't as detailed. The list call should be roughly - # equivalent to the PySequence_Fast that ''.join() would do. - chunks = self.iterencode(o, _one_shot=True) - if not isinstance(chunks, (list, tuple)): - chunks = list(chunks) - return ''.join(chunks) - - def iterencode(self, o, _one_shot=False): - """Encode the given object and yield each string - representation as available. - - For example:: - - for chunk in JSONEncoder().iterencode(bigobject): - mysocket.write(chunk) - - """ - if self.check_circular: - markers = {} - else: - markers = None - if self.ensure_ascii: - _encoder = encode_basestring_ascii - else: - _encoder = encode_basestring - if self.encoding != 'utf-8': - def _encoder(o, _orig_encoder=_encoder, _encoding=self.encoding): - if isinstance(o, str): - o = o.decode(_encoding) - return _orig_encoder(o) - - def floatstr(o, allow_nan=self.allow_nan, _repr=FLOAT_REPR, _inf=INFINITY, _neginf=-INFINITY): - # Check for specials. Note that this type of test is processor- and/or - # platform-specific, so do tests which don't depend on the internals. - - if o != o: - text = 'NaN' - elif o == _inf: - text = 'Infinity' - elif o == _neginf: - text = '-Infinity' - else: - return _repr(o) - - if not allow_nan: - raise ValueError("Out of range float values are not JSON compliant: %r" - % (o,)) - - return text - - - if _one_shot and c_make_encoder is not None and not self.indent and not self.sort_keys: - _iterencode = c_make_encoder( - markers, self.default, _encoder, self.indent, - self.key_separator, self.item_separator, self.sort_keys, - self.skipkeys, self.allow_nan) - else: - _iterencode = _make_iterencode( - markers, self.default, _encoder, self.indent, floatstr, - self.key_separator, self.item_separator, self.sort_keys, - self.skipkeys, _one_shot) - return _iterencode(o, 0) - -def _make_iterencode(markers, _default, _encoder, _indent, _floatstr, _key_separator, _item_separator, _sort_keys, _skipkeys, _one_shot, - ## HACK: hand-optimized bytecode; turn globals into locals - False=False, - True=True, - ValueError=ValueError, - basestring=basestring, - dict=dict, - float=float, - id=id, - int=int, - isinstance=isinstance, - list=list, - long=long, - str=str, - tuple=tuple, - ): - - def _iterencode_list(lst, _current_indent_level): - if not lst: - yield '[]' - return - if markers is not None: - markerid = id(lst) - if markerid in markers: - raise ValueError("Circular reference detected") - markers[markerid] = lst - buf = '[' - if _indent is not None: - _current_indent_level += 1 - newline_indent = '\n' + (' ' * (_indent * _current_indent_level)) - separator = _item_separator + newline_indent - buf += newline_indent - else: - newline_indent = None - separator = _item_separator - first = True - for value in lst: - if first: - first = False - else: - buf = separator - if isinstance(value, basestring): - yield buf + _encoder(value) - elif value is None: - yield buf + 'null' - elif value is True: - yield buf + 'true' - elif value is False: - yield buf + 'false' - elif isinstance(value, (int, long)): - yield buf + str(value) - elif isinstance(value, float): - yield buf + _floatstr(value) - else: - yield buf - if isinstance(value, (list, tuple)): - chunks = _iterencode_list(value, _current_indent_level) - elif isinstance(value, dict): - chunks = _iterencode_dict(value, _current_indent_level) - else: - chunks = _iterencode(value, _current_indent_level) - for chunk in chunks: - yield chunk - if newline_indent is not None: - _current_indent_level -= 1 - yield '\n' + (' ' * (_indent * _current_indent_level)) - yield ']' - if markers is not None: - del markers[markerid] - - def _iterencode_dict(dct, _current_indent_level): - if not dct: - yield '{}' - return - if markers is not None: - markerid = id(dct) - if markerid in markers: - raise ValueError("Circular reference detected") - markers[markerid] = dct - yield '{' - if _indent is not None: - _current_indent_level += 1 - newline_indent = '\n' + (' ' * (_indent * _current_indent_level)) - item_separator = _item_separator + newline_indent - yield newline_indent - else: - newline_indent = None - item_separator = _item_separator - first = True - if _sort_keys: - items = dct.items() - items.sort(key=lambda kv: kv[0]) - else: - items = dct.iteritems() - for key, value in items: - if isinstance(key, basestring): - pass - # JavaScript is weakly typed for these, so it makes sense to - # also allow them. Many encoders seem to do something like this. - elif isinstance(key, float): - key = _floatstr(key) - elif isinstance(key, (int, long)): - key = str(key) - elif key is True: - key = 'true' - elif key is False: - key = 'false' - elif key is None: - key = 'null' - elif _skipkeys: - continue - else: - raise TypeError("key %r is not a string" % (key,)) - if first: - first = False - else: - yield item_separator - yield _encoder(key) - yield _key_separator - if isinstance(value, basestring): - yield _encoder(value) - elif value is None: - yield 'null' - elif value is True: - yield 'true' - elif value is False: - yield 'false' - elif isinstance(value, (int, long)): - yield str(value) - elif isinstance(value, float): - yield _floatstr(value) - else: - if isinstance(value, (list, tuple)): - chunks = _iterencode_list(value, _current_indent_level) - elif isinstance(value, dict): - chunks = _iterencode_dict(value, _current_indent_level) - else: - chunks = _iterencode(value, _current_indent_level) - for chunk in chunks: - yield chunk - if newline_indent is not None: - _current_indent_level -= 1 - yield '\n' + (' ' * (_indent * _current_indent_level)) - yield '}' - if markers is not None: - del markers[markerid] - - def _iterencode(o, _current_indent_level): - if isinstance(o, basestring): - yield _encoder(o) - elif o is None: - yield 'null' - elif o is True: - yield 'true' - elif o is False: - yield 'false' - elif isinstance(o, (int, long)): - yield str(o) - elif isinstance(o, float): - yield _floatstr(o) - elif isinstance(o, (list, tuple)): - for chunk in _iterencode_list(o, _current_indent_level): - yield chunk - elif isinstance(o, dict): - for chunk in _iterencode_dict(o, _current_indent_level): - yield chunk - else: - if markers is not None: - markerid = id(o) - if markerid in markers: - raise ValueError("Circular reference detected") - markers[markerid] = o - o = _default(o) - for chunk in _iterencode(o, _current_indent_level): - yield chunk - if markers is not None: - del markers[markerid] - - return _iterencode diff --git a/django/utils/simplejson/scanner.py b/django/utils/simplejson/scanner.py deleted file mode 100644 index adbc6ec979c..00000000000 --- a/django/utils/simplejson/scanner.py +++ /dev/null @@ -1,65 +0,0 @@ -"""JSON token scanner -""" -import re -try: - from simplejson._speedups import make_scanner as c_make_scanner -except ImportError: - c_make_scanner = None - -__all__ = ['make_scanner'] - -NUMBER_RE = re.compile( - r'(-?(?:0|[1-9]\d*))(\.\d+)?([eE][-+]?\d+)?', - (re.VERBOSE | re.MULTILINE | re.DOTALL)) - -def py_make_scanner(context): - parse_object = context.parse_object - parse_array = context.parse_array - parse_string = context.parse_string - match_number = NUMBER_RE.match - encoding = context.encoding - strict = context.strict - parse_float = context.parse_float - parse_int = context.parse_int - parse_constant = context.parse_constant - object_hook = context.object_hook - - def _scan_once(string, idx): - try: - nextchar = string[idx] - except IndexError: - raise StopIteration - - if nextchar == '"': - return parse_string(string, idx + 1, encoding, strict) - elif nextchar == '{': - return parse_object((string, idx + 1), encoding, strict, _scan_once, object_hook) - elif nextchar == '[': - return parse_array((string, idx + 1), _scan_once) - elif nextchar == 'n' and string[idx:idx + 4] == 'null': - return None, idx + 4 - elif nextchar == 't' and string[idx:idx + 4] == 'true': - return True, idx + 4 - elif nextchar == 'f' and string[idx:idx + 5] == 'false': - return False, idx + 5 - - m = match_number(string, idx) - if m is not None: - integer, frac, exp = m.groups() - if frac or exp: - res = parse_float(integer + (frac or '') + (exp or '')) - else: - res = parse_int(integer) - return res, m.end() - elif nextchar == 'N' and string[idx:idx + 3] == 'NaN': - return parse_constant('NaN'), idx + 3 - elif nextchar == 'I' and string[idx:idx + 8] == 'Infinity': - return parse_constant('Infinity'), idx + 8 - elif nextchar == '-' and string[idx:idx + 9] == '-Infinity': - return parse_constant('-Infinity'), idx + 9 - else: - raise StopIteration - - return _scan_once - -make_scanner = c_make_scanner or py_make_scanner diff --git a/django/utils/simplejson/tool.py b/django/utils/simplejson/tool.py deleted file mode 100644 index 74401c279a7..00000000000 --- a/django/utils/simplejson/tool.py +++ /dev/null @@ -1,35 +0,0 @@ -r"""Using simplejson from the shell to validate and -pretty-print:: - - $ echo '{"json":"obj"}' | python -msimplejson.tool - { - "json": "obj" - } - $ echo '{ 1.2:3.4}' | python -msimplejson.tool - Expecting property name: line 1 column 2 (char 2) -""" -from django.utils import simplejson - -def main(): - import sys - if len(sys.argv) == 1: - infile = sys.stdin - outfile = sys.stdout - elif len(sys.argv) == 2: - infile = open(sys.argv[1], 'rb') - outfile = sys.stdout - elif len(sys.argv) == 3: - infile = open(sys.argv[1], 'rb') - outfile = open(sys.argv[2], 'wb') - else: - raise SystemExit("%s [infile [outfile]]" % (sys.argv[0],)) - try: - obj = simplejson.load(infile) - except ValueError, e: - raise SystemExit(e) - simplejson.dump(obj, outfile, sort_keys=True, indent=4) - outfile.write('\n') - - -if __name__ == '__main__': - main() diff --git a/docs/_ext/djangodocs.py b/docs/_ext/djangodocs.py index 3cf00a38e1c..d4b0b43ca77 100644 --- a/docs/_ext/djangodocs.py +++ b/docs/_ext/djangodocs.py @@ -1,20 +1,11 @@ """ Sphinx plugins for Django documentation. """ +import json import os import re from docutils import nodes, transforms -try: - import json -except ImportError: - try: - import simplejson as json - except ImportError: - try: - from django.utils import simplejson as json - except ImportError: - json = None from sphinx import addnodes, roles, __version__ as sphinx_ver from sphinx.builders.html import StandaloneHTMLBuilder @@ -210,9 +201,6 @@ class DjangoStandaloneHTMLBuilder(StandaloneHTMLBuilder): def finish(self): super(DjangoStandaloneHTMLBuilder, self).finish() - if json is None: - self.warn("cannot create templatebuiltins.js due to missing simplejson dependency") - return self.info(bold("writing templatebuiltins.js...")) xrefs = self.env.domaindata["std"]["objects"] templatebuiltins = { diff --git a/docs/internals/deprecation.txt b/docs/internals/deprecation.txt index e3a5e1a40bd..2f594536c55 100644 --- a/docs/internals/deprecation.txt +++ b/docs/internals/deprecation.txt @@ -268,6 +268,9 @@ these changes. See the :doc:`Django 1.5 release notes` for more details on these changes. +* The module ``django.utils.simplejson`` will be removed. The standard library +provides :mod:`json` which should be used instead. + * The function ``django.utils.itercompat.product`` will be removed. The Python builtin version should be used instead. diff --git a/docs/releases/1.5.txt b/docs/releases/1.5.txt index dcf050f2ae1..4f6b05f8f42 100644 --- a/docs/releases/1.5.txt +++ b/docs/releases/1.5.txt @@ -58,8 +58,17 @@ Backwards incompatible changes in 1.5 Features deprecated in 1.5 ========================== -itercompat.product -~~~~~~~~~~~~~~~~~~ +``django.utils.simplejson`` +~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Since Django 1.5 drops support for Python 2.5, all supported versions of +Python provide the :mod:`json` module in their standard library. This module +is actually a version of ``simplejson`` distributed by Python, so Django no +longer needs to provide a copy. Any use of :mod:`django.utils.simplejson` can +be safely changed to :mod:`json`. + +``itercompat.product`` +~~~~~~~~~~~~~~~~~~~~~~ The :func:`~django.utils.itercompat.product` function has been deprecated. Use the builtin `itertools.product` instead. diff --git a/docs/topics/class-based-views.txt b/docs/topics/class-based-views.txt index 6df8f782506..abac22000fa 100644 --- a/docs/topics/class-based-views.txt +++ b/docs/topics/class-based-views.txt @@ -501,8 +501,8 @@ different rendering behavior. For example, a simple JSON mixin might look something like this:: + import json from django import http - from django.utils import simplejson as json class JSONResponseMixin(object): def render_to_response(self, context): diff --git a/docs/topics/serialization.txt b/docs/topics/serialization.txt index d7d72ec48a8..d5a5282945c 100644 --- a/docs/topics/serialization.txt +++ b/docs/topics/serialization.txt @@ -143,15 +143,13 @@ Identifier Information ========== ============================================================== ``xml`` Serializes to and from a simple XML dialect. -``json`` Serializes to and from JSON_ (using a version of simplejson_ - bundled with Django). +``json`` Serializes to and from JSON_. ``yaml`` Serializes to YAML (YAML Ain't a Markup Language). This serializer is only available if PyYAML_ is installed. ========== ============================================================== .. _json: http://json.org/ -.. _simplejson: http://undefined.org/python/#simplejson .. _PyYAML: http://www.pyyaml.org/ Notes for specific serialization formats @@ -169,28 +167,21 @@ For example:: json_serializer = serializers.get_serializer("json")() json_serializer.serialize(queryset, ensure_ascii=False, stream=response) -The Django source code includes the simplejson_ module. However, if you're -using Python 2.6 or later (which includes a builtin version of the module), Django will -use the builtin ``json`` module automatically. If you have a system installed -version that includes the C-based speedup extension, or your system version is -more recent than the version shipped with Django (currently, 2.0.7), the -system version will be used instead of the version included with Django. - -Be aware that if you're serializing using that module directly, not all Django -output can be passed unmodified to simplejson. In particular, :ref:`lazy -translation objects ` need a `special encoder`_ written for -them. Something like this will work:: +Be aware that not all Django output can be passed unmodified to :mod:`json`. +In particular, :ref:`lazy translation objects ` need a +`special encoder`_ written for them. Something like this will work:: + import json from django.utils.functional import Promise from django.utils.encoding import force_unicode - class LazyEncoder(simplejson.JSONEncoder): + class LazyEncoder(json.JSONEncoder): def default(self, obj): if isinstance(obj, Promise): return force_unicode(obj) return super(LazyEncoder, self).default(obj) -.. _special encoder: http://svn.red-bean.com/bob/simplejson/tags/simplejson-1.7/docs/index.html +.. _special encoder: http://docs.python.org/library/json.html#encoders-and-decoders .. _topics-serialization-natural-keys: diff --git a/tests/modeltests/field_subclassing/fields.py b/tests/modeltests/field_subclassing/fields.py index f74470686da..4d809ba6f88 100644 --- a/tests/modeltests/field_subclassing/fields.py +++ b/tests/modeltests/field_subclassing/fields.py @@ -1,5 +1,6 @@ +import json + from django.db import models -from django.utils import simplejson as json from django.utils.encoding import force_unicode diff --git a/tests/modeltests/serializers/tests.py b/tests/modeltests/serializers/tests.py index 02538b0fb71..dac098763c3 100644 --- a/tests/modeltests/serializers/tests.py +++ b/tests/modeltests/serializers/tests.py @@ -1,6 +1,7 @@ from __future__ import absolute_import # -*- coding: utf-8 -*- +import json from datetime import datetime from xml.dom import minidom from StringIO import StringIO @@ -9,7 +10,7 @@ from django.conf import settings from django.core import serializers from django.db import transaction, connection from django.test import TestCase, TransactionTestCase, Approximate -from django.utils import simplejson, unittest +from django.utils import unittest from .models import (Category, Author, Article, AuthorProfile, Actor, Movie, Score, Player, Team) @@ -354,7 +355,7 @@ class JsonSerializerTestCase(SerializersTestBase, TestCase): @staticmethod def _validate_output(serial_str): try: - simplejson.loads(serial_str) + json.loads(serial_str) except Exception: return False else: @@ -363,7 +364,7 @@ class JsonSerializerTestCase(SerializersTestBase, TestCase): @staticmethod def _get_pk_values(serial_str): ret_list = [] - serial_list = simplejson.loads(serial_str) + serial_list = json.loads(serial_str) for obj_dict in serial_list: ret_list.append(obj_dict["pk"]) return ret_list @@ -371,7 +372,7 @@ class JsonSerializerTestCase(SerializersTestBase, TestCase): @staticmethod def _get_field_values(serial_str, field_name): ret_list = [] - serial_list = simplejson.loads(serial_str) + serial_list = json.loads(serial_str) for obj_dict in serial_list: if field_name in obj_dict["fields"]: ret_list.append(obj_dict["fields"][field_name]) diff --git a/tests/regressiontests/file_uploads/tests.py b/tests/regressiontests/file_uploads/tests.py index ffa2017a5e9..b6191ba0336 100644 --- a/tests/regressiontests/file_uploads/tests.py +++ b/tests/regressiontests/file_uploads/tests.py @@ -5,6 +5,7 @@ from __future__ import absolute_import import base64 import errno import hashlib +import json import os import shutil from StringIO import StringIO @@ -13,7 +14,7 @@ from django.core.files import temp as tempfile from django.core.files.uploadedfile import SimpleUploadedFile from django.http.multipartparser import MultiPartParser from django.test import TestCase, client -from django.utils import simplejson, unittest +from django.utils import unittest from . import uploadhandler from .models import FileModel, temp_storage, UPLOAD_TO @@ -78,7 +79,7 @@ class FileUploadTests(TestCase): 'wsgi.input': client.FakePayload(payload), } response = self.client.request(**r) - received = simplejson.loads(response.content) + received = json.loads(response.content) self.assertEqual(received['file'], test_string) @@ -150,7 +151,7 @@ class FileUploadTests(TestCase): response = self.client.request(**r) # The filenames should have been sanitized by the time it got to the view. - recieved = simplejson.loads(response.content) + recieved = json.loads(response.content) for i, name in enumerate(scary_file_names): got = recieved["file%s" % i] self.assertEqual(got, "hax0rd.txt") @@ -174,7 +175,7 @@ class FileUploadTests(TestCase): 'REQUEST_METHOD': 'POST', 'wsgi.input': client.FakePayload(payload), } - got = simplejson.loads(self.client.request(**r).content) + got = json.loads(self.client.request(**r).content) self.assertTrue(len(got['file']) < 256, "Got a long file name (%s characters)." % len(got['file'])) def test_truncated_multipart_handled_gracefully(self): @@ -200,7 +201,7 @@ class FileUploadTests(TestCase): 'REQUEST_METHOD': 'POST', 'wsgi.input': client.FakePayload(payload), } - got = simplejson.loads(self.client.request(**r).content) + got = json.loads(self.client.request(**r).content) self.assertEquals(got, {}) def test_empty_multipart_handled_gracefully(self): @@ -215,7 +216,7 @@ class FileUploadTests(TestCase): 'REQUEST_METHOD': 'POST', 'wsgi.input': client.FakePayload(''), } - got = simplejson.loads(self.client.request(**r).content) + got = json.loads(self.client.request(**r).content) self.assertEquals(got, {}) def test_custom_upload_handler(self): @@ -231,12 +232,12 @@ class FileUploadTests(TestCase): # Small file posting should work. response = self.client.post('/file_uploads/quota/', {'f': smallfile}) - got = simplejson.loads(response.content) + got = json.loads(response.content) self.assertTrue('f' in got) # Large files don't go through. response = self.client.post("/file_uploads/quota/", {'f': bigfile}) - got = simplejson.loads(response.content) + got = json.loads(response.content) self.assertTrue('f' not in got) def test_broken_custom_upload_handler(self): @@ -274,7 +275,7 @@ class FileUploadTests(TestCase): 'field5': u'test7', 'file2': (file2, file2a) }) - got = simplejson.loads(response.content) + got = json.loads(response.content) self.assertEqual(got.get('file1'), 1) self.assertEqual(got.get('file2'), 2) diff --git a/tests/regressiontests/file_uploads/views.py b/tests/regressiontests/file_uploads/views.py index 9fd1a8d5449..ae6842d0a70 100644 --- a/tests/regressiontests/file_uploads/views.py +++ b/tests/regressiontests/file_uploads/views.py @@ -1,11 +1,11 @@ from __future__ import absolute_import import hashlib +import json import os from django.core.files.uploadedfile import UploadedFile from django.http import HttpResponse, HttpResponseServerError -from django.utils import simplejson from .models import FileModel, UPLOAD_TO from .tests import UNICODE_FILENAME @@ -88,14 +88,14 @@ def file_upload_echo(request): Simple view to echo back info about uploaded files for tests. """ r = dict([(k, f.name) for k, f in request.FILES.items()]) - return HttpResponse(simplejson.dumps(r)) + return HttpResponse(json.dumps(r)) def file_upload_echo_content(request): """ Simple view to echo back the content of uploaded files for tests. """ r = dict([(k, f.read()) for k, f in request.FILES.items()]) - return HttpResponse(simplejson.dumps(r)) + return HttpResponse(json.dumps(r)) def file_upload_quota(request): """ @@ -120,7 +120,7 @@ def file_upload_getlist_count(request): for key in request.FILES.keys(): file_counts[key] = len(request.FILES.getlist(key)) - return HttpResponse(simplejson.dumps(file_counts)) + return HttpResponse(json.dumps(file_counts)) def file_upload_errors(request): request.upload_handlers.insert(0, ErroringUploadHandler()) diff --git a/tests/regressiontests/test_client_regress/views.py b/tests/regressiontests/test_client_regress/views.py index ebb68c4c827..ffd416655e7 100644 --- a/tests/regressiontests/test_client_regress/views.py +++ b/tests/regressiontests/test_client_regress/views.py @@ -1,3 +1,4 @@ +import json import warnings from django.conf import settings @@ -5,7 +6,6 @@ from django.contrib.auth.decorators import login_required from django.http import HttpResponse, HttpResponseRedirect from django.core.exceptions import SuspiciousOperation from django.shortcuts import render_to_response -from django.utils import simplejson from django.utils.encoding import smart_str from django.core.serializers.json import DjangoJSONEncoder from django.test.client import CONTENT_TYPE_RE @@ -81,8 +81,8 @@ def return_json_file(request): charset = settings.DEFAULT_CHARSET # This just checks that the uploaded data is JSON - obj_dict = simplejson.loads(request.body.decode(charset)) - obj_json = simplejson.dumps(obj_dict, encoding=charset, + obj_dict = json.loads(request.body.decode(charset)) + obj_json = json.dumps(obj_dict, encoding=charset, cls=DjangoJSONEncoder, ensure_ascii=False) response = HttpResponse(smart_str(obj_json, encoding=charset), status=200, diff --git a/tests/regressiontests/test_utils/tests.py b/tests/regressiontests/test_utils/tests.py index 2192404b933..a277bc999b5 100644 --- a/tests/regressiontests/test_utils/tests.py +++ b/tests/regressiontests/test_utils/tests.py @@ -491,7 +491,7 @@ class AssertFieldOutputTests(SimpleTestCase): __test__ = {"API_TEST": r""" # Some checks of the doctest output normalizer. # Standard doctests do fairly ->>> from django.utils import simplejson +>>> import json >>> from django.utils.xmlutils import SimplerXMLGenerator >>> from StringIO import StringIO @@ -502,7 +502,7 @@ __test__ = {"API_TEST": r""" ... return 42 >>> def produce_json(): -... return simplejson.dumps(['foo', {'bar': ('baz', None, 1.0, 2), 'whiz': 42}]) +... return json.dumps(['foo', {'bar': ('baz', None, 1.0, 2), 'whiz': 42}]) >>> def produce_xml(): ... stream = StringIO()