Skip to content
This repository

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse code

Fixed #7131 -- Updated included simplejson code to match the simplejs…

…on-1.9.2

release. This should be fully backwards-compatible for people using the public
interfaces.


git-svn-id: http://code.djangoproject.com/svn/django/trunk@8124 bcc190cf-cafb-0310-a4f2-bffc1f526a37
  • Loading branch information...
commit d57ce3d6a9e4533e1814cbf39194e99b91b30f4e 1 parent de9e2ae
Malcolm Tredinnick authored July 27, 2008
1  django/utils/simplejson/LICENSE.txt
... ...
@@ -1,4 +1,3 @@
1  
-simplejson 1.5
2 1
 Copyright (c) 2006 Bob Ippolito
3 2
 
4 3
 Permission is hereby granted, free of charge, to any person obtaining a copy of
186  django/utils/simplejson/__init__.py
@@ -65,6 +65,9 @@
65 65
     >>> simplejson.loads('{"__complex__": true, "real": 1, "imag": 2}',
66 66
     ...     object_hook=as_complex)
67 67
     (1+2j)
  68
+    >>> import decimal
  69
+    >>> simplejson.loads('1.1', parse_float=decimal.Decimal)
  70
+    Decimal("1.1")
68 71
 
69 72
 Extending JSONEncoder::
70 73
     
@@ -83,20 +86,48 @@
83 86
     ['[', '2.0', ', ', '1.0', ']']
84 87
     
85 88
 
  89
+Using simplejson from the shell to validate and
  90
+pretty-print::
  91
+    
  92
+    $ echo '{"json":"obj"}' | python -msimplejson.tool
  93
+    {
  94
+        "json": "obj"
  95
+    }
  96
+    $ echo '{ 1.2:3.4}' | python -msimplejson.tool
  97
+    Expecting property name: line 1 column 2 (char 2)
  98
+
86 99
 Note that the JSON produced by this module's default settings
87 100
 is a subset of YAML, so it may be used as a serializer for that as well.
88 101
 """
89  
-__version__ = '1.5'
  102
+__version__ = '1.9.2'
90 103
 __all__ = [
91 104
     'dump', 'dumps', 'load', 'loads',
92 105
     'JSONDecoder', 'JSONEncoder',
93 106
 ]
94 107
 
95  
-from django.utils.simplejson.decoder import JSONDecoder
96  
-from django.utils.simplejson.encoder import JSONEncoder
  108
+if __name__ == '__main__':
  109
+    import warnings
  110
+    warnings.warn('python -msimplejson is deprecated, use python -msiplejson.tool', DeprecationWarning)
  111
+    from django.utils.simplejson.decoder import JSONDecoder
  112
+    from django.utils.simplejson.encoder import JSONEncoder
  113
+else:
  114
+    from decoder import JSONDecoder
  115
+    from encoder import JSONEncoder
  116
+
  117
+_default_encoder = JSONEncoder(
  118
+    skipkeys=False,
  119
+    ensure_ascii=True,
  120
+    check_circular=True,
  121
+    allow_nan=True,
  122
+    indent=None,
  123
+    separators=None,
  124
+    encoding='utf-8',
  125
+    default=None,
  126
+)
97 127
 
98 128
 def dump(obj, fp, skipkeys=False, ensure_ascii=True, check_circular=True,
99  
-        allow_nan=True, cls=None, indent=None, **kw):
  129
+        allow_nan=True, cls=None, indent=None, separators=None,
  130
+        encoding='utf-8', default=None, **kw):
100 131
     """
101 132
     Serialize ``obj`` as a JSON formatted stream to ``fp`` (a
102 133
     ``.write()``-supporting file-like object).
