summaryrefslogtreecommitdiff
path: root/django/utils
diff options
context:
space:
mode:
Diffstat (limited to 'django/utils')
-rw-r--r--django/utils/datastructures.py6
-rw-r--r--django/utils/dateformat.py4
-rw-r--r--django/utils/simplejson/LICENSE.txt2
-rw-r--r--django/utils/simplejson/__init__.py47
-rw-r--r--django/utils/simplejson/decoder.py6
-rw-r--r--django/utils/simplejson/encoder.py98
-rw-r--r--django/utils/simplejson/jsonfilter.py40
-rw-r--r--django/utils/simplejson/scanner.py3
-rw-r--r--django/utils/text.py31
9 files changed, 184 insertions, 53 deletions
diff --git a/django/utils/datastructures.py b/django/utils/datastructures.py
index cecb4da170..9bec7a5df7 100644
--- a/django/utils/datastructures.py
+++ b/django/utils/datastructures.py
@@ -70,7 +70,7 @@ class SortedDict(dict):
return self.keyOrder[:]
def values(self):
- return [dict.__getitem__(self,k) for k in self.keyOrder]
+ return [dict.__getitem__(self, k) for k in self.keyOrder]
def update(self, dict):
for k, v in dict.items():
@@ -81,6 +81,10 @@ class SortedDict(dict):
self.keyOrder.append(key)
return dict.setdefault(self, key, default)
+ def value_for_index(self, index):
+ "Returns the value of the item at the given zero-based index."
+ return self[self.keyOrder[index]]
+
class MultiValueDictKeyError(KeyError):
pass
diff --git a/django/utils/dateformat.py b/django/utils/dateformat.py
index 0890a81a81..00eb9fe617 100644
--- a/django/utils/dateformat.py
+++ b/django/utils/dateformat.py
@@ -11,7 +11,7 @@ Usage:
>>>
"""
-from django.utils.dates import MONTHS, MONTHS_AP, WEEKDAYS
+from django.utils.dates import MONTHS, MONTHS_3, MONTHS_AP, WEEKDAYS
from django.utils.tzinfo import LocalTimezone
from calendar import isleap, monthrange
import re, time
@@ -147,7 +147,7 @@ class DateFormat(TimeFormat):
def M(self):
"Month, textual, 3 letters; e.g. 'Jan'"
- return MONTHS[self.data.month][0:3]
+ return MONTHS_3[self.data.month].title()
def n(self):
"Month without leading zeros; i.e. '1' to '12'"
diff --git a/django/utils/simplejson/LICENSE.txt b/django/utils/simplejson/LICENSE.txt
index 90251a9f62..1fa4fd5ba2 100644
--- a/django/utils/simplejson/LICENSE.txt
+++ b/django/utils/simplejson/LICENSE.txt
@@ -1,4 +1,4 @@
-simplejson 1.3
+simplejson 1.5
Copyright (c) 2006 Bob Ippolito
Permission is hereby granted, free of charge, to any person obtaining a copy of
diff --git a/django/utils/simplejson/__init__.py b/django/utils/simplejson/__init__.py
index f88329b950..15b7173976 100644
--- a/django/utils/simplejson/__init__.py
+++ b/django/utils/simplejson/__init__.py
@@ -27,6 +27,21 @@ Encoding basic Python object hierarchies::
>>> io.getvalue()
'["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::
>>> import simplejson
@@ -68,10 +83,10 @@ Extending JSONEncoder::
['[', '2.0', ', ', '1.0', ']']
-Note that the JSON produced by this module is a subset of YAML,
-so it may be used as a serializer for that as well.
+Note that the JSON produced by this module's default settings
+is a subset of YAML, so it may be used as a serializer for that as well.
"""
-__version__ = '1.3'
+__version__ = '1.5'
__all__ = [
'dump', 'dumps', 'load', 'loads',
'JSONDecoder', 'JSONEncoder',
@@ -81,7 +96,7 @@ from django.utils.simplejson.decoder import JSONDecoder
from django.utils.simplejson.encoder import JSONEncoder
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
``.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
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
``.default()`` method to serialize additional types), specify it with
the ``cls`` kwarg.
@@ -112,7 +131,7 @@ def dump(obj, fp, skipkeys=False, ensure_ascii=True, check_circular=True,
if cls is None:
cls = JSONEncoder
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)
# could accelerate with writelines in some versions of Python, at
# a debuggability cost
@@ -120,7 +139,7 @@ def dump(obj, fp, skipkeys=False, ensure_ascii=True, check_circular=True,
fp.write(chunk)
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``.
@@ -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
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
``.default()`` method to serialize additional types), specify it with
the ``cls`` kwarg.
"""
if cls is None:
cls = JSONEncoder
- return cls(skipkeys=skipkeys, ensure_ascii=ensure_ascii,
- check_circular=check_circular, allow_nan=allow_nan, **kw).encode(obj)
+ return cls(
+ 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):
"""
diff --git a/django/utils/simplejson/decoder.py b/django/utils/simplejson/decoder.py
index 684af8c9ad..66f68a200b 100644
--- a/django/utils/simplejson/decoder.py
+++ b/django/utils/simplejson/decoder.py
@@ -127,6 +127,7 @@ def JSONObject(match, context, _w=WHITESPACE.match):
raise ValueError(errmsg("Expecting property name", s, end))
end += 1
encoding = getattr(context, 'encoding', None)
+ iterscan = JSONScanner.iterscan
while True:
key, end = scanstring(s, end, encoding)
end = _w(s, end).end()
@@ -134,7 +135,7 @@ def JSONObject(match, context, _w=WHITESPACE.match):
raise ValueError(errmsg("Expecting : delimiter", s, end))
end = _w(s, end + 1).end()
try:
- value, end = JSONScanner.iterscan(s, idx=end).next()
+ value, end = iterscan(s, idx=end, context=context).next()
except StopIteration:
raise ValueError(errmsg("Expecting object", s, end))
pairs[key] = value
@@ -164,9 +165,10 @@ def JSONArray(match, context, _w=WHITESPACE.match):
nextchar = s[end:end + 1]
if nextchar == ']':
return values, end + 1
+ iterscan = JSONScanner.iterscan
while True:
try:
- value, end = JSONScanner.iterscan(s, idx=end).next()
+ value, end = iterscan(s, idx=end, context=context).next()
except StopIteration:
raise ValueError(errmsg("Expecting object", s, end))
values.append(value)
diff --git a/django/utils/simplejson/encoder.py b/django/utils/simplejson/encoder.py
index bb1aba09f0..c83c6873eb 100644
--- a/django/utils/simplejson/encoder.py
+++ b/django/utils/simplejson/encoder.py
@@ -3,11 +3,11 @@ Implementation of JSONEncoder
"""
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_ASCII = re.compile(r'([\\"]|[^\ -~])')
+ESCAPE_ASCII = re.compile(r'([\\"/]|[^\ -~])')
ESCAPE_DCT = {
+ # escape all forward slashes to prevent </script> attack
+ '/': '\\/',
'\\': '\\\\',
'"': '\\"',
'\b': '\\b',
@@ -16,31 +16,31 @@ ESCAPE_DCT = {
'\r': '\\r',
'\t': '\\t',
}
-for i in range(20):
+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')
+
def floatstr(o, allow_nan=True):
- s = str(o)
- # If the first non-sign is a digit then it's not a special value
- if (o < 0.0 and s[1].isdigit()) or s[0].isdigit():
- return s
- elif not allow_nan:
+ # 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 == 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"
% (o,))
- # These are the string representations on the platforms I've tried
- if s == 'nan':
- 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'
+
+ return text
+
def encode_basestring(s):
"""
@@ -90,8 +90,11 @@ class JSONEncoder(object):
implementation (to raise ``TypeError``).
"""
__all__ = ['__init__', 'default', 'encode', 'iterencode']
+ item_separator = ', '
+ key_separator = ': '
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.
@@ -116,6 +119,15 @@ class JSONEncoder(object):
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.
"""
self.skipkeys = skipkeys
@@ -123,6 +135,13 @@ class JSONEncoder(object):
self.check_circular = check_circular
self.allow_nan = allow_nan
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):
if not lst:
@@ -134,14 +153,25 @@ class JSONEncoder(object):
raise ValueError("Circular reference detected")
markers[markerid] = lst
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
for value in lst:
if first:
first = False
else:
- yield ', '
+ yield separator
for chunk in self._iterencode(value, markers):
yield chunk
+ if newline_indent is not None:
+ self.current_indent_level -= 1
+ yield self._newline_indent()
yield ']'
if markers is not None:
del markers[markerid]
@@ -156,6 +186,15 @@ class JSONEncoder(object):
raise ValueError("Circular reference detected")
markers[markerid] = dct
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
if self.ensure_ascii:
encoder = encode_basestring_ascii
@@ -165,7 +204,7 @@ class JSONEncoder(object):
if self.sort_keys:
keys = dct.keys()
keys.sort()
- items = [(k,dct[k]) for k in keys]
+ items = [(k, dct[k]) for k in keys]
else:
items = dct.iteritems()
for key, value in items:
@@ -190,11 +229,14 @@ class JSONEncoder(object):
if first:
first = False
else:
- yield ', '
+ yield item_separator
yield encoder(key)
- yield ': '
+ yield key_separator
for chunk in self._iterencode(value, markers):
yield chunk
+ if newline_indent is not None:
+ self.current_indent_level -= 1
+ yield self._newline_indent()
yield '}'
if markers is not None:
del markers[markerid]
diff --git a/django/utils/simplejson/jsonfilter.py b/django/utils/simplejson/jsonfilter.py
new file mode 100644
index 0000000000..d02ae2033a
--- /dev/null
+++ b/django/utils/simplejson/jsonfilter.py
@@ -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)
diff --git a/django/utils/simplejson/scanner.py b/django/utils/simplejson/scanner.py
index b9244cfed1..64f4999fb5 100644
--- a/django/utils/simplejson/scanner.py
+++ b/django/utils/simplejson/scanner.py
@@ -3,11 +3,12 @@ Iterator based sre token scanner
"""
import sre_parse, sre_compile, sre_constants
from sre_constants import BRANCH, SUBPATTERN
+from re import VERBOSE, MULTILINE, DOTALL
import re
__all__ = ['Scanner', 'pattern']
-FLAGS = (re.VERBOSE | re.MULTILINE | re.DOTALL)
+FLAGS = (VERBOSE | MULTILINE | DOTALL)
class Scanner(object):
def __init__(self, lexicon, flags=FLAGS):
self.actions = [None]
diff --git a/django/utils/text.py b/django/utils/text.py
index 9e7bb3b6c4..217f42491b 100644
--- a/django/utils/text.py
+++ b/django/utils/text.py
@@ -8,17 +8,28 @@ capfirst = lambda x: x and x[0].upper() + x[1:]
def wrap(text, width):
"""
A word-wrap function that preserves existing line breaks and most spaces in
- the text. Expects that existing line breaks are posix newlines (\n).
- See http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/148061
+ the text. Expects that existing line breaks are posix newlines.
"""
- return reduce(lambda line, word, width=width: '%s%s%s' %
- (line,
- ' \n'[(len(line[line.rfind('\n')+1:])
- + len(word.split('\n',1)[0]
- ) >= width)],
- word),
- text.split(' ')
- )
+ def _generator():
+ it = iter(text.split(' '))
+ word = it.next()
+ yield word
+ pos = len(word) - word.rfind('\n') - 1
+ for word in it:
+ if "\n" in word:
+ lines = word.splitlines()
+ else:
+ lines = (word,)
+ pos += len(lines[0]) + 1
+ if pos > width:
+ yield '\n'
+ pos = len(lines[-1])
+ else:
+ yield ' '
+ if len(lines) > 1:
+ pos = len(lines[-1])
+ yield word
+ return "".join(_generator())
def truncate_words(s, num):
"Truncates a string after a certain number of words."