Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

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.
  • Loading branch information...
commit cec6bd5a59547dc97fe98975c570fc27a1e970be 1 parent ee0a7c7
@aaugustin aaugustin authored
Showing with 105 additions and 1,321 deletions.
  1. +0 −1  MANIFEST.in
  2. +2 −1  django/contrib/formtools/wizard/storage/cookie.py
  3. +2 −2 django/contrib/gis/geometry/test_data.py
  4. +2 −1  django/contrib/messages/storage/cookie.py
  5. +2 −1  django/contrib/messages/tests/cookie.py
  6. +8 −5 django/core/serializers/json.py
  7. +5 −4 django/core/signing.py
  8. +4 −3 django/test/testcases.py
  9. +31 −0 django/utils/simplejson.py
  10. +0 −19 django/utils/simplejson/LICENSE.txt
  11. +0 −354 django/utils/simplejson/__init__.py
  12. +0 −345 django/utils/simplejson/decoder.py
  13. +0 −430 django/utils/simplejson/encoder.py
  14. +0 −65 django/utils/simplejson/scanner.py
  15. +0 −35 django/utils/simplejson/tool.py
  16. +1 −13 docs/_ext/djangodocs.py
  17. +3 −0  docs/internals/deprecation.txt
  18. +11 −2 docs/releases/1.5.txt
  19. +1 −1  docs/topics/class-based-views.txt
  20. +7 −16 docs/topics/serialization.txt
  21. +2 −1  tests/modeltests/field_subclassing/fields.py
  22. +5 −4 tests/modeltests/serializers/tests.py
  23. +10 −9 tests/regressiontests/file_uploads/tests.py
  24. +4 −4 tests/regressiontests/file_uploads/views.py
  25. +3 −3 tests/regressiontests/test_client_regress/views.py
  26. +2 −2 tests/regressiontests/test_utils/tests.py
View
1  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 *
View
3  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
View
4 django/contrib/gis/geometry/test_data.py
@@ -3,10 +3,10 @@
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 @@ def geometries(self):
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
View
3  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
View
3  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):
View
13 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):

I've updated to Djando dev and found Tastypie serialization is broken because json.JSONEncoder doesn't accept a keyword argument namedtuple_as_object, while simplejson.JSONEncoder does. This breaks backwards compatibility, is this the desired behavior? Should this be fixed in Django or in Tastypie?

Thanks

Most likely this is due to the fact that Tastypie is doing simplejson.dumps(data, cls=json.DjangoJSONEncoder, sort_keys=True, ensure_ascii=False) and passing a DjangoJSONEncoder which is no longer a simplejson encoder anymore, so simplejson passes to it kwargs it doesn't accept. So I'm leaning towards patching this in Tastypie.

Thanks, cheers

@ogier
ogier added a note

I consider this a bug in simplejson.

>>> import json
>>> import simplejson
>>> simplejson.dumps({'hello': 'world'}, cls=json.JSONEncoder)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/home/aogier/.virtualenvs/simp/lib/python2.7/site-packages/simplejson/__init__.py", line 334, in dumps
    **kw).encode(obj)
TypeError: __init__() got an unexpected keyword argument 'namedtuple_as_object'

Basically, simplejson 2.2 broke backwards compatibility, both with itself and with the Python standard library by adding this keyword argument.

@spookylukey Collaborator

There's a similar issue in django-piston - here was my fix: https://bitbucket.org/jespern/django-piston/pull-request/25/compatibility-fix-for-json-emitter-with

I think we should just document the problem, given that the workaround isn't too bad. Doing anything else would just perpetuate the problems that simplejson has caused.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
"""
JSONEncoder subclass that knows how to encode date/time and decimal types.
"""
View
9 django/core/signing.py
@@ -33,12 +33,13 @@
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):
View
7 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.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 @@ def check_output_json(self, want, got, optionsflags):
"""
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
View
31 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__
View
19 django/utils/simplejson/LICENSE.txt
@@ -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.
View
354 django/utils/simplejson/__init__.py
@@ -1,354 +0,0 @@
-r"""JSON (JavaScript Object Notation) <http://json.org> 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)
View
345 django/utils/simplejson/decoder.py
@@ -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 <http://json.org> 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
View
430 django/utils/simplejson/encoder.py
@@ -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 <http://json.org> 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
View
65 django/utils/simplejson/scanner.py
@@ -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
View
35 django/utils/simplejson/tool.py
@@ -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()
View
14 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 = {
View
3  docs/internals/deprecation.txt
@@ -268,6 +268,9 @@ these changes.
See the :doc:`Django 1.5 release notes</releases/1.5>` 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.
View
13 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.
View
2  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):
View
23 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 <lazy-translations>` 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 <lazy-translations>` 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:
View
3  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
View
9 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.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 @@ def _validate_output(serial_str):
@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 @@ def _get_pk_values(serial_str):
@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])
View
19 tests/regressiontests/file_uploads/tests.py
@@ -5,6 +5,7 @@
import base64
import errno
import hashlib
+import json
import os
import shutil
from StringIO import StringIO
@@ -13,7 +14,7 @@
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 @@ def test_base64_upload(self):
'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 @@ def test_dangerous_file_names(self):
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 @@ def test_filename_overflow(self):
'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 @@ def test_truncated_multipart_handled_gracefully(self):
'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 @@ def test_empty_multipart_handled_gracefully(self):
'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 @@ def test_custom_upload_handler(self):
# 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 @@ def test_fileupload_getlist(self):
'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)
View
8 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())
View
6 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.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,
View
4 tests/regressiontests/test_utils/tests.py
@@ -491,7 +491,7 @@ class MyCustomField(IntegerField):
__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 @@ class MyCustomField(IntegerField):
... 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()
@maraujop

I've updated to Djando dev and found Tastypie serialization is broken because json.JSONEncoder doesn't accept a keyword argument namedtuple_as_object, while simplejson.JSONEncoder does. This breaks backwards compatibility, is this the desired behavior? Should this be fixed in Django or in Tastypie?

Thanks

@maraujop

Most likely this is due to the fact that Tastypie is doing simplejson.dumps(data, cls=json.DjangoJSONEncoder, sort_keys=True, ensure_ascii=False) and passing a DjangoJSONEncoder which is no longer a simplejson encoder anymore, so simplejson passes to it kwargs it doesn't accept. So I'm leaning towards patching this in Tastypie.

Thanks, cheers

@ogier

I consider this a bug in simplejson.

>>> import json
>>> import simplejson
>>> simplejson.dumps({'hello': 'world'}, cls=json.JSONEncoder)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/home/aogier/.virtualenvs/simp/lib/python2.7/site-packages/simplejson/__init__.py", line 334, in dumps
    **kw).encode(obj)
TypeError: __init__() got an unexpected keyword argument 'namedtuple_as_object'

Basically, simplejson 2.2 broke backwards compatibility, both with itself and with the Python standard library by adding this keyword argument.

@spookylukey

There's a similar issue in django-piston - here was my fix: https://bitbucket.org/jespern/django-piston/pull-request/25/compatibility-fix-for-json-emitter-with

I think we should just document the problem, given that the workaround isn't too bad. Doing anything else would just perpetuate the problems that simplejson has caused.

Please sign in to comment.
Something went wrong with that request. Please try again.