~ubuntu-branches/ubuntu/quantal/python-django/quantal

« back to all changes in this revision

Viewing changes to django/utils/simplejson/encoder.py

  • Committer: Bazaar Package Importer
  • Author(s): Scott James Remnant, Eddy Mulyono
  • Date: 2008-09-16 12:18:47 UTC
  • mfrom: (1.1.5 upstream) (4.1.1 lenny)
  • Revision ID: james.westby@ubuntu.com-20080916121847-mg225rg5mnsdqzr0
Tags: 1.0-1ubuntu1
* Merge from Debian (LP: #264191), remaining changes:
  - Run test suite on build.

[Eddy Mulyono]
* Update patch to workaround network test case failures.

Show diffs side-by-side

added added

removed removed

Lines of Context:
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
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
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
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
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
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
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
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
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
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