diff --git a/vendor/cjson/Changelog b/vendor/cjson/Changelog new file mode 100644 index 000000000..d45e11d10 --- /dev/null +++ b/vendor/cjson/Changelog @@ -0,0 +1 @@ +For the list of changes please consult debian/changelog diff --git a/vendor/cjson/MANIFEST.in b/vendor/cjson/MANIFEST.in new file mode 100644 index 000000000..c00159451 --- /dev/null +++ b/vendor/cjson/MANIFEST.in @@ -0,0 +1,5 @@ +recursive-include debian changelog compat control copyright pycompat rules +include MANIFEST.in +include Changelog +include build_inplace +include jsontest.py diff --git a/vendor/cjson/PKG-INFO b/vendor/cjson/PKG-INFO new file mode 100644 index 000000000..472f63bee --- /dev/null +++ b/vendor/cjson/PKG-INFO @@ -0,0 +1,42 @@ +Metadata-Version: 1.0 +Name: python-cjson +Version: 1.0.3x5 +Summary: Fast JSON encoder/decoder for Python +Home-page: http://ag-projects.com/ +Author: Dan Pascu +Author-email: dan@ag-projects.com +License: LGPL +Download-URL: http://cx.hu/python-cjson/1.0.3x5 +Description: This module implements a very fast JSON encoder/decoder for Python. + + JSON stands for JavaScript Object Notation and is a text based lightweight + data exchange format which is easy for humans to read/write and for machines + to parse/generate. JSON is completely language independent and has multiple + implementations in most of the programming languages, making it ideal for + data exchange and storage. + + The module is written in C and it is up to 250 times faster when compared to + the other python JSON implementations which are written directly in python. + This speed gain varies with the complexity of the data and the operation and + is the the range of 10-200 times for encoding operations and in the range of + 100-250 times for decoding operations. + + This module is an enhanced version of the original python-cjson 1.0.3. + Please send your comments regarding to this version to: + Viktor Ferenczi + + New feature not in the original 1.0.3 release: + + Encoder and decoder functions are extensible to allow conversion of objects + not in the official JSON specification, such as Date objects. + + Unicode and character encoding related enhancements. Automatic conversion + of dictionary keys to strings (JSON requires string keys in objects). + +Platform: Platform Independent +Classifier: Development Status :: 5 - Production/Stable +Classifier: Intended Audience :: Developers +Classifier: License :: OSI Approved :: GNU Library or Lesser General Public License (LGPL) +Classifier: Operating System :: OS Independent +Classifier: Programming Language :: Python +Classifier: Topic :: Software Development :: Libraries :: Python Modules diff --git a/vendor/cjson/README b/vendor/cjson/README new file mode 100644 index 000000000..817937afa --- /dev/null +++ b/vendor/cjson/README @@ -0,0 +1,25 @@ +This module implements a very fast JSON encoder/decoder for Python. + +JSON stands for JavaScript Object Notation and is a text based lightweight +data exchange format which is easy for humans to read/write and for machines +to parse/generate. JSON is completely language independent and has multiple +implementations in most of the programming languages, making it ideal for +data exchange and storage. + +The module is written in C and it is up to 250 times faster when compared to +the other python JSON implementations which are written directly in python. +This speed gain varies with the complexity of the data and the operation and +is the the range of 10-200 times for encoding operations and in the range of +100-250 times for decoding operations. + +This module is an enhanced version of the original python-cjson 1.0.3. +Please send your comments regarding to this version to: +Viktor Ferenczi + +New feature not in the original 1.0.3 release: + +Encoder and decoder functions are extensible to allow conversion of objects +not in the official JSON specification, such as Date objects. Unicode and +character encoding related enhancements. Automatic conversion of dictionary +keys to strings (JSON requires string keys in objects). Extension now +compatible with the C compiler from Microsoft Visual C++ Toolkit 2003. diff --git a/vendor/cjson/build_inplace b/vendor/cjson/build_inplace new file mode 100644 index 000000000..06748a0ac --- /dev/null +++ b/vendor/cjson/build_inplace @@ -0,0 +1,5 @@ +#!/bin/sh + +python setup.py build_ext --inplace +python setup.py clean + diff --git a/vendor/cjson/cjson.c b/vendor/cjson/cjson.c new file mode 100644 index 000000000..dfa9885ee --- /dev/null +++ b/vendor/cjson/cjson.c @@ -0,0 +1,1368 @@ +/* + * Copyright (C) 2006-2007 Dan Pascu + * Author: Dan Pascu + * + * Fast JSON encoder/decoder implementation for Python + * + * Original version: Dan Pascu + * + * 2007-03-16: Viktor Ferenczi + * + * Added extension keyword arguments to encode and decode functions. + * These functions can specify encoding/decoding of non-atomic data, + * such as Date objects. For example: + * + * decode('[1,new Date(2007,1,2),2]', extension=dateDecoder) + * encode([1, datetime.date(2007,1,2), 2], extension=dateEncoder) + * + * See the unit tests (jsontest.py) for detailed example. + * + * Both encoder and decoder now has a context struct. + * Very small optimizations for the decoder (switch order). + * + * 2007-03-28: Qiangning Hong + * + * Segfault with Python 2.5 on 64 bit platforms is fixed by using the correct + * Py_ssize_t type for one of the parameters of PyDict_Next(). More testing + * with Python 2.5 on 64 bit platform required. + * + * 2007-04-01: Fixing exception handling bug + * + * When a decoder extension function was called after the failure of an + * internal decoder (for example after failing to interpret new Date(...) + * as null) the internal exception was propagated (not cleared) and could + * be incorrectly raised in the decoder extension function pointing to en + * otherwise correct statement in that function. This could cause severe + * confusion to the programmer and prevented execution of such extension + * functions. + * + * 2007-04-02: Added optional automatic conversion of dict keys to string. + * + * Since JSON specification does not allow non-string keys in objects, + * it's very useful to add optional automatic conversion of dictionary keys. + * This could be useful when porting code originally written for simplejson + * that does this by default. The feature can be enabled by passing + * key2str=True keyword argument to the encode() function. Default behaviour + * of python-cjson has been preserved, so without this keyword argument + * encoding of non-string dictionary keys will raise EncodeError. + * + * 2007-05-05: Added automatic charset encoding/decoding for strings + * + * Added keyword argument named encoding for automatic str object handling. + * Small performance optimizations. More realistic throughput test data. + * Compatibility with MS C compilers from VC++ Toolkit 2003. + * + * 2007-07-12: Fixed segmentation fault + * + * Fixed a rare, but reproducible segmentation fault when decoding specially + * crafted unicode strings. Bug reported by: Liu Cougar - China + * + */ + +#include +#include +#include +#include + +#if (PY_VERSION_HEX < 0x02050000) +typedef int Py_ssize_t; +#endif + +typedef struct EncoderContext { + PyObject *extension; /* callable to extend the encoder (encode user defined objects) */ + int key2str; /* flag to enable automatic dict key to string conversion */ + const char *encoding; /* encoding used to decode str objects, only ASCII is accepted if NULL */ +} EncoderContext; + +typedef struct DecoderContext { + char *str; /* the actual json string */ + char *end; /* pointer to the string end */ + char *ptr; /* pointer to the current parsing position */ + int all_unicode; /* make all output strings unicode if true */ + PyObject *extension; /* callable to extend the decoder (decode user defined sequences) or NULL */ + PyObject *jsonstr; /* the original JSON string */ + const char *encoding; /* encoding used to automatically encode unicode objects, they are left unicode if NULL */ +} DecoderContext; + +static PyObject* encode_object(EncoderContext *ctx, PyObject *object); +static PyObject* encode_string(PyObject *object); +static PyObject* encode_string_with_recoding(EncoderContext *ctx, PyObject *object); +static PyObject* encode_unicode(PyObject *object); +static PyObject* encode_tuple(EncoderContext *ctx, PyObject *object); +static PyObject* encode_list(EncoderContext *ctx, PyObject *object); +static PyObject* encode_dict(EncoderContext *ctx, PyObject *object); + +static PyObject* decode_json(DecoderContext *ctx); +static PyObject* decode_null(DecoderContext *ctx); +static PyObject* decode_bool(DecoderContext *ctx); +static PyObject* decode_string(DecoderContext *ctx); +static PyObject* decode_inf(DecoderContext *ctx); +static PyObject* decode_nan(DecoderContext *ctx); +static PyObject* decode_number(DecoderContext *ctx); +static PyObject* decode_array(DecoderContext *ctx); +static PyObject* decode_object(DecoderContext *ctx); +static PyObject* decode_using_extension(DecoderContext *ctx); + +static PyObject *JSON_Error; +static PyObject *JSON_EncodeError; +static PyObject *JSON_DecodeError; + +static const char *hexdigit = "0123456789abcdef"; + +#define True 1 +#define False 0 + +#ifndef INFINITY +# define INFINITY HUGE_VAL +#endif + +#ifndef NAN +# define NAN (HUGE_VAL - HUGE_VAL) +#endif + +#ifndef Py_IS_NAN +# define Py_IS_NAN(X) ((X) != (X)) +#endif + +#define skipSpaces(d) while((d)->ptr<(d)->end && *((d)->ptr) && isspace(*((d)->ptr))) (d)->ptr++ + + +/* ------------------------------ Decoding ----------------------------- */ + +static PyObject* +decode_null(DecoderContext *ctx) +{ + int left; + + left = ctx->end - ctx->ptr; + + if (left >= 4 && strncmp(ctx->ptr, "null", 4)==0) { + ctx->ptr += 4; + Py_INCREF(Py_None); + return Py_None; + } else { + PyErr_Format(JSON_DecodeError, "cannot parse JSON description: %.20s", + ctx->ptr); + return NULL; + } +} + + +static PyObject* +decode_bool(DecoderContext *ctx) +{ + int left; + + left = ctx->end - ctx->ptr; + + if (left >= 4 && strncmp(ctx->ptr, "true", 4)==0) { + ctx->ptr += 4; + Py_INCREF(Py_True); + return Py_True; + } else if (left >= 5 && strncmp(ctx->ptr, "false", 5)==0) { + ctx->ptr += 5; + Py_INCREF(Py_False); + return Py_False; + } else { + PyErr_Format(JSON_DecodeError, "cannot parse JSON description: %.20s", + ctx->ptr); + return NULL; + } +} + + +static PyObject* +decode_string(DecoderContext *ctx) +{ + PyObject *object; + register char *ptr; + register char c, escaping, string_escape, has_unicode; + int len; + + /* look for the closing quote */ + escaping = string_escape = False; + has_unicode = ctx->all_unicode; + ptr = ctx->ptr + 1; + while (True) { + c = *ptr; + if (c == 0) { + PyErr_Format(JSON_DecodeError, + "unterminated string starting at position %d", + ctx->ptr - ctx->str); + return NULL; + } + if (escaping) { + switch(c) { + case 'u': + has_unicode = True; + break; + case '"': + case 'r': + case 'n': + case 't': + case 'b': + case 'f': + case '\\': + string_escape = True; + break; + } + escaping = False; + } else { + if (c == '\\') { + escaping = True; + } else if (c == '"') { + break; + } else if (!isascii(c)) { + has_unicode = True; + } + } + ptr++; + } + + len = ptr - ctx->ptr - 1; + + if (has_unicode) + object = PyUnicode_DecodeUnicodeEscape(ctx->ptr+1, len, NULL); + else if (string_escape) + object = PyString_DecodeEscape(ctx->ptr+1, len, NULL, 0, NULL); + else + object = PyString_FromStringAndSize(ctx->ptr+1, len); + + if (object == NULL) { + PyObject *type, *value, *tb, *reason; + + PyErr_Fetch(&type, &value, &tb); + if (type == NULL) { + PyErr_Format(JSON_DecodeError, + "invalid string starting at position %d", + ctx->ptr - ctx->str); + } else { + if (PyErr_GivenExceptionMatches(type, PyExc_UnicodeDecodeError)) { + reason = PyObject_GetAttrString(value, "reason"); + PyErr_Format(JSON_DecodeError, "cannot decode string starting" + " at position %d: %s", + ctx->ptr - ctx->str, + reason ? PyString_AsString(reason) : "bad format"); + Py_XDECREF(reason); + } else { + PyErr_Format(JSON_DecodeError, + "invalid string starting at position %d", + ctx->ptr - ctx->str); + } + } + Py_XDECREF(type); + Py_XDECREF(value); + Py_XDECREF(tb); + return NULL; + } + + /* Encode unicode into a specific encoding if specified */ + if (has_unicode && ctx->encoding) { + PyObject *strobject = PyUnicode_Encode( + PyUnicode_AS_UNICODE(object), + PyUnicode_GET_SIZE(object), + ctx->encoding, "strict" + ); + Py_DECREF(object); + object=strobject; + if(strobject==NULL) { + PyErr_SetString(JSON_DecodeError, "error encoding unicode object to the specified encoding after successful JSON decoding"); + } + } + + /* Go to the end of the string */ + ctx->ptr = ptr+1; + + return object; +} + + +static PyObject* +decode_inf(DecoderContext *ctx) +{ + PyObject *object; + int left; + + left = ctx->end - ctx->ptr; + + if (left >= 8 && strncmp(ctx->ptr, "Infinity", 8)==0) { + ctx->ptr += 8; + object = PyFloat_FromDouble(INFINITY); + return object; + } else if (left >= 9 && strncmp(ctx->ptr, "+Infinity", 9)==0) { + ctx->ptr += 9; + object = PyFloat_FromDouble(INFINITY); + return object; + } else if (left >= 9 && strncmp(ctx->ptr, "-Infinity", 9)==0) { + ctx->ptr += 9; + object = PyFloat_FromDouble(-INFINITY); + return object; + } else { + PyErr_Format(JSON_DecodeError, "cannot parse JSON description: %.20s", + ctx->ptr); + return NULL; + } +} + + +static PyObject* +decode_nan(DecoderContext *ctx) +{ + PyObject *object; + int left; + + left = ctx->end - ctx->ptr; + + if (left >= 3 && strncmp(ctx->ptr, "NaN", 3)==0) { + ctx->ptr += 3; + object = PyFloat_FromDouble(NAN); + return object; + } else { + PyErr_Format(JSON_DecodeError, "cannot parse JSON description: %.20s", + ctx->ptr); + return NULL; + } +} + + +static PyObject* +decode_number(DecoderContext *ctx) +{ + PyObject *object, *str; + int c, is_float, should_stop; + char *ptr; + + /* check if we got a floating point number */ + ptr = ctx->ptr; + is_float = should_stop = False; + while (True) { + c = *ptr; + if (c == 0) + break; + switch(c) { + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + case '-': + case '+': + break; + case '.': + case 'e': + case 'E': + is_float = True; + break; + default: + should_stop = True; + } + if (should_stop) { + break; + } + ptr++; + } + + str = PyString_FromStringAndSize(ctx->ptr, ptr - ctx->ptr); + if (str == NULL) + return NULL; + + if (is_float) { + object = PyFloat_FromString(str, NULL); + } else { + object = PyInt_FromString(PyString_AS_STRING(str), NULL, 10); + } + + Py_DECREF(str); + + if (object == NULL) { + PyErr_Format(JSON_DecodeError, "invalid number starting at position %d", + ctx->ptr - ctx->str); + puts(ptr); + } else { + ctx->ptr = ptr; + } + + return object; +} + + +static PyObject* +decode_array(DecoderContext *ctx) +{ + PyObject *object, *item; + int c, expect_item, items, result; + char *start; + + object = PyList_New(0); + + start = ctx->ptr; + ctx->ptr++; + expect_item = True; + items = 0; + while (True) { + skipSpaces(ctx); + c = *ctx->ptr; + if (c == 0) { + PyErr_Format(JSON_DecodeError, "unterminated array starting at " + "position %d", start - ctx->str); + goto failure;; + } else if (c == ']') { + if (expect_item && items>0) { + PyErr_Format(JSON_DecodeError, "expecting array item at " + "position %d", ctx->ptr - ctx->str); + goto failure; + } + ctx->ptr++; + break; + } else if (c == ',') { + if (expect_item) { + PyErr_Format(JSON_DecodeError, "expecting array item at " + "position %d", ctx->ptr - ctx->str); + goto failure; + } + expect_item = True; + ctx->ptr++; + continue; + } else { + item = decode_json(ctx); + if (item == NULL) + goto failure; + result = PyList_Append(object, item); + Py_DECREF(item); + if (result == -1) + goto failure; + expect_item = False; + items++; + } + } + + return object; + +failure: + Py_DECREF(object); + return NULL; +} + + +static PyObject* +decode_object(DecoderContext *ctx) +{ + PyObject *object, *key, *value; + int c, expect_key, items, result; + char *start; + + object = PyDict_New(); + + expect_key = True; + items = 0; + start = ctx->ptr; + ctx->ptr++; + + while (True) { + skipSpaces(ctx); + c = *ctx->ptr; + if (c == 0) { + PyErr_Format(JSON_DecodeError, "unterminated object starting at " + "position %d", start - ctx->str); + goto failure;; + } else if (c == '}') { + if (expect_key && items>0) { + PyErr_Format(JSON_DecodeError, "expecting object property name" + " at position %d", ctx->ptr - ctx->str); + goto failure; + } + ctx->ptr++; + break; + } else if (c == ',') { + if (expect_key) { + PyErr_Format(JSON_DecodeError, "expecting object property name" + "at position %d", ctx->ptr - ctx->str); + goto failure; + } + expect_key = True; + ctx->ptr++; + continue; + } else { + if (c != '"') { + PyErr_Format(JSON_DecodeError, + "expecting property name in object at " + "position %d", ctx->ptr - ctx->str); + goto failure; + } + + key = decode_json(ctx); + if (key == NULL) + goto failure; + + skipSpaces(ctx); + if (*ctx->ptr != ':') { + PyErr_Format(JSON_DecodeError, + "missing colon after object property name at " + "position %d", ctx->ptr - ctx->str); + Py_DECREF(key); + goto failure; + } else { + ctx->ptr++; + } + + value = decode_json(ctx); + if (value == NULL) { + Py_DECREF(key); + goto failure; + } + + result = PyDict_SetItem(object, key, value); + Py_DECREF(key); + Py_DECREF(value); + if (result == -1) + goto failure; + expect_key = False; + items++; + } + } + + return object; + +failure: + Py_DECREF(object); + return NULL; +} + +static PyObject* +decode_using_extension(DecoderContext *ctx) +{ + PyObject *object, *index, *tuple, *delta; + long chars; + + index = PyInt_FromLong(ctx->ptr - ctx->str); + tuple = PyObject_CallFunctionObjArgs(ctx->extension, ctx->jsonstr, index, NULL); + Py_DECREF(index); + + if(!tuple) return NULL; + + if(!PyTuple_Check(tuple) || PyTuple_Size(tuple)!=2) { + Py_DECREF(tuple); + PyErr_SetString(JSON_DecodeError, "extension function should return tuple: (object, parsed_chars)"); + return NULL; + } + + object = PyTuple_GET_ITEM(tuple,0); + delta = PyTuple_GET_ITEM(tuple,1); + chars = PyInt_AsLong(delta); + Py_INCREF(object); + Py_DECREF(tuple); + + if(chars<1) { + Py_DECREF(object); + PyErr_SetString(JSON_DecodeError, "extension function should return positive integer as number of parsed characters"); + return NULL; + } + + ctx->ptr += chars; + if(ctx->ptr > ctx->end) { + Py_DECREF(object); + PyErr_SetString(JSON_DecodeError, "extension function returned parsed character count beyond the end of the JSON string"); + return NULL; + } + + return object; +} + +static PyObject* +decode_json(DecoderContext *ctx) +{ + PyObject *object; + + skipSpaces(ctx); + switch(*ctx->ptr) { + case 0: + PyErr_SetString(JSON_DecodeError, "empty JSON description"); + return NULL; + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + object = decode_number(ctx); + break; + case '-': + case '+': + if (*(ctx->ptr+1) == 'I') { + object = decode_inf(ctx); + } else { + object = decode_number(ctx); + } + break; + case '"': + object = decode_string(ctx); + break; + case 't': + case 'f': + object = decode_bool(ctx); + break; + case 'n': + object = decode_null(ctx); + break; + case '{': + object = decode_object(ctx); + break; + case '[': + object = decode_array(ctx); + break; + case 'N': + object = decode_nan(ctx); + break; + case 'I': + object = decode_inf(ctx); + break; + default: + if ( ctx->extension ) { + return decode_using_extension(ctx); + } + PyErr_SetString(JSON_DecodeError, "cannot parse JSON description"); + return NULL; + } + + if( !object && ctx->extension ) { + PyErr_Clear(); + object = decode_using_extension(ctx); + } + + return object; +} + + +/* ------------------------------ Encoding ----------------------------- */ + +/* + * This function is an almost verbatim copy of PyString_Repr() from + * Python's stringobject.c with the following differences: + * + * - it always quotes the output using double quotes. + * - it also quotes \b and \f + * - it replaces any non ASCII character hh with \u00hh instead of \xhh + */ +static PyObject* +encode_string(PyObject *string) +{ + register int i; + register char c; + register char *p; + PyStringObject* op = (PyStringObject*) string; + char quote = '"'; + size_t newsize = 2 + 6 * op->ob_size; + PyObject *v; + int sl; + + if (newsize > INT_MAX) { + PyErr_SetString(PyExc_OverflowError, + "string is too large to make repr"); + } + v = PyString_FromStringAndSize((char *)NULL, newsize); + if (v == NULL) { + return NULL; + } + + p = PyString_AS_STRING(v); + *p++ = quote; + sl = op->ob_size; + for (i = 0; i < sl; i++) { + /* There's at least enough room for a hex escape + and a closing quote. */ + assert(newsize - (p - PyString_AS_STRING(v)) >= 7); + c = op->ob_sval[i]; + if (c == quote || c == '\\') + *p++ = '\\', *p++ = c; + else if (c == '\t') + *p++ = '\\', *p++ = 't'; + else if (c == '\n') + *p++ = '\\', *p++ = 'n'; + else if (c == '\r') + *p++ = '\\', *p++ = 'r'; + else if (c == '\f') + *p++ = '\\', *p++ = 'f'; + else if (c == '\b') + *p++ = '\\', *p++ = 'b'; + else if (c < ' ' || c >= 0x7f) { + *p++='\\'; *p++='u'; *p++='0'; *p++='0'; + *p++=hexdigit[((unsigned char)c>>4)&15]; + *p++=hexdigit[(unsigned char)c&15]; + } + else + *p++ = c; + } + assert(newsize - (p - PyString_AS_STRING(v)) >= 1); + *p++ = quote; + *p = '\0'; + _PyString_Resize(&v, (int) (p - PyString_AS_STRING(v))); + return v; +} + +static PyObject* +encode_string_with_recoding(EncoderContext *ctx, PyObject *object) +{ + PyObject *unicode; + PyObject *result; + + unicode=PyUnicode_Decode( + PyString_AS_STRING(object), + PyString_GET_SIZE(object), + ctx->encoding, "strict" + ); + if(unicode==NULL) { + PyErr_SetString(JSON_EncodeError, "error decoding str object with the specified encoding before actual JSON encoding"); + return NULL; + } + result=encode_unicode(unicode); + Py_DECREF(unicode); + return result; +} + +/* + * This function is an almost verbatim copy of unicodeescape_string() from + * Python's unicodeobject.c with the following differences: + * + * - it always quotes the output using double quotes. + * - it uses \u00hh instead of \xhh in output. + * - it also quotes \b and \f + */ +static PyObject* +encode_unicode(PyObject *unicode) +{ + PyObject *repr; + Py_UNICODE *s; + int size; + char *p; + + s = PyUnicode_AS_UNICODE(unicode); + size = PyUnicode_GET_SIZE(unicode); + + repr = PyString_FromStringAndSize(NULL, 2 + 6*size + 1); + if (repr == NULL) + return NULL; + + p = PyString_AS_STRING(repr); + + *p++ = '"'; + + while (size-- > 0) { + Py_UNICODE ch = *s++; + + /* Escape quotes */ + if ((ch == (Py_UNICODE) PyString_AS_STRING(repr)[0] || ch == '\\')) { + *p++ = '\\'; + *p++ = (char) ch; + continue; + } + +#ifdef Py_UNICODE_WIDE + /* Map 21-bit characters to '\U00xxxxxx' */ + else if (ch >= 0x10000) { + int offset = p - PyString_AS_STRING(repr); + + /* Resize the string if necessary */ + if (offset + 12 > PyString_GET_SIZE(repr)) { + if (_PyString_Resize(&repr, PyString_GET_SIZE(repr) + 100)) + return NULL; + p = PyString_AS_STRING(repr) + offset; + } + + *p++ = '\\'; + *p++ = 'U'; + *p++ = hexdigit[(ch >> 28) & 0x0000000F]; + *p++ = hexdigit[(ch >> 24) & 0x0000000F]; + *p++ = hexdigit[(ch >> 20) & 0x0000000F]; + *p++ = hexdigit[(ch >> 16) & 0x0000000F]; + *p++ = hexdigit[(ch >> 12) & 0x0000000F]; + *p++ = hexdigit[(ch >> 8) & 0x0000000F]; + *p++ = hexdigit[(ch >> 4) & 0x0000000F]; + *p++ = hexdigit[ch & 0x0000000F]; + continue; + } +#endif + /* Map UTF-16 surrogate pairs to Unicode \UXXXXXXXX escapes */ + else if (ch >= 0xD800 && ch < 0xDC00) { + Py_UNICODE ch2; + Py_UCS4 ucs; + + ch2 = *s++; + size--; + if (ch2 >= 0xDC00 && ch2 <= 0xDFFF) { + ucs = (((ch & 0x03FF) << 10) | (ch2 & 0x03FF)) + 0x00010000; + *p++ = '\\'; + *p++ = 'U'; + *p++ = hexdigit[(ucs >> 28) & 0x0000000F]; + *p++ = hexdigit[(ucs >> 24) & 0x0000000F]; + *p++ = hexdigit[(ucs >> 20) & 0x0000000F]; + *p++ = hexdigit[(ucs >> 16) & 0x0000000F]; + *p++ = hexdigit[(ucs >> 12) & 0x0000000F]; + *p++ = hexdigit[(ucs >> 8) & 0x0000000F]; + *p++ = hexdigit[(ucs >> 4) & 0x0000000F]; + *p++ = hexdigit[ucs & 0x0000000F]; + continue; + } + /* Fall through: isolated surrogates are copied as-is */ + s--; + size++; + } + + /* Map 16-bit characters to '\uxxxx' */ + if (ch >= 256) { + *p++ = '\\'; + *p++ = 'u'; + *p++ = hexdigit[(ch >> 12) & 0x000F]; + *p++ = hexdigit[(ch >> 8) & 0x000F]; + *p++ = hexdigit[(ch >> 4) & 0x000F]; + *p++ = hexdigit[ch & 0x000F]; + } + + /* Map special whitespace to '\t', \n', '\r', '\f', '\b' */ + else if (ch == '\t') { + *p++ = '\\'; + *p++ = 't'; + } + else if (ch == '\n') { + *p++ = '\\'; + *p++ = 'n'; + } + else if (ch == '\r') { + *p++ = '\\'; + *p++ = 'r'; + } + else if (ch == '\f') { + *p++ = '\\'; + *p++ = 'f'; + } + else if (ch == '\b') { + *p++ = '\\'; + *p++ = 'b'; + } + + /* Map non-printable US ASCII to '\u00hh' */ + else if (ch < ' ' || ch >= 0x7F) { + *p++ = '\\'; + *p++ = 'u'; + *p++ = '0'; + *p++ = '0'; + *p++ = hexdigit[(ch >> 4) & 0x000F]; + *p++ = hexdigit[ch & 0x000F]; + } + + /* Copy everything else as-is */ + else + *p++ = (char) ch; + } + + *p++ = PyString_AS_STRING(repr)[0]; + + *p = '\0'; + _PyString_Resize(&repr, p - PyString_AS_STRING(repr)); + return repr; +} + + +/* + * This function is an almost verbatim copy of tuplerepr() from + * Python's tupleobject.c with the following differences: + * + * - it uses encode_object() to get the object's JSON reprezentation. + * - it uses [] as decorations isntead of () (to masquerade as a JSON array). + */ + +static PyObject* +encode_tuple(EncoderContext *ctx, PyObject *tuple) +{ + int i, n; + PyObject *s, *temp; + PyObject *pieces, *result = NULL; + PyTupleObject *v = (PyTupleObject*) tuple; + + n = v->ob_size; + if (n == 0) + return PyString_FromString("[]"); + + pieces = PyTuple_New(n); + if (pieces == NULL) + return NULL; + + /* Do repr() on each element. */ + for (i = 0; i < n; ++i) { + s = encode_object(ctx, v->ob_item[i]); + if (s == NULL) + goto Done; + PyTuple_SET_ITEM(pieces, i, s); + } + + /* Add "[]" decorations to the first and last items. */ + assert(n > 0); + s = PyString_FromString("["); + if (s == NULL) + goto Done; + temp = PyTuple_GET_ITEM(pieces, 0); + PyString_ConcatAndDel(&s, temp); + PyTuple_SET_ITEM(pieces, 0, s); + if (s == NULL) + goto Done; + + s = PyString_FromString("]"); + if (s == NULL) + goto Done; + temp = PyTuple_GET_ITEM(pieces, n-1); + PyString_ConcatAndDel(&temp, s); + PyTuple_SET_ITEM(pieces, n-1, temp); + if (temp == NULL) + goto Done; + + /* Paste them all together with ", " between. */ + s = PyString_FromString(", "); + if (s == NULL) + goto Done; + result = _PyString_Join(s, pieces); + Py_DECREF(s); + +Done: + Py_DECREF(pieces); + return result; +} + +/* + * This function is an almost verbatim copy of list_repr() from + * Python's listobject.c with the following differences: + * + * - it uses encode_object() to get the object's JSON reprezentation. + * - it doesn't use the ellipsis to represent a list with references + * to itself, instead it raises an exception as such lists cannot be + * represented in JSON. + */ +static PyObject* +encode_list(EncoderContext *ctx, PyObject *list) +{ + int i; + PyObject *s, *temp; + PyObject *pieces = NULL, *result = NULL; + PyListObject *v = (PyListObject*) list; + + i = Py_ReprEnter((PyObject*)v); + if (i != 0) { + if (i > 0) { + PyErr_SetString(JSON_EncodeError, "a list with references to " + "itself is not JSON encodable"); + } + return NULL; + } + + if (v->ob_size == 0) { + result = PyString_FromString("[]"); + goto Done; + } + + pieces = PyList_New(0); + if (pieces == NULL) + goto Done; + + /* Do repr() on each element. Note that this may mutate the list, + * so must refetch the list size on each iteration. */ + for (i = 0; i < v->ob_size; ++i) { + int status; + s = encode_object(ctx, v->ob_item[i]); + if (s == NULL) + goto Done; + status = PyList_Append(pieces, s); + Py_DECREF(s); /* append created a new ref */ + if (status < 0) + goto Done; + } + + /* Add "[]" decorations to the first and last items. */ + assert(PyList_GET_SIZE(pieces) > 0); + s = PyString_FromString("["); + if (s == NULL) + goto Done; + temp = PyList_GET_ITEM(pieces, 0); + PyString_ConcatAndDel(&s, temp); + PyList_SET_ITEM(pieces, 0, s); + if (s == NULL) + goto Done; + + s = PyString_FromString("]"); + if (s == NULL) + goto Done; + temp = PyList_GET_ITEM(pieces, PyList_GET_SIZE(pieces) - 1); + PyString_ConcatAndDel(&temp, s); + PyList_SET_ITEM(pieces, PyList_GET_SIZE(pieces) - 1, temp); + if (temp == NULL) + goto Done; + + /* Paste them all together with ", " between. */ + s = PyString_FromString(", "); + if (s == NULL) + goto Done; + result = _PyString_Join(s, pieces); + Py_DECREF(s); + +Done: + Py_XDECREF(pieces); + Py_ReprLeave((PyObject *)v); + return result; +} + + +/* + * This function is an almost verbatim copy of dict_repr() from + * Python's dictobject.c with the following differences: + * + * - it uses encode_object() to get the object's JSON reprezentation. + * - only accept strings for keys or convert non-string keys to string if + * this feature is enabled (raises exception if cannot be converted) + * - it doesn't use the ellipsis to represent a dictionary with references + * to itself, instead it raises an exception as such dictionaries cannot + * be represented in JSON. + */ +static PyObject* +encode_dict(EncoderContext *ctx, PyObject *dict) +{ + Py_ssize_t i; + PyObject *s, *temp, *colon = NULL; + PyObject *pieces = NULL, *result = NULL; + PyObject *key, *value, *strkey = NULL; + PyDictObject *mp = (PyDictObject*) dict; + + i = Py_ReprEnter((PyObject *)mp); + if (i != 0) { + if (i > 0) { + PyErr_SetString(JSON_EncodeError, "a dict with references to " + "itself is not JSON encodable"); + } + return NULL; + } + + if (mp->ma_used == 0) { + result = PyString_FromString("{}"); + goto Done; + } + + pieces = PyList_New(0); + if (pieces == NULL) + goto Done; + + colon = PyString_FromString(": "); + if (colon == NULL) + goto Done; + + /* Do repr() on each key+value pair, and insert ": " between them. + * Note that repr may mutate the dict. */ + i = 0; + while (PyDict_Next((PyObject *)mp, &i, &key, &value)) { + int status; + + strkey = NULL; + if (!PyString_Check(key) && !PyUnicode_Check(key)) { + if (ctx->key2str) { + /* Auto convert keys to strings */ + strkey = PyObject_Str(key); + if( strkey == NULL ) { + PyErr_SetString(JSON_EncodeError, "Cannot convert dictionary key to string"); + goto Done; + } + } else { + PyErr_SetString(JSON_EncodeError, "JSON encodable dictionaries " + "must have string/unicode keys"); + goto Done; + } + } + + /* Prevent repr from deleting value during key format. */ + Py_INCREF(value); + if( strkey ) { + s = encode_object(ctx, strkey); + Py_DECREF(strkey); + strkey = NULL; + } else { + s = encode_object(ctx, key); + } + PyString_Concat(&s, colon); + PyString_ConcatAndDel(&s, encode_object(ctx, value)); + Py_DECREF(value); + if (s == NULL) + goto Done; + status = PyList_Append(pieces, s); + Py_DECREF(s); /* append created a new ref */ + if (status < 0) + goto Done; + } + + /* Add "{}" decorations to the first and last items. */ + assert(PyList_GET_SIZE(pieces) > 0); + s = PyString_FromString("{"); + if (s == NULL) + goto Done; + temp = PyList_GET_ITEM(pieces, 0); + PyString_ConcatAndDel(&s, temp); + PyList_SET_ITEM(pieces, 0, s); + if (s == NULL) + goto Done; + + s = PyString_FromString("}"); + if (s == NULL) + goto Done; + temp = PyList_GET_ITEM(pieces, PyList_GET_SIZE(pieces) - 1); + PyString_ConcatAndDel(&temp, s); + PyList_SET_ITEM(pieces, PyList_GET_SIZE(pieces) - 1, temp); + if (temp == NULL) + goto Done; + + /* Paste them all together with ", " between. */ + s = PyString_FromString(", "); + if (s == NULL) + goto Done; + result = _PyString_Join(s, pieces); + Py_DECREF(s); + +Done: + Py_XDECREF(strkey); + Py_XDECREF(pieces); + Py_XDECREF(colon); + Py_ReprLeave((PyObject *)mp); + return result; +} + + +static PyObject* +encode_object(EncoderContext *ctx, PyObject *object) +{ + if (object == Py_True) { + return PyString_FromString("true"); + } else if (object == Py_False) { + return PyString_FromString("false"); + } else if (object == Py_None) { + return PyString_FromString("null"); + } else if (PyString_Check(object)) { + if ( ctx->encoding ) { + /* Recode string from the str encoding specified to unicode */ + return encode_string_with_recoding(ctx, object); + } else { + /* No encoding specified, string must be ASCII */ + return encode_string(object); + } + } else if (PyUnicode_Check(object)) { + return encode_unicode(object); + } else if (PyFloat_Check(object)) { + double val = PyFloat_AS_DOUBLE(object); + if (Py_IS_NAN(val)) { + return PyString_FromString("NaN"); + } else if (Py_IS_INFINITY(val)) { + if (val > 0) { + return PyString_FromString("Infinity"); + } else { + return PyString_FromString("-Infinity"); + } + } else { + return PyObject_Str(object); + } + } else if (PyInt_Check(object) || PyLong_Check(object)) { + return PyObject_Str(object); + } else if (PyList_Check(object)) { + return encode_list(ctx, object); + } else if (PyTuple_Check(object)) { + return encode_tuple(ctx, object); + } else if (PyDict_Check(object)) { /* use PyMapping_Check(object) instead? -Dan */ + return encode_dict(ctx, object); + } else if (ctx->extension) { + return PyObject_CallFunctionObjArgs(ctx->extension, object, NULL); + } else { + PyErr_SetString(JSON_EncodeError, "object is not JSON encodable"); + return NULL; + } +} + + +/* Encode object into its JSON representation */ + +static PyObject* +JSON_encode(PyObject *self, PyObject *args, PyObject *kwargs) +{ + static char *kwlist[] = {"obj", "extension", "key2str", "encoding", NULL}; + PyObject *object, *key2str = NULL; + EncoderContext ctx; + + ctx.extension=NULL; + ctx.encoding=NULL; + + if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O|OOz:encode", kwlist, + &object, &ctx.extension, &key2str, &ctx.encoding)) + return NULL; + + if (ctx.extension==Py_None) ctx.extension=NULL; + ctx.key2str=key2str?PyObject_IsTrue(key2str):0; + + if(ctx.extension && !PyCallable_Check(ctx.extension)) { + PyErr_SetString(JSON_EncodeError, "extension is not callable"); + return NULL; + } + + return encode_object(&ctx, object); +} + + +/* Decode JSON representation into python objects */ + +static PyObject* +JSON_decode(PyObject *self, PyObject *args, PyObject *kwargs) +{ + static char *kwlist[] = {"json", "all_unicode", "extension", "encoding", NULL}; + int all_unicode = False; /* by default return unicode only when needed */ + PyObject *object, *string, *str, *oAllUnicode = NULL; + DecoderContext ctx; + + ctx.extension=NULL; + ctx.jsonstr=NULL; + ctx.encoding=NULL; + + if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O|OOz:decode", kwlist, + &string, &oAllUnicode, &ctx.extension, &ctx.encoding)) + return NULL; + + all_unicode=oAllUnicode?PyObject_IsTrue(oAllUnicode):0; + + if (PyUnicode_Check(string)) { + str = PyUnicode_AsRawUnicodeEscapeString(string); + if (str == NULL) { + return NULL; + } + } else { + Py_INCREF(string); + str = string; + } + + if (PyString_AsStringAndSize(str, &(ctx.str), NULL) == -1) { + Py_DECREF(str); + return NULL; /* not a string object or it contains null bytes */ + } + + ctx.ptr = ctx.str; + ctx.end = ctx.str + strlen(ctx.str); + ctx.all_unicode = all_unicode; + ctx.jsonstr = str; + + if (ctx.extension==Py_None) ctx.extension=NULL; + + if (ctx.extension && !PyCallable_Check(ctx.extension)) { + PyErr_SetString(JSON_DecodeError, "extension is not callable"); + Py_DECREF(str); + return NULL; + } + + object = decode_json(&ctx); + + Py_DECREF(str); + + if (object != NULL) { + skipSpaces(&ctx); + if (ctx.ptr < ctx.end) { + Py_DECREF(object); + PyErr_Format(JSON_DecodeError, "extra data after JSON description" + " at position %d", ctx.ptr-ctx.str); + return NULL; + } + } + + return object; +} + + +/* List of functions defined in the module */ + +static PyMethodDef cjson_methods[] = { + {"encode", (PyCFunction)JSON_encode, METH_VARARGS|METH_KEYWORDS, + PyDoc_STR("encode(object, extension=None, key2str=False, encoding='ascii') -> \n" + "generate the JSON representation for object. The optional argument\n" + "`extension' defines a function to encode objects not in the original\n" + "JSON specification. For example this can be used to convert\n" + "datetime.date objects to new Date(...) expressions. The extension\n" + "function must have a single argument and must return the JSON\n" + "representation of the object passed or raise EncodeError if the\n" + "object cannot be converted to JSON format. Automatically convert\n" + "dictionary keys to str if key2str is True, otherwise EncodeError\n" + "is raised whenever a non-str and non-unicode dictinary key is found.\n" + "The encoding argument must specify the encoding used to decode\n" + "python str objects." + )}, + + {"decode", (PyCFunction)JSON_decode, METH_VARARGS|METH_KEYWORDS, + PyDoc_STR("decode(string, all_unicode=False, extension=None, encoding=None) -> \n" + "parse the JSON representation into python objects.\n" + "The optional argument `all_unicode', specifies how to\n" + "convert the strings in the JSON representation into python objects.\n" + "If it is False (default), it will return strings everywhere possible\n" + "and unicode objects only where necessary, else it will return unicode\n" + "objects everywhere (this is slower). The optional argument\n" + "`extension' defines a function to decode objects not in the original\n" + "JSON specification. For example this can be used to convert\n" + "new Date(...) expressions to datetime.date objects. The extension\n" + "function must have a two arguments: json, idx. The `json' argument\n" + "receives the original JSON string under conversion. The `idx' argument\n" + "receives the index of the first character of the substring to be\n" + "parsed as an extended object. The extension function must return a\n" + "2-tuple: (obj,count) or raise DecodeError if the string cannot be\n" + "parsed to an extended object. `obj' must be the object parsed,\n" + "`count' must be the (positive integer) number of characters consumed\n" + "from the JSON string (length of the object's representation).\n" + "All unicode strings will be encoded to the specified encoding\n" + "automatically if encoding is not None. Unicode objects are returned\n" + "if encoding is None (this is the default). It's encouraged to set\n" + "all_unicode=True or define an encoding to prevent mixing of str and\n" + "unicode objects in the decoder output." + )}, + + {NULL, NULL} /* sentinel */ +}; + +PyDoc_STRVAR(module_doc, +"Fast JSON encoder/decoder module." +); + +/* Initialization function for the module (*must* be called initcjson) */ + +PyMODINIT_FUNC +initcjson(void) +{ + PyObject *m; + + /* Create the module and add the functions */ + m = Py_InitModule3("cjson", cjson_methods, module_doc); + + /* Add some symbolic constants to the module */ + if (JSON_Error == NULL) { + JSON_Error = PyErr_NewException("cjson.Error", NULL, NULL); + if (JSON_Error == NULL) + return; + /*Py_INCREF(JSON_Error);*/ + PyModule_AddObject(m, "Error", JSON_Error); + } + if (JSON_EncodeError == NULL) { + JSON_EncodeError = PyErr_NewException("cjson.EncodeError", + JSON_Error, NULL); + if (JSON_EncodeError == NULL) + return; + /*Py_INCREF(JSON_EncodeError);*/ + PyModule_AddObject(m, "EncodeError", JSON_EncodeError); + } + if (JSON_DecodeError == NULL) { + JSON_DecodeError = PyErr_NewException("cjson.DecodeError", + JSON_Error, NULL); + if (JSON_DecodeError == NULL) + return; + /*Py_INCREF(JSON_DecodeError);*/ + PyModule_AddObject(m, "DecodeError", JSON_DecodeError); + } +} diff --git a/vendor/cjson/jsontest.py b/vendor/cjson/jsontest.py new file mode 100644 index 000000000..a9994e61d --- /dev/null +++ b/vendor/cjson/jsontest.py @@ -0,0 +1,418 @@ +#!/usr/bin/python +# -*- coding: latin2 -*- + +## this test suite is an almost verbatim copy of the jsontest.py test suite +## found in json-py available from http://sourceforge.net/projects/json-py/ +## Copyright (C) 2005 Patrick D. Logan + +## 2007-03-15 - Viktor Ferenczi (python@cx.hu) +## Added unit tests for encoder/decoder extensions. +## Added throughput measurement. +## Typical values on a 3.0GHz Intel P4: about 8Mbytes/s + +## 2007-04-02 - Viktor Ferenczi (python@cx.hu) +## Added unit test for encoding with automatic dict key to str conversion. + +## 2007-05-04 - Viktor Ferenczi (python@cx.hu) +## Added unit tests for unicode encoding/decoding. +## More realistic, grid like data used for performance tests. + +import re +import time +import math +import unittest +import datetime + +import cjson +_exception = cjson.DecodeError + +# The object tests should be order-independent. They're not. +# i.e. they should test for existence of keys and values +# with read/write invariance. + +def _removeWhitespace(str): + return str.replace(" ", "") + +class JsonTest(unittest.TestCase): + def testReadEmptyObject(self): + obj = cjson.decode("{}") + self.assertEqual({}, obj) + + def testWriteEmptyObject(self): + s = cjson.encode({}) + self.assertEqual("{}", _removeWhitespace(s)) + + def testReadStringValue(self): + obj = cjson.decode('{ "name" : "Patrick" }') + self.assertEqual({ "name" : "Patrick" }, obj) + + def testReadEscapedQuotationMark(self): + obj = cjson.decode(r'"\""') + self.assertEqual(r'"', obj) + +# def testReadEscapedSolidus(self): +# obj = cjson.decode(r'"\/"') +# self.assertEqual(r'/', obj) + + def testReadEscapedReverseSolidus(self): + obj = cjson.decode(r'"\\"') + self.assertEqual("\\", obj) + + def testReadEscapedBackspace(self): + obj = cjson.decode(r'"\b"') + self.assertEqual("\b", obj) + + def testReadEscapedFormfeed(self): + obj = cjson.decode(r'"\f"') + self.assertEqual("\f", obj) + + def testReadEscapedNewline(self): + obj = cjson.decode(r'"\n"') + self.assertEqual("\n", obj) + + def testReadEscapedCarriageReturn(self): + obj = cjson.decode(r'"\r"') + self.assertEqual("\r", obj) + + def testReadEscapedHorizontalTab(self): + obj = cjson.decode(r'"\t"') + self.assertEqual("\t", obj) + + def testReadEscapedHexCharacter(self): + obj = cjson.decode(r'"\u000A"') + self.assertEqual("\n", obj) + obj = cjson.decode(r'"\u1001"') + self.assertEqual(u'\u1001', obj) + + def testWriteEscapedQuotationMark(self): + s = cjson.encode(r'"') + self.assertEqual(r'"\""', _removeWhitespace(s)) + + def testWriteEscapedSolidus(self): + s = cjson.encode(r'/') + #self.assertEqual(r'"\/"', _removeWhitespace(s)) + self.assertEqual('"/"', _removeWhitespace(s)) + + def testWriteNonEscapedSolidus(self): + s = cjson.encode(r'/') + self.assertEqual(r'"/"', _removeWhitespace(s)) + + def testWriteEscapedReverseSolidus(self): + s = cjson.encode("\\") + self.assertEqual(r'"\\"', _removeWhitespace(s)) + + def testWriteEscapedBackspace(self): + s = cjson.encode("\b") + self.assertEqual(r'"\b"', _removeWhitespace(s)) + + def testWriteEscapedFormfeed(self): + s = cjson.encode("\f") + self.assertEqual(r'"\f"', _removeWhitespace(s)) + + def testWriteEscapedNewline(self): + s = cjson.encode("\n") + self.assertEqual(r'"\n"', _removeWhitespace(s)) + + def testWriteEscapedCarriageReturn(self): + s = cjson.encode("\r") + self.assertEqual(r'"\r"', _removeWhitespace(s)) + + def testWriteEscapedHorizontalTab(self): + s = cjson.encode("\t") + self.assertEqual(r'"\t"', _removeWhitespace(s)) + + def testWriteEscapedHexCharacter(self): + s = cjson.encode(u'\u1001') + self.assertEqual(r'"\u1001"', _removeWhitespace(s)) + + def testReadBadEscapedHexCharacter(self): + self.assertRaises(_exception, self.doReadBadEscapedHexCharacter) + + def doReadBadEscapedHexCharacter(self): + cjson.decode('"\u10K5"') + + def testReadBadObjectKey(self): + self.assertRaises(_exception, self.doReadBadObjectKey) + + def doReadBadObjectKey(self): + cjson.decode('{ 44 : "age" }') + + def testReadBadArray(self): + self.assertRaises(_exception, self.doReadBadArray) + + def doReadBadArray(self): + cjson.decode('[1,2,3,,]') + + def testReadBadObjectSyntax(self): + self.assertRaises(_exception, self.doReadBadObjectSyntax) + + def doReadBadObjectSyntax(self): + cjson.decode('{"age", 44}') + + def testWriteStringValue(self): + s = cjson.encode({ "name" : "Patrick" }) + self.assertEqual('{"name":"Patrick"}', _removeWhitespace(s)) + + def testReadIntegerValue(self): + obj = cjson.decode('{ "age" : 44 }') + self.assertEqual({ "age" : 44 }, obj) + + def testReadNegativeIntegerValue(self): + obj = cjson.decode('{ "key" : -44 }') + self.assertEqual({ "key" : -44 }, obj) + + def testReadFloatValue(self): + obj = cjson.decode('{ "age" : 44.5 }') + self.assertEqual({ "age" : 44.5 }, obj) + + def testReadNegativeFloatValue(self): + obj = cjson.decode(' { "key" : -44.5 } ') + self.assertEqual({ "key" : -44.5 }, obj) + + def testReadBadNumber(self): + self.assertRaises(_exception, self.doReadBadNumber) + + def doReadBadNumber(self): + cjson.decode('-44.4.4') + + def testReadSmallObject(self): + obj = cjson.decode('{ "name" : "Patrick", "age":44} ') + self.assertEqual({ "age" : 44, "name" : "Patrick" }, obj) + + def testReadEmptyArray(self): + obj = cjson.decode('[]') + self.assertEqual([], obj) + + def testWriteEmptyArray(self): + self.assertEqual("[]", _removeWhitespace(cjson.encode([]))) + + def testReadSmallArray(self): + obj = cjson.decode(' [ "a" , "b", "c" ] ') + self.assertEqual(["a", "b", "c"], obj) + + def testWriteSmallArray(self): + self.assertEqual('[1,2,3,4]', _removeWhitespace(cjson.encode([1, 2, 3, 4]))) + + def testWriteSmallObject(self): + s = cjson.encode({ "name" : "Patrick", "age": 44 }) + self.assertEqual('{"age":44,"name":"Patrick"}', _removeWhitespace(s)) + + def testWriteFloat(self): + self.assertEqual("3.44556677", _removeWhitespace(cjson.encode(3.44556677))) + + def testReadTrue(self): + self.assertEqual(True, cjson.decode("true")) + + def testReadFalse(self): + self.assertEqual(False, cjson.decode("false")) + + def testReadNull(self): + self.assertEqual(None, cjson.decode("null")) + + def testWriteTrue(self): + self.assertEqual("true", _removeWhitespace(cjson.encode(True))) + + def testWriteFalse(self): + self.assertEqual("false", _removeWhitespace(cjson.encode(False))) + + def testWriteNull(self): + self.assertEqual("null", _removeWhitespace(cjson.encode(None))) + + def testReadArrayOfSymbols(self): + self.assertEqual([True, False, None], cjson.decode(" [ true, false,null] ")) + + def testWriteArrayOfSymbolsFromList(self): + self.assertEqual("[true,false,null]", _removeWhitespace(cjson.encode([True, False, None]))) + + def testWriteArrayOfSymbolsFromTuple(self): + self.assertEqual("[true,false,null]", _removeWhitespace(cjson.encode((True, False, None)))) + + def testReadComplexObject(self): + src = ''' + { "name": "Patrick", "age" : 44, "Employed?" : true, "Female?" : false, "grandchildren":null } +''' + obj = cjson.decode(src) + self.assertEqual({"name":"Patrick","age":44,"Employed?":True,"Female?":False,"grandchildren":None}, obj) + + def testReadLongArray(self): + src = '''[ "used", + "abused", + "confused", + true, false, null, + 1, + 2, + [3, 4, 5]] +''' + obj = cjson.decode(src) + self.assertEqual(["used","abused","confused", True, False, None, + 1,2,[3,4,5]], obj) + + def testReadIncompleteArray(self): + self.assertRaises(_exception, self.doReadIncompleteArray) + + def doReadIncompleteArray(self): + cjson.decode('[') + + def testReadComplexArray(self): + src = ''' +[ + { "name": "Patrick", "age" : 44, + "Employed?" : true, "Female?" : false, + "grandchildren":null }, + "used", + "abused", + "confused", + 1, + 2, + [3, 4, 5] +] +''' + obj = cjson.decode(src) + self.assertEqual([{"name":"Patrick","age":44,"Employed?":True,"Female?":False,"grandchildren":None}, + "used","abused","confused", + 1,2,[3,4,5]], obj) + + def testWriteComplexArray(self): + obj = [{"name":"Patrick","age":44,"Employed?":True,"Female?":False,"grandchildren":None}, + "used","abused","confused", + 1,2,[3,4,5]] + self.assertEqual('[{"Female?":false,"age":44,"name":"Patrick","grandchildren":null,"Employed?":true},"used","abused","confused",1,2,[3,4,5]]', + _removeWhitespace(cjson.encode(obj))) + + + def testReadWriteCopies(self): + orig_obj = {'a':' " '} + json_str = cjson.encode(orig_obj) + copy_obj = cjson.decode(json_str) + self.assertEqual(orig_obj, copy_obj) + self.assertEqual(True, orig_obj == copy_obj) + self.assertEqual(False, orig_obj is copy_obj) + + def testStringEncoding(self): + s = cjson.encode([1, 2, 3]) + self.assertEqual(unicode("[1,2,3]", "utf-8"), _removeWhitespace(s)) + + def testReadEmptyObjectAtEndOfArray(self): + self.assertEqual(["a","b","c",{}], + cjson.decode('["a","b","c",{}]')) + + def testReadEmptyObjectMidArray(self): + self.assertEqual(["a","b",{},"c"], + cjson.decode('["a","b",{},"c"]')) + + def testReadClosingObjectBracket(self): + self.assertEqual({"a":[1,2,3]}, cjson.decode('{"a":[1,2,3]}')) + + def testEmptyObjectInList(self): + obj = cjson.decode('[{}]') + self.assertEqual([{}], obj) + + def testObjectWithEmptyList(self): + obj = cjson.decode('{"test": [] }') + self.assertEqual({"test":[]}, obj) + + def testObjectWithNonEmptyList(self): + obj = cjson.decode('{"test": [3, 4, 5] }') + self.assertEqual({"test":[3, 4, 5]}, obj) + + def testWriteLong(self): + self.assertEqual("12345678901234567890", cjson.encode(12345678901234567890)) + + def testEncoderExtension(self): + def dateEncoder(d): + assert isinstance(d, datetime.date) + return 'new Date(Date.UTC(%d,%d,%d))'%(d.year, d.month, d.day) + self.assertEqual(cjson.encode([1,datetime.date(2007,1,2),2], extension=dateEncoder), '[1, new Date(Date.UTC(2007,1,2)), 2]') + self.assertRaises(cjson.EncodeError, lambda: cjson.encode(1, extension=0)) + + def testDecoderExtension(self): + re_date=re.compile('^new\sDate\(Date\.UTC\(.*?\)\)') + def dateDecoder(json,idx): + json=json[idx:] + m=re_date.match(json) + if not m: raise 'cannot parse JSON string as Date object: %s'%json[idx:] + args=cjson.decode('[%s]'%json[18:m.end()-2]) + dt=datetime.date(*args) + return (dt,m.end()) + self.assertEqual(cjson.decode('[1, new Date(Date.UTC(2007,1,2)), 2]', extension=dateDecoder), [1,datetime.date(2007,1,2),2]) + self.assertEqual(cjson.decode('[1, new Date(Date.UTC( 2007, 1 , 2 )) , 2]', extension=dateDecoder), [1,datetime.date(2007,1,2),2]) + self.assertRaises(cjson.DecodeError, lambda: cjson.decode('1', extension=0)) + + def testEncodeKey2Str(self): + d={'1':'str 1', 1:'int 1', 3.1415:'pi'} + self.assertRaises(cjson.EncodeError, lambda: cjson.encode(d)) + # NOTE: decode needed for order invariance + self.assertEqual(cjson.decode(cjson.encode(d, key2str=True)), + {"1": "str 1", "1": "int 1", "3.1415": "pi"}) + + def testUnicodeEncode(self): + self.assertEqual(cjson.encode({u'b':2}), '{"b": 2}') + self.assertEqual(cjson.encode({'o"':u'öõüû'}), r'{"o\"": "\u00f6\u0151\u00fc\u0171"}') + self.assertEqual(cjson.encode('öõüû', encoding='latin2'), r'"\u00f6\u0151\u00fc\u0171"') + self.assertRaises(cjson.EncodeError, lambda: cjson.encode('öõüû', encoding='ascii')) + + def testUnicodeDecode(self): + self.assertEqual(cjson.decode('{"b": 2}', all_unicode=True), {u'b':2}) + self.assertEqual(cjson.decode(r'{"o\"": "\u00f6\u0151\u00fc\u0171"}'), {'o"':u'öõüû'}) + self.assertEqual(cjson.decode(r'{"o\"": "\u00f6\u0151\u00fc\u0171"}', encoding='latin2'), {'o"':'öõüû'}) + self.assertEqual(cjson.decode(ur'"\u00f6\u0151\u00fc\u0171"', all_unicode=True), u'öõüû') + self.assertEqual(cjson.decode(r'"\u00f6\u0151\u00fc\u0171"', encoding='latin2'), 'öõüû') + self.assertRaises(cjson.DecodeError, lambda: cjson.decode('"öõüû"', encoding='ascii')) + + def testUnicodeEncodeDecode(self): + for s in ('abc', 'aáé', 'öõüû'): + self.assertEqual(cjson.decode(cjson.encode(s)), s.decode('latin1')) + +def measureEncoderThroughput(data): + bytes=0 + st=time.time() + cnt=0 + while True: + dt=time.time()-st + if dt>=0.5 and cnt>9: break + bytes+=len(cjson.encode(data)) + cnt+=1 + return int(bytes/1024/dt) + +def measureDecoderThroughput(data): + json=cjson.encode(data) + bytes=0 + st=time.time() + cnt=0 + while True: + dt=time.time()-st + if dt>=0.5 and cnt>9: break + cjson.decode(json) + bytes+=len(json) + cnt+=1 + return int(math.floor(bytes/dt/1024.0+0.5)) + +def measureThroughput(): + # Try to imitate realistic data, for example a large grid of records + data=[ + dict([ + ('cell(%d,%d)'%(x,y), ( + None, False, True, 0, 1, + x+y, x*y, math.pi, math.pi*x*y, + 'str(%d,%d)%s'%(x,y,'#'*(x/10)), + u'unicode[%04X]:%s'%(x*y,unichr(x*y)), + )) + for x in xrange(y) + ]) + for y in xrange(1,100) + ] + json=cjson.encode(data) + print 'Test data: tuples in dicts in a list, %d bytes as JSON string'%len(json) + print 'Encoder throughput: ~%d kbyte/s'%measureEncoderThroughput(data) + print 'Decoder throughput: ~%d kbyte/s'%measureDecoderThroughput(data) + +def main(): + try: + unittest.main() + #suite = unittest.TestLoader().loadTestsFromTestCase(JsonTest) + #unittest.TextTestRunner(verbosity=2).run(suite) + finally: + measureThroughput() + +if __name__ == '__main__': + main() diff --git a/vendor/cjson/setup.py b/vendor/cjson/setup.py new file mode 100644 index 000000000..3d467ecc9 --- /dev/null +++ b/vendor/cjson/setup.py @@ -0,0 +1,28 @@ +#!/usr/bin/python +# -*- coding: ascii -*- + +from distutils.core import setup, Extension + +__version__ = "1.0.3x6" + +setup( + name = "python-cjson", + version = __version__, + author = "Dan Pascu", + author_email = "dan@ag-projects.com", + url = "http://ag-projects.com/", + download_url = "http://cheeseshop.python.org/pypi/python-cjson/%s" % __version__, + description = "Fast JSON encoder/decoder for Python", + long_description = open('README', 'r').read(), + license = "LGPL", + platforms = ["Platform Independent"], + classifiers = [ + "Development Status :: 5 - Production/Stable", + "Intended Audience :: Developers", + "License :: OSI Approved :: GNU Library or Lesser General Public License (LGPL)", + "Operating System :: OS Independent", + "Programming Language :: Python", + "Topic :: Software Development :: Libraries :: Python Modules" + ], + ext_modules = [Extension(name='cjson', sources=['cjson.c'])], +)