Code

Ticket #3320: simplejson_1_5.diff

File simplejson_1_5.diff, 12.4 KB (added by LawrenceOluyede <l.oluyede@…>, 7 years ago)
Line 
1Index: LICENSE.txt
2===================================================================
3--- LICENSE.txt (revision 4347)
4+++ LICENSE.txt (working copy)
5@@ -1,4 +1,3 @@
6-simplejson 1.3
7 Copyright (c) 2006 Bob Ippolito
8 
9 Permission is hereby granted, free of charge, to any person obtaining a copy of
10Index: scanner.py
11===================================================================
12--- scanner.py  (revision 4347)
13+++ scanner.py  (working copy)
14@@ -3,11 +3,12 @@
15 """
16 import sre_parse, sre_compile, sre_constants
17 from sre_constants import BRANCH, SUBPATTERN
18+from re import VERBOSE, MULTILINE, DOTALL
19 import re
20 
21 __all__ = ['Scanner', 'pattern']
22 
23-FLAGS = (re.VERBOSE | re.MULTILINE | re.DOTALL)
24+FLAGS = (VERBOSE | MULTILINE | DOTALL)
25 class Scanner(object):
26     def __init__(self, lexicon, flags=FLAGS):
27         self.actions = [None]
28Index: __init__.py
29===================================================================
30--- __init__.py (revision 4347)
31+++ __init__.py (working copy)
32@@ -27,6 +27,21 @@
33     >>> io.getvalue()
34     '["streaming API"]'
35 
36+Compact encoding::
37+
38+    >>> import simplejson
39+    >>> simplejson.dumps([1,2,3,{'4': 5, '6': 7}], separators=(',',':'))
40+    '[1,2,3,{"4":5,"6":7}]'
41+
42+Pretty printing::
43+
44+    >>> import simplejson
45+    >>> print simplejson.dumps({'4': 5, '6': 7}, sort_keys=True, indent=4)
46+    {
47+        "4": 5,
48+        "6": 7
49+    }
50+
51 Decoding JSON::
52     
53     >>> import simplejson
54@@ -68,20 +83,20 @@
55     ['[', '2.0', ', ', '1.0', ']']
56     
57 
58-Note that the JSON produced by this module is a subset of YAML,
59-so it may be used as a serializer for that as well.
60+Note that the JSON produced by this module's default settings
61+is a subset of YAML, so it may be used as a serializer for that as well.
62 """
63-__version__ = '1.3'
64+__version__ = '1.5'
65 __all__ = [
66     'dump', 'dumps', 'load', 'loads',
67     'JSONDecoder', 'JSONEncoder',
68 ]
69 
70-from django.utils.simplejson.decoder import JSONDecoder
71-from django.utils.simplejson.encoder import JSONEncoder
72+from decoder import JSONDecoder
73+from encoder import JSONEncoder
74 
75 def dump(obj, fp, skipkeys=False, ensure_ascii=True, check_circular=True,
76-        allow_nan=True, cls=None, **kw):
77+        allow_nan=True, cls=None, indent=None, **kw):
78     """
79     Serialize ``obj`` as a JSON formatted stream to ``fp`` (a
80     ``.write()``-supporting file-like object).
81@@ -105,6 +120,10 @@
82     in strict compliance of the JSON specification, instead of using the
83     JavaScript equivalents (``NaN``, ``Infinity``, ``-Infinity``).
84 
85+    If ``indent`` is a non-negative integer, then JSON array elements and object
86+    members will be pretty-printed with that indent level.  An indent level
87+    of 0 will only insert newlines.  ``None`` is the most compact representation.
88+
89     To use a custom ``JSONEncoder`` subclass (e.g. one that overrides the
90     ``.default()`` method to serialize additional types), specify it with
91     the ``cls`` kwarg.
92@@ -112,7 +131,7 @@
93     if cls is None:
94         cls = JSONEncoder
95     iterable = cls(skipkeys=skipkeys, ensure_ascii=ensure_ascii,
96-        check_circular=check_circular, allow_nan=allow_nan,
97+        check_circular=check_circular, allow_nan=allow_nan, indent=indent,
98         **kw).iterencode(obj)
99     # could accelerate with writelines in some versions of Python, at
100     # a debuggability cost
101@@ -120,7 +139,7 @@
102         fp.write(chunk)
103 
104 def dumps(obj, skipkeys=False, ensure_ascii=True, check_circular=True,
105-        allow_nan=True, cls=None, **kw):
106+        allow_nan=True, cls=None, indent=None, separators=None, **kw):
107     """
108     Serialize ``obj`` to a JSON formatted ``str``.
109 
110@@ -141,14 +160,26 @@
111     strict compliance of the JSON specification, instead of using the
112     JavaScript equivalents (``NaN``, ``Infinity``, ``-Infinity``).
113 
114+    If ``indent`` is a non-negative integer, then JSON array elements and
115+    object members will be pretty-printed with that indent level.  An indent
116+    level of 0 will only insert newlines.  ``None`` is the most compact
117+    representation.
118+
119+    If ``separators`` is an ``(item_separator, dict_separator)`` tuple
120+    then it will be used instead of the default ``(', ', ': ')`` separators.
121+    ``(',', ':')`` is the most compact JSON representation.
122+
123     To use a custom ``JSONEncoder`` subclass (e.g. one that overrides the
124     ``.default()`` method to serialize additional types), specify it with
125     the ``cls`` kwarg.
126     """
127     if cls is None:
128         cls = JSONEncoder
129-    return cls(skipkeys=skipkeys, ensure_ascii=ensure_ascii,
130-        check_circular=check_circular, allow_nan=allow_nan, **kw).encode(obj)
131+    return cls(
132+        skipkeys=skipkeys, ensure_ascii=ensure_ascii,
133+        check_circular=check_circular, allow_nan=allow_nan, indent=indent,
134+        separators=separators,
135+        **kw).encode(obj)
136 
137 def load(fp, encoding=None, cls=None, object_hook=None, **kw):
138     """
139Index: encoder.py
140===================================================================
141--- encoder.py  (revision 4347)
142+++ encoder.py  (working copy)
143@@ -3,11 +3,11 @@
144 """
145 import re
146 
147-# this should match any kind of infinity
148-INFCHARS = re.compile(r'[infINF]')
149 ESCAPE = re.compile(r'[\x00-\x19\\"\b\f\n\r\t]')
150-ESCAPE_ASCII = re.compile(r'([\\"]|[^\ -~])')
151+ESCAPE_ASCII = re.compile(r'([\\"/]|[^\ -~])')
152 ESCAPE_DCT = {
153+    # escape all forward slashes to prevent </script> attack
154+    '/': '\\/',
155     '\\': '\\\\',
156     '"': '\\"',
157     '\b': '\\b',
158@@ -16,32 +16,32 @@
159     '\r': '\\r',
160     '\t': '\\t',
161 }
162-for i in range(20):
163+for i in range(0x20):
164     ESCAPE_DCT.setdefault(chr(i), '\\u%04x' % (i,))
165 
166+# assume this produces an infinity on all machines (probably not guaranteed)
167+INFINITY = float('1e66666')
168+
169 def floatstr(o, allow_nan=True):
170-    s = str(o)
171-    # If the first non-sign is a digit then it's not a special value
172-    if (o < 0.0 and s[1].isdigit()) or s[0].isdigit():
173-        return s
174-    elif not allow_nan:
175+    # Check for specials.  Note that this type of test is processor- and/or
176+    # platform-specific, so do tests which don't depend on the internals.
177+
178+    if o != o:
179+        text = 'NaN'
180+    elif o == INFINITY:
181+        text = 'Infinity'
182+    elif o == -INFINITY:
183+        text = '-Infinity'
184+    else:
185+        return str(o)
186+
187+    if not allow_nan:
188         raise ValueError("Out of range float values are not JSON compliant: %r"
189             % (o,))
190-    # These are the string representations on the platforms I've tried
191-    if s == 'nan':
192-        return 'NaN'
193-    if s == 'inf':
194-        return 'Infinity'
195-    if s == '-inf':
196-        return '-Infinity'
197-    # NaN should either be inequal to itself, or equal to everything
198-    if o != o or o == 0.0:
199-        return 'NaN'
200-    # Last ditch effort, assume inf
201-    if o < 0:
202-        return '-Infinity'
203-    return 'Infinity'
204 
205+    return text
206+
207+
208 def encode_basestring(s):
209     """
210     Return a JSON representation of a Python string
211@@ -90,8 +90,11 @@
212     implementation (to raise ``TypeError``).
213     """
214     __all__ = ['__init__', 'default', 'encode', 'iterencode']
215+    item_separator = ', '
216+    key_separator = ': '
217     def __init__(self, skipkeys=False, ensure_ascii=True,
218-            check_circular=True, allow_nan=True, sort_keys=False):
219+            check_circular=True, allow_nan=True, sort_keys=False,
220+            indent=None, separators=None):
221         """
222         Constructor for JSONEncoder, with sensible defaults.
223 
224@@ -116,6 +119,15 @@
225         If sort_keys is True, then the output of dictionaries will be
226         sorted by key; this is useful for regression tests to ensure
227         that JSON serializations can be compared on a day-to-day basis.
228+
229+        If indent is a non-negative integer, then JSON array
230+        elements and object members will be pretty-printed with that
231+        indent level.  An indent level of 0 will only insert newlines.
232+        None is the most compact representation.
233+
234+        If specified, separators should be a (item_separator, key_separator)
235+        tuple. The default is (', ', ': '). To get the most compact JSON
236+        representation you should specify (',', ':') to eliminate whitespace.
237         """
238 
239         self.skipkeys = skipkeys
240@@ -123,7 +135,14 @@
241         self.check_circular = check_circular
242         self.allow_nan = allow_nan
243         self.sort_keys = sort_keys
244+        self.indent = indent
245+        self.current_indent_level = 0
246+        if separators is not None:
247+            self.item_separator, self.key_separator = separators
248 
249+    def _newline_indent(self):
250+        return '\n' + (' ' * (self.indent * self.current_indent_level))
251+
252     def _iterencode_list(self, lst, markers=None):
253         if not lst:
254             yield '[]'
255@@ -134,14 +153,25 @@
256                 raise ValueError("Circular reference detected")
257             markers[markerid] = lst
258         yield '['
259+        if self.indent is not None:
260+            self.current_indent_level += 1
261+            newline_indent = self._newline_indent()
262+            separator = self.item_separator + newline_indent
263+            yield newline_indent
264+        else:
265+            newline_indent = None
266+            separator = self.item_separator
267         first = True
268         for value in lst:
269             if first:
270                 first = False
271             else:
272-                yield ', '
273+                yield separator
274             for chunk in self._iterencode(value, markers):
275                 yield chunk
276+        if newline_indent is not None:
277+            self.current_indent_level -= 1
278+            yield self._newline_indent()
279         yield ']'
280         if markers is not None:
281             del markers[markerid]
282@@ -156,6 +186,15 @@
283                 raise ValueError("Circular reference detected")
284             markers[markerid] = dct
285         yield '{'
286+        key_separator = self.key_separator
287+        if self.indent is not None:
288+            self.current_indent_level += 1
289+            newline_indent = self._newline_indent()
290+            item_separator = self.item_separator + newline_indent
291+            yield newline_indent
292+        else:
293+            newline_indent = None
294+            item_separator = self.item_separator
295         first = True
296         if self.ensure_ascii:
297             encoder = encode_basestring_ascii
298@@ -165,7 +204,7 @@
299         if self.sort_keys:
300             keys = dct.keys()
301             keys.sort()
302-            items = [(k,dct[k]) for k in keys]
303+            items = [(k, dct[k]) for k in keys]
304         else:
305             items = dct.iteritems()
306         for key, value in items:
307@@ -190,11 +229,14 @@
308             if first:
309                 first = False
310             else:
311-                yield ', '
312+                yield item_separator
313             yield encoder(key)
314-            yield ': '
315+            yield key_separator
316             for chunk in self._iterencode(value, markers):
317                 yield chunk
318+        if newline_indent is not None:
319+            self.current_indent_level -= 1
320+            yield self._newline_indent()
321         yield '}'
322         if markers is not None:
323             del markers[markerid]
324Index: decoder.py
325===================================================================
326--- decoder.py  (revision 4347)
327+++ decoder.py  (working copy)
328@@ -3,7 +3,7 @@
329 """
330 import re
331 
332-from django.utils.simplejson.scanner import Scanner, pattern
333+from simplejson.scanner import Scanner, pattern
334 
335 FLAGS = re.VERBOSE | re.MULTILINE | re.DOTALL
336 
337@@ -127,6 +127,7 @@
338         raise ValueError(errmsg("Expecting property name", s, end))
339     end += 1
340     encoding = getattr(context, 'encoding', None)
341+    iterscan = JSONScanner.iterscan
342     while True:
343         key, end = scanstring(s, end, encoding)
344         end = _w(s, end).end()
345@@ -134,7 +135,7 @@
346             raise ValueError(errmsg("Expecting : delimiter", s, end))
347         end = _w(s, end + 1).end()
348         try:
349-            value, end = JSONScanner.iterscan(s, idx=end).next()
350+            value, end = iterscan(s, idx=end, context=context).next()
351         except StopIteration:
352             raise ValueError(errmsg("Expecting object", s, end))
353         pairs[key] = value
354@@ -164,9 +165,10 @@
355     nextchar = s[end:end + 1]
356     if nextchar == ']':
357         return values, end + 1
358+    iterscan = JSONScanner.iterscan
359     while True:
360         try:
361-            value, end = JSONScanner.iterscan(s, idx=end).next()
362+            value, end = iterscan(s, idx=end, context=context).next()
363         except StopIteration:
364             raise ValueError(errmsg("Expecting object", s, end))
365         values.append(value)