@@ -107,7 +138,7 @@ def dump(obj, fp, skipkeys=False, ensure_ascii=True, check_circular=True,
107 138
 
108 139
     If ``ensure_ascii`` is ``False``, then the some chunks written to ``fp``
109 140
     may be ``unicode`` instances, subject to normal Python ``str`` to
110  
-    ``unicode`` coercion rules.  Unless ``fp.write()`` explicitly
  141
+    ``unicode`` coercion rules. Unless ``fp.write()`` explicitly
111 142
     understands ``unicode`` (as in ``codecs.getwriter()``) this is likely
112 143
     to cause an error.
113 144
 
@@ -121,25 +152,44 @@ def dump(obj, fp, skipkeys=False, ensure_ascii=True, check_circular=True,
121 152
     JavaScript equivalents (``NaN``, ``Infinity``, ``-Infinity``).
122 153
 
123 154
     If ``indent`` is a non-negative integer, then JSON array elements and object
124  
-    members will be pretty-printed with that indent level.  An indent level
125  
-    of 0 will only insert newlines.  ``None`` is the most compact representation.
  155
+    members will be pretty-printed with that indent level. An indent level
  156
+    of 0 will only insert newlines. ``None`` is the most compact representation.
  157
+
  158
+    If ``separators`` is an ``(item_separator, dict_separator)`` tuple
  159
+    then it will be used instead of the default ``(', ', ': ')`` separators.
  160
+    ``(',', ':')`` is the most compact JSON representation.
  161
+
  162
+    ``encoding`` is the character encoding for str instances, default is UTF-8.
  163
+
  164
+    ``default(obj)`` is a function that should return a serializable version
  165
+    of obj or raise TypeError. The default simply raises TypeError.
126 166
 
127 167
     To use a custom ``JSONEncoder`` subclass (e.g. one that overrides the
128 168
     ``.default()`` method to serialize additional types), specify it with
129 169
     the ``cls`` kwarg.
130 170
     """
131  
-    if cls is None:
132  
-        cls = JSONEncoder
133  
-    iterable = cls(skipkeys=skipkeys, ensure_ascii=ensure_ascii,
134  
-        check_circular=check_circular, allow_nan=allow_nan, indent=indent,
135  
-        **kw).iterencode(obj)
  171
+    # cached encoder
  172
+    if (skipkeys is False and ensure_ascii is True and
  173
+        check_circular is True and allow_nan is True and
  174
+        cls is None and indent is None and separators is None and
  175
+        encoding == 'utf-8' and default is None and not kw):
  176
+        iterable = _default_encoder.iterencode(obj)
  177
+    else:
  178
+        if cls is None:
  179
+            cls = JSONEncoder
  180
+        iterable = cls(skipkeys=skipkeys, ensure_ascii=ensure_ascii,
  181
+            check_circular=check_circular, allow_nan=allow_nan, indent=indent,
  182
+            separators=separators, encoding=encoding,
  183
+            default=default, **kw).iterencode(obj)
136 184
     # could accelerate with writelines in some versions of Python, at
137 185
     # a debuggability cost
138 186
     for chunk in iterable:
139 187
         fp.write(chunk)
140 188
 
  189
+
141 190
 def dumps(obj, skipkeys=False, ensure_ascii=True, check_circular=True,
142  
-        allow_nan=True, cls=None, indent=None, separators=None, **kw):
  191
+        allow_nan=True, cls=None, indent=None, separators=None,
  192
+        encoding='utf-8', default=None, **kw):
143 193
     """
144 194
     Serialize ``obj`` to a JSON formatted ``str``.
145 195
 
@@ -161,88 +211,159 @@ def dumps(obj, skipkeys=False, ensure_ascii=True, check_circular=True,
161 211
     JavaScript equivalents (``NaN``, ``Infinity``, ``-Infinity``).
162 212
 
163 213
     If ``indent`` is a non-negative integer, then JSON array elements and
164  
-    object members will be pretty-printed with that indent level.  An indent
165  
-    level of 0 will only insert newlines.  ``None`` is the most compact
  214
+    object members will be pretty-printed with that indent level. An indent
  215
+    level of 0 will only insert newlines. ``None`` is the most compact
166 216
     representation.
167 217
 
168 218
     If ``separators`` is an ``(item_separator, dict_separator)`` tuple
169 219
     then it will be used instead of the default ``(', ', ': ')`` separators.
170 220
     ``(',', ':')`` is the most compact JSON representation.
171 221
 
  222
+    ``encoding`` is the character encoding for str instances, default is UTF-8.
  223
+
  224
+    ``default(obj)`` is a function that should return a serializable version
  225
+    of obj or raise TypeError. The default simply raises TypeError.
  226
+
172 227
     To use a custom ``JSONEncoder`` subclass (e.g. one that overrides the
173 228
     ``.default()`` method to serialize additional types), specify it with
174 229
     the ``cls`` kwarg.
175 230
     """
  231
+    # cached encoder
  232
+    if (skipkeys is False and ensure_ascii is True and
  233
+        check_circular is True and allow_nan is True and
  234
+        cls is None and indent is None and separators is None and
  235
+        encoding == 'utf-8' and default is None and not kw):
  236
+        return _default_encoder.encode(obj)
176 237
     if cls is None:
177 238
         cls = JSONEncoder
178 239
     return cls(
179 240
         skipkeys=skipkeys, ensure_ascii=ensure_ascii,
180 241
         check_circular=check_circular, allow_nan=allow_nan, indent=indent,
181  
-        separators=separators,
  242
+        separators=separators, encoding=encoding, default=default,
182 243
         **kw).encode(obj)
183 244
 
184  
-def load(fp, encoding=None, cls=None, object_hook=None, **kw):
  245
+
  246
+_default_decoder = JSONDecoder(encoding=None, object_hook=None)
  247
+
  248
+
  249
+def load(fp, encoding=None, cls=None, object_hook=None, parse_float=None,
  250
+        parse_int=None, parse_constant=None, **kw):
185 251
     """
186 252
     Deserialize ``fp`` (a ``.read()``-supporting file-like object containing
187 253
     a JSON document) to a Python object.
188 254
 
189 255
     If the contents of ``fp`` is encoded with an ASCII based encoding other
190 256
     than utf-8 (e.g. latin-1), then an appropriate ``encoding`` name must
191  
-    be specified.  Encodings that are not ASCII based (such as UCS-2) are
  257
+    be specified. Encodings that are not ASCII based (such as UCS-2) are
192 258
     not allowed, and should be wrapped with
193 259
     ``codecs.getreader(fp)(encoding)``, or simply decoded to a ``unicode``
194 260
     object and passed to ``loads()``
195 261
 
196 262
     ``object_hook`` is an optional function that will be called with the
197  
-    result of any object literal decode (a ``dict``).  The return value of
198  
-    ``object_hook`` will be used instead of the ``dict``.  This feature
  263
+    result of any object literal decode (a ``dict``). The return value of
  264
+    ``object_hook`` will be used instead of the ``dict``. This feature
199 265
     can be used to implement custom decoders (e.g. JSON-RPC class hinting).
200 266
     
201 267
     To use a custom ``JSONDecoder`` subclass, specify it with the ``cls``
202 268
     kwarg.
203 269
     """
204  
-    if cls is None:
205  
-        cls = JSONDecoder
206  
-    if object_hook is not None:
207  
-        kw['object_hook'] = object_hook
208  
-    return cls(encoding=encoding, **kw).decode(fp.read())
  270
+    return loads(fp.read(),
  271
+        encoding=encoding, cls=cls, object_hook=object_hook,
  272
+        parse_float=parse_float, parse_int=parse_int,
  273
+        parse_constant=parse_constant, **kw)
  274
+
209 275
 
210  
-def loads(s, encoding=None, cls=None, object_hook=None, **kw):
  276
+def loads(s, encoding=None, cls=None, object_hook=None, parse_float=None,
  277
+        parse_int=None, parse_constant=None, **kw):
211 278
     """
212 279
     Deserialize ``s`` (a ``str`` or ``unicode`` instance containing a JSON
213 280
     document) to a Python object.
214 281
 
215 282
     If ``s`` is a ``str`` instance and is encoded with an ASCII based encoding
216 283
     other than utf-8 (e.g. latin-1) then an appropriate ``encoding`` name
217  
-    must be specified.  Encodings that are not ASCII based (such as UCS-2)
  284
+    must be specified. Encodings that are not ASCII based (such as UCS-2)
218 285
     are not allowed and should be decoded to ``unicode`` first.
219 286
 
220 287
     ``object_hook`` is an optional function that will be called with the
221  
-    result of any object literal decode (a ``dict``).  The return value of
222  
-    ``object_hook`` will be used instead of the ``dict``.  This feature
  288
+    result of any object literal decode (a ``dict``). The return value of
  289
+    ``object_hook`` will be used instead of the ``dict``. This feature
223 290
     can be used to implement custom decoders (e.g. JSON-RPC class hinting).
224 291
 
  292
+    ``parse_float``, if specified, will be called with the string
  293
+    of every JSON float to be decoded. By default this is equivalent to
  294
+    float(num_str). This can be used to use another datatype or parser
  295
+    for JSON floats (e.g. decimal.Decimal).
  296
+
  297
+    ``parse_int``, if specified, will be called with the string
  298
+    of every JSON int to be decoded. By default this is equivalent to
  299
+    int(num_str). This can be used to use another datatype or parser
  300
+    for JSON integers (e.g. float).
  301
+
  302
+    ``parse_constant``, if specified, will be called with one of the
  303
+    following strings: -Infinity, Infinity, NaN, null, true, false.
  304
+    This can be used to raise an exception if invalid JSON numbers
  305
+    are encountered.
  306
+
225 307
     To use a custom ``JSONDecoder`` subclass, specify it with the ``cls``
226 308
     kwarg.
227 309
     """
  310
+    if (cls is None and encoding is None and object_hook is None and
  311
+            parse_int is None and parse_float is None and
  312
+            parse_constant is None and not kw):
  313
+        return _default_decoder.decode(s)
228 314
     if cls is None:
229 315
         cls = JSONDecoder
230 316
     if object_hook is not None:
231 317
         kw['object_hook'] = object_hook
  318
+    if parse_float is not None:
  319
+        kw['parse_float'] = parse_float
  320
+    if parse_int is not None:
  321
+        kw['parse_int'] = parse_int
  322
+    if parse_constant is not None:
  323
+        kw['parse_constant'] = parse_constant
232 324
     return cls(encoding=encoding, **kw).decode(s)
233 325
 
  326
+
  327
+#
  328
+# Compatibility cruft from other libraries
  329
+#
  330
+
  331
+
  332
+def decode(s):
  333
+    """
  334
+    demjson, python-cjson API compatibility hook. Use loads(s) instead.
  335
+    """
  336
+    import warnings
  337
+    warnings.warn("simplejson.loads(s) should be used instead of decode(s)",
  338
+        DeprecationWarning)
  339
+    return loads(s)
  340
+
  341
+
  342
+def encode(obj):
  343
+    """
  344
+    demjson, python-cjson compatibility hook. Use dumps(s) instead.
  345
+    """
  346
+    import warnings
  347
+    warnings.warn("simplejson.dumps(s) should be used instead of encode(s)",
  348
+        DeprecationWarning)
  349
+    return dumps(obj)
  350
+
  351
+
234 352
 def read(s):
235 353
     """
236  
-    json-py API compatibility hook.  Use loads(s) instead.
  354
+    jsonlib, JsonUtils, python-json, json-py API compatibility hook.
  355
+    Use loads(s) instead.
237 356
     """
238 357
     import warnings
239 358
     warnings.warn("simplejson.loads(s) should be used instead of read(s)",
240 359
         DeprecationWarning)
241 360
     return loads(s)
242 361
 
  362
+
243 363
 def write(obj):
244 364
     """
245  
-    json-py API compatibility hook.  Use dumps(s) instead.
  365
+    jsonlib, JsonUtils, python-json, json-py API compatibility hook.
  366
+    Use dumps(s) instead.
246 367
     """
247 368
     import warnings
248 369
     warnings.warn("simplejson.dumps(s) should be used instead of write(s)",
@@ -250,3 +371,6 @@ def write(obj):
250 371
     return dumps(obj)
251 372
 
252 373
 
  374
+if __name__ == '__main__':
  375
+    import simplejson.tool
  376
+    simplejson.tool.main()
104  django/utils/simplejson/decoder.py
@@ -2,8 +2,13 @@
2 2
 Implementation of JSONDecoder
3 3
 """
4 4
 import re
  5
+import sys
5 6
 
6 7
 from django.utils.simplejson.scanner import Scanner, pattern
  8
+try:
  9
+    from django.utils.simplejson._speedups import scanstring as c_scanstring
  10
+except ImportError:
  11
+    pass
7 12
 
8 13
 FLAGS = re.VERBOSE | re.MULTILINE | re.DOTALL
9 14
 
@@ -18,6 +23,7 @@ def _floatconstants():
18 23
 
19 24
 NaN, PosInf, NegInf = _floatconstants()
20 25
 
  26
+
21 27
 def linecol(doc, pos):
22 28
     lineno = doc.count('\n', 0, pos) + 1
23 29
     if lineno == 1:
@@ -26,6 +32,7 @@ def linecol(doc, pos):
26 32
         colno = pos - doc.rindex('\n', 0, pos)
27 33
     return lineno, colno
28 34
 
  35
+
29 36
 def errmsg(msg, doc, pos, end=None):
30 37
     lineno, colno = linecol(doc, pos)
31 38
     if end is None:
@@ -34,6 +41,7 @@ def errmsg(msg, doc, pos, end=None):
34 41
     return '%s: line %d column %d - line %d column %d (char %d - %d)' % (
35 42
         msg, lineno, colno, endlineno, endcolno, pos, end)
36 43
 
  44
+
37 45
 _CONSTANTS = {
38 46
     '-Infinity': NegInf,
39 47
     'Infinity': PosInf,
@@ -44,20 +52,30 @@ def errmsg(msg, doc, pos, end=None):
44 52
 }
45 53
 
46 54
 def JSONConstant(match, context, c=_CONSTANTS):
47  
-    return c[match.group(0)], None
  55
+    s = match.group(0)
  56
+    fn = getattr(context, 'parse_constant', None)
  57
+    if fn is None:
  58
+        rval = c[s]
  59
+    else:
  60
+        rval = fn(s)
  61
+    return rval, None
48 62
 pattern('(-?Infinity|NaN|true|false|null)')(JSONConstant)
49 63
 
  64
+
50 65
 def JSONNumber(match, context):
51 66
     match = JSONNumber.regex.match(match.string, *match.span())
52 67
     integer, frac, exp = match.groups()
53 68
     if frac or exp:
54  
-        res = float(integer + (frac or '') + (exp or ''))
  69
+        fn = getattr(context, 'parse_float', None) or float
  70
+        res = fn(integer + (frac or '') + (exp or ''))
55 71
     else:
56  
-        res = int(integer)
  72
+        fn = getattr(context, 'parse_int', None) or int
  73
+        res = fn(integer)
57 74
     return res, None
58 75
 pattern(r'(-?(?:0|[1-9]\d*))(\.\d+)?([eE][-+]?\d+)?')(JSONNumber)
59 76
 
60  
-STRINGCHUNK = re.compile(r'(.*?)(["\\])', FLAGS)
  77
+
  78
+STRINGCHUNK = re.compile(r'(.*?)(["\\\x00-\x1f])', FLAGS)
61 79
 BACKSLASH = {
62 80
     '"': u'"', '\\': u'\\', '/': u'/',
63 81
     'b': u'\b', 'f': u'\f', 'n': u'\n', 'r': u'\r', 't': u'\t',
@@ -65,7 +83,7 @@ def JSONNumber(match, context):
65 83
 
66 84
 DEFAULT_ENCODING = "utf-8"
67 85
 
68  
-def scanstring(s, end, encoding=None, _b=BACKSLASH, _m=STRINGCHUNK.match):
  86
+def py_scanstring(s, end, encoding=None, strict=True, _b=BACKSLASH, _m=STRINGCHUNK.match):
69 87
     if encoding is None:
70 88
         encoding = DEFAULT_ENCODING
71 89
     chunks = []
@@ -84,6 +102,12 @@ def scanstring(s, end, encoding=None, _b=BACKSLASH, _m=STRINGCHUNK.match):
84 102
             _append(content)
85 103
         if terminator == '"':
86 104
             break
  105
+        elif terminator != '\\':
  106
+            if strict:
  107
+                raise ValueError(errmsg("Invalid control character %r at", s, end))
  108
+            else:
  109
+                _append(terminator)
  110
+                continue
87 111
         try:
88 112
             esc = s[end]
89 113
         except IndexError:
@@ -98,21 +122,43 @@ def scanstring(s, end, encoding=None, _b=BACKSLASH, _m=STRINGCHUNK.match):
98 122
             end += 1
99 123
         else:
100 124
             esc = s[end + 1:end + 5]
  125
+            next_end = end + 5
  126
+            msg = "Invalid \\uXXXX escape"
101 127
             try:
102  
-                m = unichr(int(esc, 16))
103  
-                if len(esc) != 4 or not esc.isalnum():
  128
+                if len(esc) != 4:
104 129
                     raise ValueError
  130
+                uni = int(esc, 16)
  131
+                if 0xd800 <= uni <= 0xdbff and sys.maxunicode > 65535:
  132
+                    msg = "Invalid \\uXXXX\\uXXXX surrogate pair"
  133
+                    if not s[end + 5:end + 7] == '\\u':
  134
+                        raise ValueError
  135
+                    esc2 = s[end + 7:end + 11]
  136
+                    if len(esc2) != 4:
  137
+                        raise ValueError
  138
+                    uni2 = int(esc2, 16)
  139
+                    uni = 0x10000 + (((uni - 0xd800) << 10) | (uni2 - 0xdc00))
  140
+                    next_end += 6
  141
+                m = unichr(uni)
105 142
             except ValueError:
106  
-                raise ValueError(errmsg("Invalid \\uXXXX escape", s, end))
107  
-            end += 5
  143
+                raise ValueError(errmsg(msg, s, end))
  144
+            end = next_end
108 145
         _append(m)
109 146
     return u''.join(chunks), end
110 147
 
  148
+
  149
+# Use speedup
  150
+try:
  151
+    scanstring = c_scanstring
  152
+except NameError:
  153
+    scanstring = py_scanstring
  154
+
111 155
 def JSONString(match, context):
112 156
     encoding = getattr(context, 'encoding', None)
113  
-    return scanstring(match.string, match.end(), encoding)
  157
+    strict = getattr(context, 'strict', True)
  158
+    return scanstring(match.string, match.end(), encoding, strict)
114 159
 pattern(r'"')(JSONString)
115 160
 
  161
+
116 162
 WHITESPACE = re.compile(r'\s*', FLAGS)
117 163
 
118 164
 def JSONObject(match, context, _w=WHITESPACE.match):
@@ -120,16 +166,17 @@ def JSONObject(match, context, _w=WHITESPACE.match):
120 166
     s = match.string
121 167
     end = _w(s, match.end()).end()
122 168
     nextchar = s[end:end + 1]
123  
-    # trivial empty object
  169
+    # Trivial empty object
124 170
     if nextchar == '}':
125 171
         return pairs, end + 1
126 172
     if nextchar != '"':
127 173
         raise ValueError(errmsg("Expecting property name", s, end))
128 174
     end += 1
129 175
     encoding = getattr(context, 'encoding', None)
  176
+    strict = getattr(context, 'strict', True)
130 177
     iterscan = JSONScanner.iterscan
131 178
     while True:
132  
-        key, end = scanstring(s, end, encoding)
  179
+        key, end = scanstring(s, end, encoding, strict)
133 180
         end = _w(s, end).end()
134 181
         if s[end:end + 1] != ':':
135 182
             raise ValueError(errmsg("Expecting : delimiter", s, end))
@@ -156,12 +203,13 @@ def JSONObject(match, context, _w=WHITESPACE.match):
156 203
         pairs = object_hook(pairs)
157 204
     return pairs, end
158 205
 pattern(r'{')(JSONObject)
159  
-            
  206
+
  207
+
160 208
 def JSONArray(match, context, _w=WHITESPACE.match):
161 209
     values = []
162 210
     s = match.string
163 211
     end = _w(s, match.end()).end()
164  
-    # look-ahead for trivial empty array
  212
+    # Look-ahead for trivial empty array
165 213
     nextchar = s[end:end + 1]
166 214
     if nextchar == ']':
167 215
         return values, end + 1
@@ -182,7 +230,8 @@ def JSONArray(match, context, _w=WHITESPACE.match):
182 230
         end = _w(s, end).end()
183 231
     return values, end
184 232
 pattern(r'\[')(JSONArray)
185  
- 
  233
+
  234
+
186 235
 ANYTHING = [
187 236
     JSONObject,
188 237
     JSONArray,
@@ -193,11 +242,12 @@ def JSONArray(match, context, _w=WHITESPACE.match):
193 242
 
194 243
 JSONScanner = Scanner(ANYTHING)
195 244
 
  245
+
196 246
 class JSONDecoder(object):
197 247
     """
198 248
     Simple JSON <http://json.org> decoder
199 249
 
200  
-    Performs the following translations in decoding:
  250
+    Performs the following translations in decoding by default:
201 251
     
202 252
     +---------------+-------------------+
203 253
     | JSON          | Python            |
@@ -226,7 +276,8 @@ class JSONDecoder(object):
226 276
     _scanner = Scanner(ANYTHING)
227 277
     __all__ = ['__init__', 'decode', 'raw_decode']
228 278
 
229  
-    def __init__(self, encoding=None, object_hook=None):
  279
+    def __init__(self, encoding=None, object_hook=None, parse_float=None,
  280
+            parse_int=None, parse_constant=None, strict=True):
230 281
         """
231 282
         ``encoding`` determines the encoding used to interpret any ``str``
232 283
         objects decoded by this instance (utf-8 by default).  It has no
@@ -239,9 +290,28 @@ def __init__(self, encoding=None, object_hook=None):
239 290
         of every JSON object decoded and its return value will be used in
240 291
         place of the given ``dict``.  This can be used to provide custom
241 292
         deserializations (e.g. to support JSON-RPC class hinting).
  293
+
  294
+        ``parse_float``, if specified, will be called with the string
  295
+        of every JSON float to be decoded. By default this is equivalent to
  296
+        float(num_str). This can be used to use another datatype or parser
  297
+        for JSON floats (e.g. decimal.Decimal).
  298
+
  299
+        ``parse_int``, if specified, will be called with the string
  300
+        of every JSON int to be decoded. By default this is equivalent to
  301
+        int(num_str). This can be used to use another datatype or parser
  302
+        for JSON integers (e.g. float).
  303
+
  304
+        ``parse_constant``, if specified, will be called with one of the
  305
+        following strings: -Infinity, Infinity, NaN, null, true, false.
  306
+        This can be used to raise an exception if invalid JSON numbers
  307
+        are encountered.
242 308
         """
243 309
         self.encoding = encoding
244 310
         self.object_hook = object_hook
  311
+        self.parse_float = parse_float
  312
+        self.parse_int = parse_int
  313
+        self.parse_constant = parse_constant
  314
+        self.strict = strict
245 315
 
246 316
     def decode(self, s, _w=WHITESPACE.match):
247 317
         """
86  django/utils/simplejson/encoder.py
@@ -3,11 +3,15 @@
3 3
 """
4 4
 import re
5 5
 
6  
-ESCAPE = re.compile(r'[\x00-\x19\\"\b\f\n\r\t]')
7  
-ESCAPE_ASCII = re.compile(r'([\\"/]|[^\ -~])')
  6
+try:
  7
+    from django.utils.simplejson._speedups import encode_basestring_ascii as c_encode_basestring_ascii
  8
+except ImportError:
  9
+    pass
  10
+
  11
+ESCAPE = re.compile(r'[\x00-\x1f\\"\b\f\n\r\t]')
  12
+ESCAPE_ASCII = re.compile(r'([\\"]|[^\ -~])')
  13
+HAS_UTF8 = re.compile(r'[\x80-\xff]')
8 14
 ESCAPE_DCT = {
9  
-    # escape all forward slashes to prevent </script> attack
10  
-    '/': '\\/',
11 15
     '\\': '\\\\',
12 16
     '"': '\\"',
13 17
     '\b': '\\b',
@@ -19,8 +23,9 @@
19 23
 for i in range(0x20):
20 24
     ESCAPE_DCT.setdefault(chr(i), '\\u%04x' % (i,))
21 25
 
22  
-# assume this produces an infinity on all machines (probably not guaranteed)
  26
+# Assume this produces an infinity on all machines (probably not guaranteed)
23 27
 INFINITY = float('1e66666')
  28
+FLOAT_REPR = repr
24 29
 
25 30
 def floatstr(o, allow_nan=True):
26 31
     # Check for specials.  Note that this type of test is processor- and/or
@@ -33,7 +38,7 @@ def floatstr(o, allow_nan=True):
33 38
     elif o == -INFINITY:
34 39
         text = '-Infinity'
35 40
     else:
36  
-        return str(o)
  41
+        return FLOAT_REPR(o)
37 42
 
38 43
     if not allow_nan:
39 44
         raise ValueError("Out of range float values are not JSON compliant: %r"
@@ -50,15 +55,32 @@ def replace(match):
50 55
         return ESCAPE_DCT[match.group(0)]
51 56
     return '"' + ESCAPE.sub(replace, s) + '"'
52 57
 
53  
-def encode_basestring_ascii(s):
  58
+
  59
+def py_encode_basestring_ascii(s):
  60
+    if isinstance(s, str) and HAS_UTF8.search(s) is not None:
  61
+        s = s.decode('utf-8')
54 62
     def replace(match):
55 63
         s = match.group(0)
56 64
         try:
57 65
             return ESCAPE_DCT[s]
58 66
         except KeyError:
59  
-            return '\\u%04x' % (ord(s),)
  67
+            n = ord(s)
  68
+            if n < 0x10000:
  69
+                return '\\u%04x' % (n,)
  70
+            else:
  71
+                # surrogate pair
  72
+                n -= 0x10000
  73
+                s1 = 0xd800 | ((n >> 10) & 0x3ff)
  74
+                s2 = 0xdc00 | (n & 0x3ff)
  75
+                return '\\u%04x\\u%04x' % (s1, s2)
60 76
     return '"' + str(ESCAPE_ASCII.sub(replace, s)) + '"'
61  
-        
  77
+
  78
+
  79
+try:
  80
+    encode_basestring_ascii = c_encode_basestring_ascii
  81
+except NameError:
  82
+    encode_basestring_ascii = py_encode_basestring_ascii
  83
+
62 84
 
63 85
 class JSONEncoder(object):
64 86
     """
@@ -94,7 +116,7 @@ class JSONEncoder(object):
94 116
     key_separator = ': '
95 117
     def __init__(self, skipkeys=False, ensure_ascii=True,
96 118
             check_circular=True, allow_nan=True, sort_keys=False,
97  
-            indent=None, separators=None):
  119
+            indent=None, separators=None, encoding='utf-8', default=None):
98 120
         """
99 121
         Constructor for JSONEncoder, with sensible defaults.
100 122
 
@@ -126,8 +148,16 @@ def __init__(self, skipkeys=False, ensure_ascii=True,
126 148
         None is the most compact representation.
127 149
 
128 150
         If specified, separators should be a (item_separator, key_separator)
129  
-        tuple. The default is (', ', ': '). To get the most compact JSON
  151
+        tuple.  The default is (', ', ': ').  To get the most compact JSON
130 152
         representation you should specify (',', ':') to eliminate whitespace.
  153
+
  154
+        If specified, default is a function that gets called for objects
  155
+        that can't otherwise be serialized.  It should return a JSON encodable
  156
+        version of the object or raise a ``TypeError``.
  157
+
  158
+        If encoding is not None, then all input strings will be
  159
+        transformed into unicode using that encoding prior to JSON-encoding.
  160
+        The default is UTF-8.
131 161
         """
132 162
 
133 163
         self.skipkeys = skipkeys
@@ -139,6 +169,9 @@ def __init__(self, skipkeys=False, ensure_ascii=True,
139 169
         self.current_indent_level = 0
140 170
         if separators is not None:
141 171
             self.item_separator, self.key_separator = separators
  172
+        if default is not None:
  173
+            self.default = default
  174
+        self.encoding = encoding
142 175
 
143 176
     def _newline_indent(self):
144 177
         return '\n' + (' ' * (self.indent * self.current_indent_level))
@@ -207,8 +240,14 @@ def _iterencode_dict(self, dct, markers=None):
207 240
             items = [(k, dct[k]) for k in keys]
208 241
         else:
209 242
             items = dct.iteritems()
  243
+        _encoding = self.encoding
  244
+        _do_decode = (_encoding is not None
  245
+            and not (_encoding == 'utf-8'))
210 246
         for key, value in items:
211  
-            if isinstance(key, basestring):
  247
+            if isinstance(key, str):
  248
+                if _do_decode:
  249
+                    key = key.decode(_encoding)
  250
+            elif isinstance(key, basestring):
212 251
                 pass
213 252
             # JavaScript is weakly typed for these, so it makes sense to
214 253
             # also allow them.  Many encoders seem to do something like this.
@@ -247,6 +286,10 @@ def _iterencode(self, o, markers=None):
247 286
                 encoder = encode_basestring_ascii
248 287
             else:
249 288
                 encoder = encode_basestring
  289
+            _encoding = self.encoding
  290
+            if (_encoding is not None and isinstance(o, str)
  291
+                    and not (_encoding == 'utf-8')):
  292
+                o = o.decode(_encoding)
250 293
             yield encoder(o)
251 294
         elif o is None:
252 295
             yield 'null'
@@ -304,11 +347,22 @@ def encode(self, o):
304 347
         Return a JSON string representation of a Python data structure.
305 348
 
306 349
         >>> JSONEncoder().encode({"foo": ["bar", "baz"]})
307  
-        '{"foo":["bar", "baz"]}'
  350
+        '{"foo": ["bar", "baz"]}'
308 351
         """
309  
-        # This doesn't pass the iterator directly to ''.join() because it
310  
-        # sucks at reporting exceptions.  It's going to do this internally
311  
-        # anyway because it uses PySequence_Fast or similar.
  352
+        # This is for extremely simple cases and benchmarks.
  353
+        if isinstance(o, basestring):
  354
+            if isinstance(o, str):
  355
+                _encoding = self.encoding
  356
+                if (_encoding is not None 
  357
+                        and not (_encoding == 'utf-8')):
  358
+                    o = o.decode(_encoding)
  359
+            if self.ensure_ascii:
  360
+                return encode_basestring_ascii(o)
  361
+            else:
  362
+                return encode_basestring(o)
  363
+        # This doesn't pass the iterator directly to ''.join() because the
  364
+        # exceptions aren't as detailed.  The list call should be roughly
  365
+        # equivalent to the PySequence_Fast that ''.join() would do.
312 366
         chunks = list(self.iterencode(o))
313 367
         return ''.join(chunks)
314 368
 
40  django/utils/simplejson/jsonfilter.py
... ...
@@ -1,40 +0,0 @@
1  
-from django.utils import simplejson
2  
-import cgi
3  
-
4  
-class JSONFilter(object):
5  
-    def __init__(self, app, mime_type='text/x-json'):
6  
-        self.app = app
7  
-        self.mime_type = mime_type
8  
-
9  
-    def __call__(self, environ, start_response):
10  
-        # Read JSON POST input to jsonfilter.json if matching mime type
11  
-        response = {'status': '200 OK', 'headers': []}
12  
-        def json_start_response(status, headers):
13  
-            response['status'] = status
14  
-            response['headers'].extend(headers)
15  
-        environ['jsonfilter.mime_type'] = self.mime_type
16  
-        if environ.get('REQUEST_METHOD', '') == 'POST':
17  
-            if environ.get('CONTENT_TYPE', '') == self.mime_type:
18  
-                args = [_ for _ in [environ.get('CONTENT_LENGTH')] if _]
19  
-                data = environ['wsgi.input'].read(*map(int, args))
20  
-                environ['jsonfilter.json'] = simplejson.loads(data)
21  
-        res = simplejson.dumps(self.app(environ, json_start_response))
22  
-        jsonp = cgi.parse_qs(environ.get('QUERY_STRING', '')).get('jsonp')
23  
-        if jsonp:
24  
-            content_type = 'text/javascript'
25  
-            res = ''.join(jsonp + ['(', res, ')'])
26  
-        elif 'Opera' in environ.get('HTTP_USER_AGENT', ''):
27  
-            # Opera has bunk XMLHttpRequest support for most mime types
28  
-            content_type = 'text/plain'
29  
-        else:
30  
-            content_type = self.mime_type
31  
-        headers = [
32  
-            ('Content-type', content_type),
33  
-            ('Content-length', len(res)),
34  
-        ]
35  
-        headers.extend(response['headers'])
36  
-        start_response(response['status'], headers)
37  
-        return [res]
38  
-
39  
-def factory(app, global_conf, **kw):
40  
-    return JSONFilter(app, **kw)
18  django/utils/simplejson/scanner.py
... ...
@@ -1,18 +1,21 @@
1 1
 """
2 2
 Iterator based sre token scanner
3 3
 """
4  
-import sre_parse, sre_compile, sre_constants
5  
-from sre_constants import BRANCH, SUBPATTERN
6  
-from re import VERBOSE, MULTILINE, DOTALL
7 4
 import re
  5
+from re import VERBOSE, MULTILINE, DOTALL
  6
+import sre_parse
  7
+import sre_compile
  8
+import sre_constants
  9
+from sre_constants import BRANCH, SUBPATTERN
8 10
 
9 11
 __all__ = ['Scanner', 'pattern']
10 12
 
11 13
 FLAGS = (VERBOSE | MULTILINE | DOTALL)
  14
+
12 15
 class Scanner(object):
13 16
     def __init__(self, lexicon, flags=FLAGS):
14 17
         self.actions = [None]
15  
-        # combine phrases into a compound pattern
  18
+        # Combine phrases into a compound pattern
16 19
         s = sre_parse.Pattern()
17 20
         s.flags = flags
18 21
         p = []
@@ -26,10 +29,10 @@ def __init__(self, lexicon, flags=FLAGS):
26 29
             p.append(subpattern)
27 30
             self.actions.append(token)
28 31
 
  32
+        s.groups = len(p) + 1 # NOTE(guido): Added to make SRE validation work
29 33
         p = sre_parse.SubPattern(s, [(BRANCH, (None, p))])
30 34
         self.scanner = sre_compile.compile(p)
31 35
 
32  
-
33 36
     def iterscan(self, string, idx=0, context=None):
34 37
         """
35 38
         Yield match, end_idx for each match
@@ -54,10 +57,11 @@ def iterscan(self, string, idx=0, context=None):
54 57
                     match = self.scanner.scanner(string, matchend).match
55 58
                 yield rval, matchend
56 59
             lastend = matchend
57  
-            
  60
+
  61
+
58 62
 def pattern(pattern, flags=FLAGS):
59 63
     def decorator(fn):
60 64
         fn.pattern = pattern
61 65
         fn.regex = re.compile(pattern, flags)
62 66
         return fn
63  
-    return decorator
  67
+    return decorator
44  django/utils/simplejson/tool.py
... ...
@@ -0,0 +1,44 @@
  1
+r"""
  2
+Using simplejson from the shell to validate and
  3
+pretty-print::
  4
+    
  5
+    $ echo '{"json":"obj"}' | python -msimplejson
  6
+    {
  7
+        "json": "obj"
  8
+    }
  9
+    $ echo '{ 1.2:3.4}' | python -msimplejson
  10
+    Expecting property name: line 1 column 2 (char 2)
  11
+
  12
+Note that the JSON produced by this module's default settings
  13
+is a subset of YAML, so it may be used as a serializer for that as well.
  14
+"""
  15
+import django.utils.simplejson
  16
+
  17
+#
  18
+# Pretty printer:
  19
+#     curl http://mochikit.com/examples/ajax_tables/domains.json | python -msimplejson.tool
  20
+#
  21
+
  22
+def main():
  23
+    import sys
  24
+    if len(sys.argv) == 1:
  25
+        infile = sys.stdin
  26
+        outfile = sys.stdout
  27
+    elif len(sys.argv) == 2:
  28
+        infile = open(sys.argv[1], 'rb')
  29
+        outfile = sys.stdout
  30
+    elif len(sys.argv) == 3:
  31
+        infile = open(sys.argv[1], 'rb')
  32
+        outfile = open(sys.argv[2], 'wb')
  33
+    else:
  34
+        raise SystemExit("%s [infile [outfile]]" % (sys.argv[0],))
  35
+    try:
  36
+        obj = simplejson.load(infile)
  37
+    except ValueError, e:
  38
+        raise SystemExit(e)
  39
+    simplejson.dump(obj, outfile, sort_keys=True, indent=4)
  40
+    outfile.write('\n')
  41
+
  42
+
  43
+if __name__ == '__main__':
  44
+    main()

0 notes on commit d57ce3d

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