Fixed #3320: upgraded django.utils.simplejson to simplejson version 1.5. Thanks, Lawrence Oluyede.

git-svn-id: http://code.djangoproject.com/svn/django/trunk@4454 bcc190cf-cafb-0310-a4f2-bffc1f526a37
This commit is contained in:
Jacob Kaplan-Moss 2007-01-30 22:34:15 +00:00
parent 1e3d035fcc
commit 695302ce92
6 changed files with 156 additions and 40 deletions

View File

@ -1,4 +1,4 @@
simplejson 1.3 simplejson 1.5
Copyright (c) 2006 Bob Ippolito Copyright (c) 2006 Bob Ippolito
Permission is hereby granted, free of charge, to any person obtaining a copy of Permission is hereby granted, free of charge, to any person obtaining a copy of

View File

@ -27,6 +27,21 @@ Encoding basic Python object hierarchies::
>>> io.getvalue() >>> io.getvalue()
'["streaming API"]' '["streaming API"]'
Compact encoding::
>>> import simplejson
>>> simplejson.dumps([1,2,3,{'4': 5, '6': 7}], separators=(',',':'))
'[1,2,3,{"4":5,"6":7}]'
Pretty printing::
>>> import simplejson
>>> print simplejson.dumps({'4': 5, '6': 7}, sort_keys=True, indent=4)
{
"4": 5,
"6": 7
}
Decoding JSON:: Decoding JSON::
>>> import simplejson >>> import simplejson
@ -68,10 +83,10 @@ Extending JSONEncoder::
['[', '2.0', ', ', '1.0', ']'] ['[', '2.0', ', ', '1.0', ']']
Note that the JSON produced by this module is a subset of YAML, Note that the JSON produced by this module's default settings
so it may be used as a serializer for that as well. is a subset of YAML, so it may be used as a serializer for that as well.
""" """
__version__ = '1.3' __version__ = '1.5'
__all__ = [ __all__ = [
'dump', 'dumps', 'load', 'loads', 'dump', 'dumps', 'load', 'loads',
'JSONDecoder', 'JSONEncoder', 'JSONDecoder', 'JSONEncoder',
@ -81,7 +96,7 @@ from django.utils.simplejson.decoder import JSONDecoder
from django.utils.simplejson.encoder import JSONEncoder from django.utils.simplejson.encoder import JSONEncoder
def dump(obj, fp, skipkeys=False, ensure_ascii=True, check_circular=True, def dump(obj, fp, skipkeys=False, ensure_ascii=True, check_circular=True,
allow_nan=True, cls=None, **kw): allow_nan=True, cls=None, indent=None, **kw):
""" """
Serialize ``obj`` as a JSON formatted stream to ``fp`` (a Serialize ``obj`` as a JSON formatted stream to ``fp`` (a
``.write()``-supporting file-like object). ``.write()``-supporting file-like object).
@ -105,6 +120,10 @@ def dump(obj, fp, skipkeys=False, ensure_ascii=True, check_circular=True,
in strict compliance of the JSON specification, instead of using the in strict compliance of the JSON specification, instead of using the
JavaScript equivalents (``NaN``, ``Infinity``, ``-Infinity``). 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.
To use a custom ``JSONEncoder`` subclass (e.g. one that overrides the To use a custom ``JSONEncoder`` subclass (e.g. one that overrides the
``.default()`` method to serialize additional types), specify it with ``.default()`` method to serialize additional types), specify it with
the ``cls`` kwarg. the ``cls`` kwarg.
@ -112,7 +131,7 @@ def dump(obj, fp, skipkeys=False, ensure_ascii=True, check_circular=True,
if cls is None: if cls is None:
cls = JSONEncoder cls = JSONEncoder
iterable = cls(skipkeys=skipkeys, ensure_ascii=ensure_ascii, iterable = cls(skipkeys=skipkeys, ensure_ascii=ensure_ascii,
check_circular=check_circular, allow_nan=allow_nan, check_circular=check_circular, allow_nan=allow_nan, indent=indent,
**kw).iterencode(obj) **kw).iterencode(obj)
# could accelerate with writelines in some versions of Python, at # could accelerate with writelines in some versions of Python, at
# a debuggability cost # a debuggability cost
@ -120,7 +139,7 @@ def dump(obj, fp, skipkeys=False, ensure_ascii=True, check_circular=True,
fp.write(chunk) fp.write(chunk)
def dumps(obj, skipkeys=False, ensure_ascii=True, check_circular=True, def dumps(obj, skipkeys=False, ensure_ascii=True, check_circular=True,
allow_nan=True, cls=None, **kw): allow_nan=True, cls=None, indent=None, separators=None, **kw):
""" """
Serialize ``obj`` to a JSON formatted ``str``. Serialize ``obj`` to a JSON formatted ``str``.
@ -141,14 +160,26 @@ def dumps(obj, skipkeys=False, ensure_ascii=True, check_circular=True,
strict compliance of the JSON specification, instead of using the strict compliance of the JSON specification, instead of using the
JavaScript equivalents (``NaN``, ``Infinity``, ``-Infinity``). 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.
To use a custom ``JSONEncoder`` subclass (e.g. one that overrides the To use a custom ``JSONEncoder`` subclass (e.g. one that overrides the
``.default()`` method to serialize additional types), specify it with ``.default()`` method to serialize additional types), specify it with
the ``cls`` kwarg. the ``cls`` kwarg.
""" """
if cls is None: if cls is None:
cls = JSONEncoder cls = JSONEncoder
return cls(skipkeys=skipkeys, ensure_ascii=ensure_ascii, return cls(
check_circular=check_circular, allow_nan=allow_nan, **kw).encode(obj) skipkeys=skipkeys, ensure_ascii=ensure_ascii,
check_circular=check_circular, allow_nan=allow_nan, indent=indent,
separators=separators,
**kw).encode(obj)
def load(fp, encoding=None, cls=None, object_hook=None, **kw): def load(fp, encoding=None, cls=None, object_hook=None, **kw):
""" """

View File

@ -127,6 +127,7 @@ def JSONObject(match, context, _w=WHITESPACE.match):
raise ValueError(errmsg("Expecting property name", s, end)) raise ValueError(errmsg("Expecting property name", s, end))
end += 1 end += 1
encoding = getattr(context, 'encoding', None) encoding = getattr(context, 'encoding', None)
iterscan = JSONScanner.iterscan
while True: while True:
key, end = scanstring(s, end, encoding) key, end = scanstring(s, end, encoding)
end = _w(s, end).end() end = _w(s, end).end()
@ -134,7 +135,7 @@ def JSONObject(match, context, _w=WHITESPACE.match):
raise ValueError(errmsg("Expecting : delimiter", s, end)) raise ValueError(errmsg("Expecting : delimiter", s, end))
end = _w(s, end + 1).end() end = _w(s, end + 1).end()
try: try:
value, end = JSONScanner.iterscan(s, idx=end).next() value, end = iterscan(s, idx=end, context=context).next()
except StopIteration: except StopIteration:
raise ValueError(errmsg("Expecting object", s, end)) raise ValueError(errmsg("Expecting object", s, end))
pairs[key] = value pairs[key] = value
@ -164,9 +165,10 @@ def JSONArray(match, context, _w=WHITESPACE.match):
nextchar = s[end:end + 1] nextchar = s[end:end + 1]
if nextchar == ']': if nextchar == ']':
return values, end + 1 return values, end + 1
iterscan = JSONScanner.iterscan
while True: while True:
try: try:
value, end = JSONScanner.iterscan(s, idx=end).next() value, end = iterscan(s, idx=end, context=context).next()
except StopIteration: except StopIteration:
raise ValueError(errmsg("Expecting object", s, end)) raise ValueError(errmsg("Expecting object", s, end))
values.append(value) values.append(value)

View File

@ -3,11 +3,11 @@ Implementation of JSONEncoder
""" """
import re import re
# this should match any kind of infinity
INFCHARS = re.compile(r'[infINF]')
ESCAPE = re.compile(r'[\x00-\x19\\"\b\f\n\r\t]') ESCAPE = re.compile(r'[\x00-\x19\\"\b\f\n\r\t]')
ESCAPE_ASCII = re.compile(r'([\\"]|[^\ -~])') ESCAPE_ASCII = re.compile(r'([\\"/]|[^\ -~])')
ESCAPE_DCT = { ESCAPE_DCT = {
# escape all forward slashes to prevent </script> attack
'/': '\\/',
'\\': '\\\\', '\\': '\\\\',
'"': '\\"', '"': '\\"',
'\b': '\\b', '\b': '\\b',
@ -16,31 +16,31 @@ ESCAPE_DCT = {
'\r': '\\r', '\r': '\\r',
'\t': '\\t', '\t': '\\t',
} }
for i in range(20): for i in range(0x20):
ESCAPE_DCT.setdefault(chr(i), '\\u%04x' % (i,)) ESCAPE_DCT.setdefault(chr(i), '\\u%04x' % (i,))
# assume this produces an infinity on all machines (probably not guaranteed)
INFINITY = float('1e66666')
def floatstr(o, allow_nan=True): def floatstr(o, allow_nan=True):
s = str(o) # Check for specials. Note that this type of test is processor- and/or
# If the first non-sign is a digit then it's not a special value # platform-specific, so do tests which don't depend on the internals.
if (o < 0.0 and s[1].isdigit()) or s[0].isdigit():
return s if o != o:
elif not allow_nan: text = 'NaN'
elif o == INFINITY:
text = 'Infinity'
elif o == -INFINITY:
text = '-Infinity'
else:
return str(o)
if not allow_nan:
raise ValueError("Out of range float values are not JSON compliant: %r" raise ValueError("Out of range float values are not JSON compliant: %r"
% (o,)) % (o,))
# These are the string representations on the platforms I've tried
if s == 'nan': return text
return 'NaN'
if s == 'inf':
return 'Infinity'
if s == '-inf':
return '-Infinity'
# NaN should either be inequal to itself, or equal to everything
if o != o or o == 0.0:
return 'NaN'
# Last ditch effort, assume inf
if o < 0:
return '-Infinity'
return 'Infinity'
def encode_basestring(s): def encode_basestring(s):
""" """
@ -90,8 +90,11 @@ class JSONEncoder(object):
implementation (to raise ``TypeError``). implementation (to raise ``TypeError``).
""" """
__all__ = ['__init__', 'default', 'encode', 'iterencode'] __all__ = ['__init__', 'default', 'encode', 'iterencode']
item_separator = ', '
key_separator = ': '
def __init__(self, skipkeys=False, ensure_ascii=True, def __init__(self, skipkeys=False, ensure_ascii=True,
check_circular=True, allow_nan=True, sort_keys=False): check_circular=True, allow_nan=True, sort_keys=False,
indent=None, separators=None):
""" """
Constructor for JSONEncoder, with sensible defaults. Constructor for JSONEncoder, with sensible defaults.
@ -116,6 +119,15 @@ class JSONEncoder(object):
If sort_keys is True, then the output of dictionaries will be If sort_keys is True, then the output of dictionaries will be
sorted by key; this is useful for regression tests to ensure sorted by key; this is useful for regression tests to ensure
that JSON serializations can be compared on a day-to-day basis. 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.
""" """
self.skipkeys = skipkeys self.skipkeys = skipkeys
@ -123,6 +135,13 @@ class JSONEncoder(object):
self.check_circular = check_circular self.check_circular = check_circular
self.allow_nan = allow_nan self.allow_nan = allow_nan
self.sort_keys = sort_keys self.sort_keys = sort_keys
self.indent = indent
self.current_indent_level = 0
if separators is not None:
self.item_separator, self.key_separator = separators
def _newline_indent(self):
return '\n' + (' ' * (self.indent * self.current_indent_level))
def _iterencode_list(self, lst, markers=None): def _iterencode_list(self, lst, markers=None):
if not lst: if not lst:
@ -134,14 +153,25 @@ class JSONEncoder(object):
raise ValueError("Circular reference detected") raise ValueError("Circular reference detected")
markers[markerid] = lst markers[markerid] = lst
yield '[' yield '['
if self.indent is not None:
self.current_indent_level += 1
newline_indent = self._newline_indent()
separator = self.item_separator + newline_indent
yield newline_indent
else:
newline_indent = None
separator = self.item_separator
first = True first = True
for value in lst: for value in lst:
if first: if first:
first = False first = False
else: else:
yield ', ' yield separator
for chunk in self._iterencode(value, markers): for chunk in self._iterencode(value, markers):
yield chunk yield chunk
if newline_indent is not None:
self.current_indent_level -= 1
yield self._newline_indent()
yield ']' yield ']'
if markers is not None: if markers is not None:
del markers[markerid] del markers[markerid]
@ -156,6 +186,15 @@ class JSONEncoder(object):
raise ValueError("Circular reference detected") raise ValueError("Circular reference detected")
markers[markerid] = dct markers[markerid] = dct
yield '{' yield '{'
key_separator = self.key_separator
if self.indent is not None:
self.current_indent_level += 1
newline_indent = self._newline_indent()
item_separator = self.item_separator + newline_indent
yield newline_indent
else:
newline_indent = None
item_separator = self.item_separator
first = True first = True
if self.ensure_ascii: if self.ensure_ascii:
encoder = encode_basestring_ascii encoder = encode_basestring_ascii
@ -165,7 +204,7 @@ class JSONEncoder(object):
if self.sort_keys: if self.sort_keys:
keys = dct.keys() keys = dct.keys()
keys.sort() keys.sort()
items = [(k,dct[k]) for k in keys] items = [(k, dct[k]) for k in keys]
else: else:
items = dct.iteritems() items = dct.iteritems()
for key, value in items: for key, value in items:
@ -190,11 +229,14 @@ class JSONEncoder(object):
if first: if first:
first = False first = False
else: else:
yield ', ' yield item_separator
yield encoder(key) yield encoder(key)
yield ': ' yield key_separator
for chunk in self._iterencode(value, markers): for chunk in self._iterencode(value, markers):
yield chunk yield chunk
if newline_indent is not None:
self.current_indent_level -= 1
yield self._newline_indent()
yield '}' yield '}'
if markers is not None: if markers is not None:
del markers[markerid] del markers[markerid]

View File

@ -0,0 +1,40 @@
from django.utils import simplejson
import cgi
class JSONFilter(object):
def __init__(self, app, mime_type='text/x-json'):
self.app = app
self.mime_type = mime_type
def __call__(self, environ, start_response):
# Read JSON POST input to jsonfilter.json if matching mime type
response = {'status': '200 OK', 'headers': []}
def json_start_response(status, headers):
response['status'] = status
response['headers'].extend(headers)
environ['jsonfilter.mime_type'] = self.mime_type
if environ.get('REQUEST_METHOD', '') == 'POST':
if environ.get('CONTENT_TYPE', '') == self.mime_type:
args = [_ for _ in [environ.get('CONTENT_LENGTH')] if _]
data = environ['wsgi.input'].read(*map(int, args))
environ['jsonfilter.json'] = simplejson.loads(data)
res = simplejson.dumps(self.app(environ, json_start_response))
jsonp = cgi.parse_qs(environ.get('QUERY_STRING', '')).get('jsonp')
if jsonp:
content_type = 'text/javascript'
res = ''.join(jsonp + ['(', res, ')'])
elif 'Opera' in environ.get('HTTP_USER_AGENT', ''):
# Opera has bunk XMLHttpRequest support for most mime types
content_type = 'text/plain'
else:
content_type = self.mime_type
headers = [
('Content-type', content_type),
('Content-length', len(res)),
]
headers.extend(response['headers'])
start_response(response['status'], headers)
return [res]
def factory(app, global_conf, **kw):
return JSONFilter(app, **kw)

View File

@ -3,11 +3,12 @@ Iterator based sre token scanner
""" """
import sre_parse, sre_compile, sre_constants import sre_parse, sre_compile, sre_constants
from sre_constants import BRANCH, SUBPATTERN from sre_constants import BRANCH, SUBPATTERN
from re import VERBOSE, MULTILINE, DOTALL
import re import re
__all__ = ['Scanner', 'pattern'] __all__ = ['Scanner', 'pattern']
FLAGS = (re.VERBOSE | re.MULTILINE | re.DOTALL) FLAGS = (VERBOSE | MULTILINE | DOTALL)
class Scanner(object): class Scanner(object):
def __init__(self, lexicon, flags=FLAGS): def __init__(self, lexicon, flags=FLAGS):
self.actions = [None] self.actions = [None]