├── .gitignore ├── .gitmodules ├── BUILDING.markdown ├── MANIFEST.in ├── README.markdown ├── compare.py ├── decoder.c ├── encoder.c ├── includes └── yajl ├── ptrstack.h ├── py_yajl.h ├── runtests.sh ├── setup.py ├── test_data ├── issue_11.gz └── issue_6.txt ├── tests ├── __init__.py ├── issue_11.py ├── issue_6.py ├── python2.py ├── unit.py └── xmlrunner.py ├── yajl.c └── yajl_hacks.c /.gitignore: -------------------------------------------------------------------------------- 1 | .*.swp 2 | *.pyo 3 | *.pyc 4 | build/* 5 | *.so 6 | *.egg-info 7 | dist 8 | Yajl-Tests.xml 9 | .buildinfo 10 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "yajl"] 2 | path = yajl 3 | url = git://github.com/lloyd/yajl.git 4 | -------------------------------------------------------------------------------- /BUILDING.markdown: -------------------------------------------------------------------------------- 1 | Getting started building py-yajl 2 | ================================= 3 | 4 | 1. clone this repository 5 | 2. `git submodule update --init` 6 | 3. `python setup.py build_ext --inplace` 7 | 4. `python tests.py` 8 | 9 | -------------------------------------------------------------------------------- /MANIFEST.in: -------------------------------------------------------------------------------- 1 | include py_yajl.h ptrstack.h 2 | graft yajl 3 | graft includes 4 | prune yajl/test 5 | prune yajl/verify 6 | prune yajl/reformatter 7 | prune yajl/.git 8 | prune yajl/build 9 | -------------------------------------------------------------------------------- /README.markdown: -------------------------------------------------------------------------------- 1 | py-yajl: Python bindings for Yet Another JSON Library 2 | ====================================================== 3 | 4 | 5 | Introduction 6 | -------------- 7 | py-yajl is a C-based Python module to interface 8 | with Yajl (Yet Another JSON Library). While modules like `jsonlib`, 9 | `simplejson` and `cjson already exist, py-yajl is intended on providing 10 | pythonic access to Yajl's extremely fast string and stream parsing 11 | facilities. 12 | 13 | py-yajl fully supports Python 2.4-2.6 and Python 3. 14 | 15 | 16 | Building py-yajl 17 | ----------------- 18 | Please refer to `BUILDING.markdon` 19 | 20 | Authors 21 | --------- 22 | * R. Tyler Ballance **Original author/maintainer** 23 | * Lloyd Hilaiel **Yajl author/contributor** 24 | * Travis Parker **Python 3 support** 25 | -------------------------------------------------------------------------------- /compare.py: -------------------------------------------------------------------------------- 1 | import time 2 | 3 | import pickle 4 | import yajl 5 | try: 6 | import cjson 7 | except ImportError: 8 | cjson = None 9 | try: 10 | import simplejson 11 | except ImportError: 12 | simplejson = None 13 | try: 14 | import json 15 | except ImportError: 16 | json = None 17 | 18 | default_data = { 19 | "name": "Foo", 20 | "type": "Bar", 21 | "count": 1, 22 | "info": { 23 | "x": 203, 24 | "y": 102,},} 25 | 26 | 27 | def ttt(f, data=None, x=100*1000): 28 | start = time.time() 29 | while x: 30 | x -= 1 31 | foo = f(data) 32 | return time.time()-start 33 | 34 | 35 | def profile(serial, deserial, data=None, x=100*1000): 36 | if not data: 37 | data = default_data 38 | squashed = serial(data) 39 | return (ttt(serial, data, x), ttt(deserial, squashed, x)) 40 | 41 | 42 | def test(serial, deserial, data=None): 43 | if not data: 44 | data = default_data 45 | assert deserial(serial(data)) == data 46 | 47 | 48 | contenders = [ 49 | ('yajl', (yajl.Encoder().encode, yajl.Decoder().decode)), 50 | ] 51 | if cjson: 52 | contenders.append(('cjson', (cjson.encode, cjson.decode))) 53 | if simplejson: 54 | contenders.append(('simplejson', (simplejson.dumps, simplejson.loads))) 55 | if json: 56 | contenders.append(('stdlib json', (json.dumps, json.loads))) 57 | 58 | for name, args in contenders: 59 | test(*args) 60 | x, y = profile(*args) 61 | print("%-11s serialize: %0.3f deserialize: %0.3f total: %0.3f" % ( 62 | name, x, y, x+y)) 63 | -------------------------------------------------------------------------------- /decoder.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2009, R. Tyler Ballance 3 | * 4 | * Redistribution and use in source and binary forms, with or without 5 | * modification, are permitted provided that the following conditions are 6 | * met: 7 | * 8 | * 1. Redistributions of source code must retain the above copyright 9 | * notice, this list of conditions and the following disclaimer. 10 | * 11 | * 2. Redistributions in binary form must reproduce the above copyright 12 | * notice, this list of conditions and the following disclaimer in 13 | * the documentation and/or other materials provided with the 14 | * distribution. 15 | * 16 | * 3. Neither the name of R. Tyler Ballance nor the names of its 17 | * contributors may be used to endorse or promote products derived 18 | * from this software without specific prior written permission. 19 | * 20 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 21 | * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 22 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 23 | * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, 24 | * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 25 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 26 | * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 27 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 28 | * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING 29 | * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 30 | * POSSIBILITY OF SUCH DAMAGE. 31 | */ 32 | 33 | 34 | #include 35 | 36 | #include 37 | 38 | #include 39 | #include 40 | 41 | #include "py_yajl.h" 42 | 43 | int _PlaceObject(_YajlDecoder *self, PyObject *parent, PyObject *child) 44 | { 45 | if ( (!self) || (!child) || (!parent) ) 46 | return failure; 47 | 48 | if (PyList_Check(parent)) { 49 | PyList_Append(parent, child); 50 | // child is now owned by parent! 51 | if ((child) && (child != Py_None)) { 52 | Py_XDECREF(child); 53 | } 54 | return success; 55 | } else if (PyDict_Check(parent)) { 56 | PyObject* key = py_yajl_ps_current(self->keys); 57 | PyDict_SetItem(parent, key, child); 58 | py_yajl_ps_pop(self->keys); 59 | // child is now owned by parent! 60 | Py_XDECREF(key); 61 | if ((child) && (child != Py_None)) { 62 | Py_XDECREF(child); 63 | } 64 | return success; 65 | } 66 | return failure; 67 | } 68 | 69 | int PlaceObject(_YajlDecoder *self, PyObject *object) 70 | { 71 | unsigned int length = py_yajl_ps_length(self->elements); 72 | 73 | if (length == 0) { 74 | /* 75 | * When the length is zero, and we're entering this code path 76 | * we should only be handling "primitive types" i.e. strings and 77 | * numbers, not dict/list. 78 | */ 79 | self->root = object; 80 | return success; 81 | } 82 | return _PlaceObject(self, py_yajl_ps_current(self->elements), object); 83 | } 84 | 85 | 86 | static int handle_null(void *ctx) 87 | { 88 | Py_INCREF(Py_None); 89 | return PlaceObject(ctx, Py_None); 90 | } 91 | 92 | static int handle_bool(void *ctx, int value) 93 | { 94 | return PlaceObject(ctx, PyBool_FromLong((long)(value))); 95 | } 96 | 97 | static int handle_number(void *ctx, const char *value, unsigned int length) 98 | { 99 | _YajlDecoder *self = (_YajlDecoder *)(ctx); 100 | PyObject *object; 101 | #ifdef IS_PYTHON3 102 | PyBytesObject *string; 103 | #else 104 | PyObject *string; 105 | #endif 106 | 107 | int floaty_char; 108 | 109 | // take a moment here to scan the input string to see if there's 110 | // any chars which suggest this is a floating point number 111 | for (floaty_char = 0; floaty_char < length; floaty_char++) { 112 | switch (value[floaty_char]) { 113 | case '.': case 'e': case 'E': goto floatin; 114 | } 115 | } 116 | 117 | floatin: 118 | #ifdef IS_PYTHON3 119 | string = (PyBytesObject *)PyBytes_FromStringAndSize(value, length); 120 | if (floaty_char >= length) { 121 | object = PyLong_FromString(string->ob_sval, NULL, 10); 122 | } else { 123 | object = PyFloat_FromString((PyObject *)string); 124 | } 125 | #else 126 | string = PyString_FromStringAndSize(value, length); 127 | if (floaty_char >= length) { 128 | object = PyInt_FromString(PyString_AS_STRING(string), NULL, 10); 129 | } else { 130 | object = PyFloat_FromString(string, NULL); 131 | } 132 | #endif 133 | Py_XDECREF(string); 134 | return PlaceObject(self, object); 135 | } 136 | 137 | static int handle_string(void *ctx, const unsigned char *value, unsigned int length) 138 | { 139 | return PlaceObject(ctx, PyUnicode_FromStringAndSize((char *)value, length)); 140 | } 141 | 142 | static int handle_start_dict(void *ctx) 143 | { 144 | PyObject *object = PyDict_New(); 145 | if (!object) 146 | return failure; 147 | 148 | py_yajl_ps_push(((_YajlDecoder *)(ctx))->elements, object); 149 | return success; 150 | } 151 | 152 | static int handle_dict_key(void *ctx, const unsigned char *value, unsigned int length) 153 | { 154 | PyObject *object = PyUnicode_FromStringAndSize((const char *) value, length); 155 | 156 | if (object == NULL) 157 | return failure; 158 | 159 | py_yajl_ps_push(((_YajlDecoder *)(ctx))->keys, object); 160 | return success; 161 | } 162 | 163 | static int handle_end_dict(void *ctx) 164 | { 165 | _YajlDecoder *self = (_YajlDecoder *)(ctx); 166 | PyObject *last, *popped; 167 | unsigned int length; 168 | 169 | length = py_yajl_ps_length(self->elements); 170 | if (length == 1) { 171 | /* 172 | * If this is the last element in the stack 173 | * then it's "root" and we should finish up 174 | */ 175 | self->root = py_yajl_ps_current(self->elements); 176 | py_yajl_ps_pop(self->elements); 177 | return success; 178 | } else if (length < 2) { 179 | return failure; 180 | } 181 | 182 | /* 183 | * If not, then we should properly add this dict 184 | * to it's appropriate parent 185 | */ 186 | popped = py_yajl_ps_current(self->elements); 187 | py_yajl_ps_pop(self->elements); 188 | last = py_yajl_ps_current(self->elements); 189 | 190 | return _PlaceObject(self, last, popped); 191 | } 192 | 193 | static int handle_start_list(void *ctx) 194 | { 195 | PyObject *object = PyList_New(0); 196 | 197 | if (!object) 198 | return failure; 199 | 200 | py_yajl_ps_push(((_YajlDecoder *)(ctx))->elements, object); 201 | return success; 202 | } 203 | 204 | static int handle_end_list(void *ctx) 205 | { 206 | _YajlDecoder *self = (_YajlDecoder *)(ctx); 207 | PyObject *last, *popped; 208 | unsigned int length; 209 | 210 | length = py_yajl_ps_length(self->elements); 211 | if (length == 1) { 212 | self->root = py_yajl_ps_current(self->elements); 213 | py_yajl_ps_pop(self->elements); 214 | return success; 215 | } else if (length < 2) { 216 | return failure; 217 | } 218 | 219 | popped = py_yajl_ps_current(self->elements); 220 | py_yajl_ps_pop(self->elements); 221 | last = py_yajl_ps_current(self->elements); 222 | 223 | return _PlaceObject(self, last, popped); 224 | } 225 | 226 | static yajl_callbacks decode_callbacks = { 227 | handle_null, 228 | handle_bool, 229 | NULL, 230 | NULL, 231 | handle_number, 232 | handle_string, 233 | handle_start_dict, 234 | handle_dict_key, 235 | handle_end_dict, 236 | handle_start_list, 237 | handle_end_list 238 | }; 239 | 240 | PyObject *_internal_decode(_YajlDecoder *self, char *buffer, unsigned int buflen) 241 | { 242 | yajl_handle parser = NULL; 243 | yajl_status yrc; 244 | yajl_parser_config config = { 1, 1 }; 245 | 246 | if (self->elements.used > 0) { 247 | py_yajl_ps_free(self->elements); 248 | py_yajl_ps_init(self->elements); 249 | } 250 | if (self->keys.used > 0) { 251 | py_yajl_ps_free(self->keys); 252 | py_yajl_ps_init(self->keys); 253 | } 254 | 255 | /* callbacks, config, allocfuncs */ 256 | parser = yajl_alloc(&decode_callbacks, &config, NULL, (void *)(self)); 257 | yrc = yajl_parse(parser, (const unsigned char *)(buffer), buflen); 258 | yajl_parse_complete(parser); 259 | yajl_free(parser); 260 | 261 | if (yrc != yajl_status_ok) { 262 | PyErr_SetObject(PyExc_ValueError, 263 | PyUnicode_FromString(yajl_status_to_string(yrc))); 264 | return NULL; 265 | } 266 | 267 | if (self->root == NULL) { 268 | PyErr_SetObject(PyExc_ValueError, 269 | PyUnicode_FromString("The root object is NULL")); 270 | return NULL; 271 | } 272 | 273 | // Callee now owns memory, we'll leave refcnt at one and 274 | // null out our pointer. 275 | PyObject *root = self->root; 276 | self->root = NULL; 277 | return root; 278 | } 279 | 280 | PyObject *py_yajldecoder_decode(PYARGS) 281 | { 282 | _YajlDecoder *decoder = (_YajlDecoder *)(self); 283 | char *buffer = NULL; 284 | PyObject *pybuffer = NULL; 285 | PyObject *result = NULL; 286 | Py_ssize_t buflen = 0; 287 | 288 | if (!PyArg_ParseTuple(args, "O", &pybuffer)) 289 | return NULL; 290 | 291 | Py_INCREF(pybuffer); 292 | 293 | if (PyUnicode_Check(pybuffer)) { 294 | if (!(result = PyUnicode_AsUTF8String(pybuffer))) { 295 | Py_DECREF(pybuffer); 296 | return NULL; 297 | } 298 | Py_DECREF(pybuffer); 299 | pybuffer = result; 300 | result = NULL; 301 | } 302 | 303 | if (PyString_Check(pybuffer)) { 304 | if (PyString_AsStringAndSize(pybuffer, &buffer, &buflen)) { 305 | Py_DECREF(pybuffer); 306 | return NULL; 307 | } 308 | } else { 309 | /* really seems like this should be a TypeError, but 310 | tests/unit.py:ErrorCasesTests.test_None disagrees */ 311 | Py_DECREF(pybuffer); 312 | PyErr_SetString(PyExc_ValueError, "string or unicode expected"); 313 | return NULL; 314 | } 315 | 316 | if (!buflen) { 317 | PyErr_SetObject(PyExc_ValueError, 318 | PyUnicode_FromString("Cannot parse an empty buffer")); 319 | return NULL; 320 | } 321 | 322 | result = _internal_decode(decoder, buffer, (unsigned int)buflen); 323 | Py_DECREF(pybuffer); 324 | return result; 325 | } 326 | 327 | int yajldecoder_init(PYARGS) 328 | { 329 | _YajlDecoder *me = (_YajlDecoder *)(self); 330 | py_yajl_ps_init(me->elements); 331 | py_yajl_ps_init(me->keys); 332 | me->root = NULL; 333 | 334 | return 0; 335 | } 336 | 337 | void yajldecoder_dealloc(_YajlDecoder *self) 338 | { 339 | py_yajl_ps_free(self->elements); 340 | py_yajl_ps_init(self->elements); 341 | py_yajl_ps_free(self->keys); 342 | py_yajl_ps_init(self->keys); 343 | if (self->root) { 344 | Py_XDECREF(self->root); 345 | } 346 | #ifdef IS_PYTHON3 347 | Py_TYPE(self)->tp_free((PyObject*)self); 348 | #else 349 | self->ob_type->tp_free((PyObject*)self); 350 | #endif 351 | } 352 | -------------------------------------------------------------------------------- /encoder.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2010, R. Tyler Ballance 3 | * 4 | * Redistribution and use in source and binary forms, with or without 5 | * modification, are permitted provided that the following conditions are 6 | * met: 7 | * 8 | * 1. Redistributions of source code must retain the above copyright 9 | * notice, this list of conditions and the following disclaimer. 10 | * 11 | * 2. Redistributions in binary form must reproduce the above copyright 12 | * notice, this list of conditions and the following disclaimer in 13 | * the documentation and/or other materials provided with the 14 | * distribution. 15 | * 16 | * 3. Neither the name of R. Tyler Ballance nor the names of its 17 | * contributors may be used to endorse or promote products derived 18 | * from this software without specific prior written permission. 19 | * 20 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 21 | * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 22 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 23 | * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, 24 | * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 25 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 26 | * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 27 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 28 | * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING 29 | * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 30 | * POSSIBILITY OF SUCH DAMAGE. 31 | */ 32 | #include 33 | 34 | #include 35 | #include 36 | #include 37 | #include 38 | 39 | #include "py_yajl.h" 40 | 41 | static const char *hexdigit = "0123456789abcdef"; 42 | 43 | /* Located in yajl_hacks.c */ 44 | extern yajl_gen_status yajl_gen_raw_string(yajl_gen g, 45 | const unsigned char * str, unsigned int len); 46 | 47 | static yajl_gen_status ProcessObject(_YajlEncoder *self, PyObject *object) 48 | { 49 | yajl_gen handle = (yajl_gen)(self->_generator); 50 | yajl_gen_status status = yajl_gen_in_error_state; 51 | PyObject *iterator, *item; 52 | 53 | if (object == Py_None) { 54 | return yajl_gen_null(handle); 55 | } 56 | if (object == Py_True) { 57 | return yajl_gen_bool(handle, 1); 58 | } 59 | if (object == Py_False) { 60 | return yajl_gen_bool(handle, 0); 61 | } 62 | if (PyUnicode_Check(object)) { 63 | Py_ssize_t length = PyUnicode_GET_SIZE(object); 64 | Py_UNICODE *raw_unicode = PyUnicode_AS_UNICODE(object); 65 | /* 66 | * Create a buffer with enough space for code-points, preceeding and 67 | * following quotes and a null termination character 68 | */ 69 | char *buffer = (char *)(malloc(sizeof(char) * (1 + length * 6))); 70 | unsigned int offset = 0; 71 | 72 | while (length-- > 0) { 73 | Py_UNICODE ch = *raw_unicode++; 74 | 75 | /* Escape escape characters */ 76 | switch (ch) { 77 | case '\t': 78 | buffer[offset++] = '\\'; 79 | buffer[offset++] = 't'; 80 | continue; 81 | break; 82 | case '\n': 83 | buffer[offset++] = '\\'; 84 | buffer[offset++] = 'n'; 85 | continue; 86 | break; 87 | case '\r': 88 | buffer[offset++] = '\\'; 89 | buffer[offset++] = 'r'; 90 | continue; 91 | break; 92 | case '\f': 93 | buffer[offset++] = '\\'; 94 | buffer[offset++] = 'f'; 95 | continue; 96 | break; 97 | case '\b': 98 | buffer[offset++] = '\\'; 99 | buffer[offset++] = 'b'; 100 | continue; 101 | break; 102 | case '\\': 103 | buffer[offset++] = '\\'; 104 | buffer[offset++] = '\\'; 105 | continue; 106 | break; 107 | case '\"': 108 | buffer[offset++] = '\\'; 109 | buffer[offset++] = '\"'; 110 | continue; 111 | break; 112 | default: 113 | break; 114 | } 115 | 116 | /* Map 16-bit characters to '\uxxxx' */ 117 | if (ch >= 256) { 118 | buffer[offset++] = '\\'; 119 | buffer[offset++] = 'u'; 120 | buffer[offset++] = hexdigit[(ch >> 12) & 0x000F]; 121 | buffer[offset++] = hexdigit[(ch >> 8) & 0x000F]; 122 | buffer[offset++] = hexdigit[(ch >> 4) & 0x000F]; 123 | buffer[offset++] = hexdigit[ch & 0x000F]; 124 | continue; 125 | } 126 | 127 | /* Map non-printable US ASCII to '\u00hh' */ 128 | if ( (ch < 0x20) || (ch >= 0x7F) ) { 129 | buffer[offset++] = '\\'; 130 | buffer[offset++] = 'u'; 131 | buffer[offset++] = '0'; 132 | buffer[offset++] = '0'; 133 | buffer[offset++] = hexdigit[(ch >> 4) & 0x0F]; 134 | buffer[offset++] = hexdigit[ch & 0x0F]; 135 | continue; 136 | } 137 | 138 | /* Handle proper ascii chars */ 139 | if ( (ch >= 0x20) && (ch < 0x7F) ) { 140 | buffer[offset++] = (char)(ch); 141 | continue; 142 | } 143 | } 144 | buffer[offset] = '\0'; 145 | status = yajl_gen_raw_string(handle, (const unsigned char *)(buffer), (unsigned int)(offset)); 146 | free(buffer); 147 | return status; 148 | } 149 | #ifdef IS_PYTHON3 150 | if (PyBytes_Check(object)) { 151 | #else 152 | if (PyString_Check(object)) { 153 | #endif 154 | const unsigned char *buffer = NULL; 155 | Py_ssize_t length; 156 | #ifdef IS_PYTHON3 157 | PyBytes_AsStringAndSize(object, (char **)&buffer, &length); 158 | #else 159 | PyString_AsStringAndSize(object, (char **)&buffer, &length); 160 | #endif 161 | return yajl_gen_string(handle, buffer, (unsigned int)(length)); 162 | } 163 | #ifndef IS_PYTHON3 164 | if (PyInt_Check(object)) { 165 | long number = PyInt_AsLong(object); 166 | if ( (number == -1) && (PyErr_Occurred()) ) { 167 | return yajl_gen_in_error_state; 168 | } 169 | return yajl_gen_integer(handle, number); 170 | } 171 | #endif 172 | if (PyLong_Check(object)) { 173 | long long number = PyLong_AsLongLong(object); 174 | char *buffer = NULL; 175 | 176 | if ( (number == -1) && (PyErr_Occurred()) ) { 177 | return yajl_gen_in_error_state;; 178 | } 179 | 180 | /* 181 | * Nifty trick for getting the buffer length of a long long, going 182 | * to convert this long long into a buffer to be handled by 183 | * yajl_gen_number() 184 | */ 185 | unsigned int length = (unsigned int)(snprintf(NULL, 0, "%lld", number)) + 1; 186 | buffer = (char *)(malloc(length)); 187 | snprintf(buffer, length, "%lld", number); 188 | return yajl_gen_number(handle, buffer, length - 1); 189 | } 190 | if (PyFloat_Check(object)) { 191 | return yajl_gen_double(handle, PyFloat_AsDouble(object)); 192 | } 193 | if (PyList_Check(object)||PyGen_Check(object)||PyTuple_Check(object)) { 194 | /* 195 | * Recurse and handle the list 196 | */ 197 | iterator = PyObject_GetIter(object); 198 | if (iterator == NULL) 199 | goto exit; 200 | status = yajl_gen_array_open(handle); 201 | if (status == yajl_max_depth_exceeded) { 202 | Py_XDECREF(iterator); 203 | goto exit; 204 | } 205 | while ((item = PyIter_Next(iterator))) { 206 | status = ProcessObject(self, item); 207 | Py_XDECREF(item); 208 | } 209 | Py_XDECREF(iterator); 210 | yajl_gen_status close_status = yajl_gen_array_close(handle); 211 | if (status == yajl_gen_in_error_state) 212 | return status; 213 | return close_status; 214 | } 215 | if (PyDict_Check(object)) { 216 | PyObject *key, *value; 217 | Py_ssize_t position = 0; 218 | 219 | status = yajl_gen_map_open(handle); 220 | if (status == yajl_max_depth_exceeded) goto exit; 221 | while (PyDict_Next(object, &position, &key, &value)) { 222 | PyObject *newKey = key; 223 | 224 | if ( (PyFloat_Check(key)) || 225 | #ifndef IS_PYTHON3 226 | (PyInt_Check(key)) || 227 | #endif 228 | (PyLong_Check(key)) ) { 229 | 230 | /* 231 | * Performing the conversion separately for Python 2 232 | * and Python 3 to ensure we consistently generate 233 | * unicode strings in both versions 234 | */ 235 | #ifdef IS_PYTHON3 236 | newKey = PyObject_Str(key); 237 | #else 238 | newKey = PyObject_Unicode(key); 239 | #endif 240 | } 241 | 242 | status = ProcessObject(self, newKey); 243 | if (key != newKey) { 244 | Py_XDECREF(newKey); 245 | } 246 | if (status == yajl_gen_in_error_state) return status; 247 | if (status == yajl_max_depth_exceeded) goto exit; 248 | 249 | status = ProcessObject(self, value); 250 | if (status == yajl_gen_in_error_state) return status; 251 | if (status == yajl_max_depth_exceeded) goto exit; 252 | } 253 | return yajl_gen_map_close(handle); 254 | } 255 | else { 256 | object = PyObject_CallMethod((PyObject *)self, "default", "O", object); 257 | if (object==NULL) 258 | goto exit; 259 | status = ProcessObject(self, object); 260 | return status; 261 | } 262 | 263 | 264 | 265 | exit: 266 | return yajl_gen_in_error_state; 267 | } 268 | 269 | yajl_alloc_funcs *y_allocs = NULL; 270 | /* a structure used to pass context to our printer function */ 271 | struct StringAndUsedCount 272 | { 273 | PyObject * str; 274 | size_t used; 275 | }; 276 | 277 | static void py_yajl_printer(void * ctx, 278 | const char * str, 279 | unsigned int len) 280 | { 281 | struct StringAndUsedCount * sauc = (struct StringAndUsedCount *) ctx; 282 | size_t newsize; 283 | 284 | if (!sauc || !sauc->str) return; 285 | 286 | /* resize our string if necc */ 287 | newsize = Py_SIZE(sauc->str); 288 | while (sauc->used + len > newsize) newsize *= 2; 289 | if (newsize != Py_SIZE(sauc->str)) { 290 | #ifdef IS_PYTHON3 291 | _PyBytes_Resize(&(sauc->str), newsize); 292 | #else 293 | _PyString_Resize(&(sauc->str), newsize); 294 | #endif 295 | if (!sauc->str) 296 | return; 297 | } 298 | 299 | /* and append data if available */ 300 | if (len && str) { 301 | #ifdef IS_PYTHON3 302 | memcpy((void *)(((PyBytesObject *)sauc->str)->ob_sval + sauc->used), str, len); 303 | #else 304 | memcpy((void *) (((PyStringObject *) sauc->str)->ob_sval + sauc->used), str, len); 305 | #endif 306 | sauc->used += len; 307 | } 308 | } 309 | 310 | /* Efficiently allocate a python string of a fixed size containing uninitialized memory */ 311 | static PyObject * lowLevelStringAlloc(Py_ssize_t size) 312 | { 313 | #ifdef IS_PYTHON3 314 | PyBytesObject * op = (PyBytesObject *)PyObject_MALLOC(sizeof(PyBytesObject) + size); 315 | if (op) { 316 | PyObject_INIT_VAR(op, &PyBytes_Type, size); 317 | } 318 | #else 319 | PyStringObject * op = (PyStringObject *)PyObject_MALLOC(sizeof(PyStringObject) + size); 320 | if (op) { 321 | PyObject_INIT_VAR(op, &PyString_Type, size); 322 | op->ob_shash = -1; 323 | op->ob_sstate = SSTATE_NOT_INTERNED; 324 | } 325 | #endif 326 | return (PyObject *) op; 327 | } 328 | 329 | PyObject *_internal_encode(_YajlEncoder *self, PyObject *obj, yajl_gen_config genconfig) 330 | { 331 | yajl_gen generator = NULL; 332 | yajl_gen_status status; 333 | struct StringAndUsedCount sauc; 334 | #ifdef IS_PYTHON3 335 | PyObject *result = NULL; 336 | #endif 337 | 338 | /* initialize context for our printer function which 339 | * performs low level string appending, using the python 340 | * string implementation as a chunked growth buffer */ 341 | sauc.used = 0; 342 | sauc.str = lowLevelStringAlloc(PY_YAJL_CHUNK_SZ); 343 | 344 | generator = yajl_gen_alloc2(py_yajl_printer, &genconfig, NULL, (void *) &sauc); 345 | 346 | self->_generator = generator; 347 | 348 | status = ProcessObject(self, obj); 349 | 350 | yajl_gen_free(generator); 351 | self->_generator = NULL; 352 | 353 | /* if resize failed inside our printer function we'll have a null sauc.str */ 354 | if (!sauc.str) { 355 | PyErr_SetObject(PyExc_ValueError, PyUnicode_FromString("Allocation failure")); 356 | return NULL; 357 | } 358 | 359 | if ( (status == yajl_gen_in_error_state) || 360 | (status != yajl_gen_status_ok) ) { 361 | /* 362 | * If we have an exception underneath the covers, let's raise that 363 | * instead 364 | */ 365 | if (!PyErr_Occurred()) { 366 | PyErr_SetObject(PyExc_TypeError, PyUnicode_FromString("Object is not JSON serializable")); 367 | } 368 | Py_XDECREF(sauc.str); 369 | return NULL; 370 | } 371 | 372 | #ifdef IS_PYTHON3 373 | result = PyUnicode_DecodeUTF8(((PyBytesObject *)sauc.str)->ob_sval, sauc.used, "strict"); 374 | Py_XDECREF(sauc.str); 375 | return result; 376 | #else 377 | /* truncate to used size, and resize will handle the null plugging */ 378 | _PyString_Resize(&sauc.str, sauc.used); 379 | return sauc.str; 380 | #endif 381 | } 382 | 383 | PyObject *py_yajlencoder_default(PYARGS) 384 | { 385 | PyObject *value; 386 | if (!PyArg_ParseTuple(args, "O", &value)) 387 | return NULL; 388 | PyErr_SetObject(PyExc_TypeError, PyUnicode_FromString("Not serializable to JSON")); 389 | return NULL; 390 | } 391 | 392 | PyObject *py_yajlencoder_encode(PYARGS) 393 | { 394 | _YajlEncoder *encoder = (_YajlEncoder *)(self); 395 | yajl_gen_config config = {0, NULL}; 396 | PyObject *value; 397 | 398 | if (!PyArg_ParseTuple(args, "O", &value)) 399 | return NULL; 400 | return _internal_encode(encoder, value, config); 401 | } 402 | 403 | int yajlencoder_init(PYARGS) 404 | { 405 | _YajlEncoder *me = (_YajlEncoder *)(self); 406 | 407 | if (!me) 408 | return 1; 409 | return 0; 410 | } 411 | 412 | void yajlencoder_dealloc(_YajlEncoder *self) 413 | { 414 | #ifdef IS_PYTHON3 415 | Py_TYPE(self)->tp_free((PyObject*)self); 416 | #else 417 | self->ob_type->tp_free((PyObject*)self); 418 | #endif 419 | } 420 | -------------------------------------------------------------------------------- /includes/yajl: -------------------------------------------------------------------------------- 1 | ../yajl/src/api -------------------------------------------------------------------------------- /ptrstack.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2009, R. Tyler Ballance 3 | * 4 | * Redistribution and use in source and binary forms, with or without 5 | * modification, are permitted provided that the following conditions are 6 | * met: 7 | * 8 | * 1. Redistributions of source code must retain the above copyright 9 | * notice, this list of conditions and the following disclaimer. 10 | * 11 | * 2. Redistributions in binary form must reproduce the above copyright 12 | * notice, this list of conditions and the following disclaimer in 13 | * the documentation and/or other materials provided with the 14 | * distribution. 15 | * 16 | * 3. Neither the name of R. Tyler Ballance nor the names of its 17 | * contributors may be used to endorse or promote products derived 18 | * from this software without specific prior written permission. 19 | * 20 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 21 | * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 22 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 23 | * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, 24 | * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 25 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 26 | * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 27 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 28 | * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING 29 | * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 30 | * POSSIBILITY OF SUCH DAMAGE. 31 | */ 32 | 33 | /* 34 | * A header only, highly efficient custom pointer stack implementation, 35 | * used in py-yajl to maintain parse state. 36 | */ 37 | 38 | #ifndef __PY_YAJL_BYTESTACK_H__ 39 | #define __PY_YAJL_BYTESTACK_H__ 40 | 41 | #include 42 | #include "assert.h" 43 | 44 | #define PY_YAJL_PS_INC 128 45 | 46 | typedef struct py_yajl_bytestack_t 47 | { 48 | PyObject ** stack; 49 | unsigned int size; 50 | unsigned int used; 51 | } py_yajl_bytestack; 52 | 53 | /* initialize a bytestack */ 54 | #define py_yajl_ps_init(ops) { \ 55 | (ops).stack = NULL; \ 56 | (ops).size = 0; \ 57 | (ops).used = 0; \ 58 | } \ 59 | 60 | 61 | /* initialize a bytestack */ 62 | #define py_yajl_ps_free(ops) \ 63 | if ((ops).stack) free((ops).stack); 64 | 65 | #define py_yajl_ps_current(ops) \ 66 | (assert((ops).used > 0), (ops).stack[(ops).used - 1]) 67 | 68 | #define py_yajl_ps_length(ops) ((ops).used) 69 | 70 | #define py_yajl_ps_push(ops, pointer) { \ 71 | if (((ops).size - (ops).used) == 0) { \ 72 | (ops).size += PY_YAJL_PS_INC; \ 73 | (ops).stack = realloc((void *) (ops).stack, sizeof(PyObject *) * (ops).size); \ 74 | } \ 75 | (ops).stack[((ops).used)++] = (pointer); \ 76 | } 77 | 78 | /* removes the top item of the stack, returns nothing */ 79 | #define py_yajl_ps_pop(ops) { ((ops).used)--; } 80 | 81 | #define py_yajl_ps_set(ops, pointer) \ 82 | (ops).stack[((ops).used) - 1] = (pointer); 83 | 84 | 85 | #endif 86 | -------------------------------------------------------------------------------- /py_yajl.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2009, R. Tyler Ballance 3 | * 4 | * Redistribution and use in source and binary forms, with or without 5 | * modification, are permitted provided that the following conditions are 6 | * met: 7 | * 8 | * 1. Redistributions of source code must retain the above copyright 9 | * notice, this list of conditions and the following disclaimer. 10 | * 11 | * 2. Redistributions in binary form must reproduce the above copyright 12 | * notice, this list of conditions and the following disclaimer in 13 | * the documentation and/or other materials provided with the 14 | * distribution. 15 | * 16 | * 3. Neither the name of R. Tyler Ballance nor the names of its 17 | * contributors may be used to endorse or promote products derived 18 | * from this software without specific prior written permission. 19 | * 20 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 21 | * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 22 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 23 | * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, 24 | * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 25 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 26 | * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 27 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 28 | * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING 29 | * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 30 | * POSSIBILITY OF SUCH DAMAGE. 31 | */ 32 | 33 | #ifndef _PY_YAJL_H_ 34 | #define _PY_YAJL_H_ 35 | 36 | #include 37 | #include 38 | #include "ptrstack.h" 39 | 40 | #if PY_MAJOR_VERSION >= 3 41 | #define IS_PYTHON3 42 | #define PyString_AsStringAndSize PyBytes_AsStringAndSize 43 | #define PyString_Check PyBytes_Check 44 | #endif 45 | 46 | typedef struct { 47 | PyObject_HEAD 48 | 49 | py_yajl_bytestack elements; 50 | py_yajl_bytestack keys; 51 | PyObject *root; 52 | 53 | } _YajlDecoder; 54 | 55 | typedef struct { 56 | PyObject_HEAD 57 | /* type specifics */ 58 | void *_generator; 59 | } _YajlEncoder; 60 | 61 | #define PYARGS PyObject *self, PyObject *args, PyObject *kwargs 62 | enum { failure, success }; 63 | 64 | #define PY_YAJL_CHUNK_SZ 64 65 | 66 | /* Defining the Py_SIZE macro for 2.4/2.5 compat */ 67 | #ifndef Py_SIZE 68 | #define Py_SIZE(ob) (((PyVarObject*)(ob))->ob_size) 69 | #endif 70 | 71 | /* 72 | * PyUnicode_FromStringAndSize isn't defined in Python 2.4/2.5; IIRC 73 | * JSON strings /should/ be UTF-8 encoded, so PyUnicode_DecodeUTF8() 74 | * seems like the most logical extension 75 | */ 76 | #ifndef PyUnicode_FromStringAndSize 77 | #define PyUnicode_FromStringAndSize(a, b) PyUnicode_DecodeUTF8(a, b, NULL) 78 | #endif 79 | #ifndef PyUnicode_FromString 80 | #define PyUnicode_FromString(s) PyUnicode_DecodeUTF8(s, strlen(s), NULL) 81 | #endif 82 | 83 | /* On Python 2.4 Py_ssize_t doesn't exist */ 84 | #ifndef Py_ssize_t 85 | #define Py_ssize_t ssize_t 86 | #endif 87 | 88 | /* 89 | * Methods defined for the YajlDecoder type in decoder.c 90 | */ 91 | extern PyObject *py_yajldecoder_decode(PYARGS); 92 | extern int yajldecoder_init(PYARGS); 93 | extern void yajldecoder_dealloc(_YajlDecoder *self); 94 | extern PyObject *_internal_decode(_YajlDecoder *self, char *buffer, unsigned int buflen); 95 | 96 | 97 | /* 98 | * Methods defined for the YajlEncoder type in encoder.c 99 | */ 100 | extern PyObject *py_yajlencoder_encode(PYARGS); 101 | extern PyObject* py_yajlencoder_default(PYARGS); 102 | extern int yajlencoder_init(PYARGS); 103 | extern void yajlencoder_dealloc(_YajlEncoder *self); 104 | extern PyObject *_internal_encode(_YajlEncoder *self, PyObject *obj, yajl_gen_config config); 105 | 106 | #endif 107 | 108 | -------------------------------------------------------------------------------- /runtests.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | python setup.py build && PYTHONPATH=.:build/lib.linux-x86_64-2.6 python tests/unit.py && zcat test_data/issue_11.gz| PYTHONPATH=build/lib.linux-x86_64-2.6 ./tests/issue_11.py && python3 setup.py build && PYTHONPATH=build/lib.linux-x86_64-3.1 python3 tests/unit.py 4 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | import os 4 | import subprocess 5 | import sys 6 | 7 | USE_SETUPTOOLS = False 8 | try: 9 | from setuptools import setup, Extension 10 | USE_SETUPTOOLS = True 11 | except ImportError: 12 | from distutils.core import setup, Extension 13 | 14 | version = '0.3.6' 15 | if os.path.exists('.git'): 16 | try: 17 | commit = subprocess.Popen(['git', 'log', '--max-count=1', '--format=%h'], stdout=subprocess.PIPE).communicate()[0] 18 | version = '%s-%s' % (version, commit.strip()) 19 | except: 20 | pass 21 | 22 | 23 | base_modules = [ 24 | Extension('yajl', [ 25 | 'yajl.c', 26 | 'encoder.c', 27 | 'decoder.c', 28 | 'yajl_hacks.c', 29 | 'yajl/src/yajl_alloc.c', 30 | 'yajl/src/yajl_buf.c', 31 | 'yajl/src/yajl.c', 32 | 'yajl/src/yajl_encode.c', 33 | 'yajl/src/yajl_gen.c', 34 | 'yajl/src/yajl_lex.c', 35 | 'yajl/src/yajl_parser.c', 36 | ], 37 | include_dirs=('.', 'includes/', 'yajl/src'), 38 | extra_compile_args=['-Wall', '-DMOD_VERSION="%s"' % version], 39 | language='c'), 40 | ] 41 | 42 | 43 | packages = ('yajl',) 44 | 45 | 46 | setup_kwargs = dict( 47 | name = 'yajl', 48 | description = '''A CPython module for Yet-Another-Json-Library''', 49 | version = version, 50 | author = 'R. Tyler Ballance', 51 | author_email = 'tyler@monkeypox.org', 52 | url = 'http://rtyler.github.com/py-yajl', 53 | long_description='''The `yajl` module provides a Python binding to the Yajl library originally written by `Lloyd Hilaiel `_. 54 | 55 | Mailing List 56 | ============== 57 | You can discuss the C library **Yajl** or py-yajl on the Yajl mailing list, 58 | simply send your email to yajl@librelist.com 59 | ''', 60 | ext_modules=base_modules, 61 | ) 62 | 63 | if USE_SETUPTOOLS: 64 | setup_kwargs.update({'test_suite' : 'tests.unit'}) 65 | 66 | if not os.listdir('yajl'): 67 | # Submodule hasn't been created, let's inform the user 68 | print('>>> It looks like the `yajl` submodule hasn\'t been initialized') 69 | print('>>> I\'ll try to do that, but if I fail, you can run:') 70 | print('>>> `git submodule update --init`') 71 | subprocess.call(['git', 'submodule', 'update', '--init']) 72 | 73 | subprocess.call(['git', 'submodule', 'update',]) 74 | 75 | if not os.path.exists('includes'): 76 | # Our symlink into the yajl directory isn't there, let's fixulate that 77 | os.mkdir('includes') 78 | 79 | if not os.path.exists(os.path.join('includes', 'yajl')): 80 | print('>>> Creating a symlink for compilationg: includes/yajl -> yajl/src/api') 81 | # Now that we have a directory, we need a symlink 82 | os.symlink(os.path.join('..', 'yajl', 'src', 'api'), os.path.join('includes', 'yajl')) 83 | 84 | setup(**setup_kwargs) 85 | -------------------------------------------------------------------------------- /test_data/issue_11.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rtyler/py-yajl/1f3221e58e7fd46b1a1c12cd2e7b7706d1f53d86/test_data/issue_11.gz -------------------------------------------------------------------------------- /tests/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rtyler/py-yajl/1f3221e58e7fd46b1a1c12cd2e7b7706d1f53d86/tests/__init__.py -------------------------------------------------------------------------------- /tests/issue_11.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | import yajl 4 | import sys 5 | 6 | for i, l in enumerate(sys.stdin): 7 | l = l.rstrip('\n').split('\t') 8 | d = yajl.dumps(tuple(l)) 9 | print i, 10 | -------------------------------------------------------------------------------- /tests/issue_6.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | import sys 4 | import yajl as json 5 | 6 | 7 | def foo(): 8 | 9 | for l in sys.stdin: 10 | l = l.rstrip('\n') 11 | t = json.loads(l) 12 | yield t 13 | 14 | if __name__ == '__main__': 15 | i = 0 16 | for r in foo(): 17 | i += 1 18 | if i % 1000 == 0: 19 | print '.', 20 | -------------------------------------------------------------------------------- /tests/python2.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | ''' 4 | File for keeping Python 2 specific things, that raise SyntaxError 5 | exceptions in Python 3 6 | ''' 7 | 8 | 9 | IssueSevenTest_latin1_char = u'f\xe9in' 10 | # u'早安, 爸爸' # Good morning! 11 | IssueSevenTest_chinese_char = u'\u65e9\u5b89, \u7238\u7238' 12 | 13 | IssueTwelveTest_dict = {u'a' : u'b', u'c' : u'd'} 14 | -------------------------------------------------------------------------------- /tests/unit.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | 4 | import sys 5 | import unittest 6 | 7 | def is_python3(): 8 | return sys.version_info[0] == 3 9 | 10 | if is_python3(): 11 | from io import StringIO 12 | else: 13 | from StringIO import StringIO 14 | 15 | import yajl 16 | 17 | class DecoderBase(unittest.TestCase): 18 | def decode(self, json): 19 | return yajl.Decoder().decode(json) 20 | 21 | def assertDecodesTo(self, json, value): 22 | rc = self.decode(json) 23 | assert rc == value, ('Failed to decode JSON correctly', 24 | json, value, rc) 25 | return True 26 | 27 | class BasicJSONDecodeTests(DecoderBase): 28 | def test_TrueBool(self): 29 | self.assertDecodesTo('true', True) 30 | 31 | def test_FalseBool(self): 32 | self.assertDecodesTo('false', False) 33 | 34 | def test_Null(self): 35 | self.assertDecodesTo('null', None) 36 | 37 | def test_List(self): 38 | self.assertDecodesTo('[1,2]', [1, 2]) 39 | 40 | def test_ListOfFloats(self): 41 | self.assertDecodesTo('[3.14, 2.718]', [3.14, 2.718]) 42 | 43 | def test_Dict(self): 44 | self.assertDecodesTo('{"key" : "pair"}', {'key' : 'pair'}) 45 | 46 | def test_ListInDict(self): 47 | self.assertDecodesTo(''' 48 | {"key" : [1, 2, 3]} 49 | ''', {'key' : [1, 2, 3]}) 50 | 51 | def test_DictInDict(self): 52 | self.assertDecodesTo(''' 53 | {"key" : {"subkey" : true}}''', 54 | {'key' : {'subkey' : True}}) 55 | 56 | def test_NestedDictAndList(self): 57 | self.assertDecodesTo(''' 58 | {"key" : {"subkey" : [1, 2, 3]}}''', 59 | {'key' : {'subkey' : [1,2,3]}}) 60 | 61 | 62 | class EncoderBase(unittest.TestCase): 63 | def encode(self, value): 64 | return yajl.Encoder().encode(value) 65 | 66 | def assertEncodesTo(self, value, json): 67 | rc = self.encode(value) 68 | assert rc == json, ('Failed to encode JSON correctly', locals()) 69 | return True 70 | 71 | class BasicJSONEncodeTests(EncoderBase): 72 | def test_TrueBool(self): 73 | self.assertEncodesTo(True, 'true') 74 | 75 | def test_FalseBool(self): 76 | self.assertEncodesTo(False, 'false') 77 | 78 | def test_Null(self): 79 | self.assertEncodesTo(None, 'null') 80 | 81 | def test_List(self): 82 | self.assertEncodesTo([1,2], '[1,2]') 83 | 84 | def test_Dict(self): 85 | self.assertEncodesTo({'key' : 'value'}, '{"key":"value"}') 86 | 87 | # Python 3 version 88 | #def test_UnicodeDict(self): 89 | # self.assertEncodesTo({'foō' : 'bār'}, '{"foō":"bār"}') 90 | 91 | # Python 2 version 92 | #def test_UnicodeDict(self): 93 | # self.assertEncodesTo({u'foō' : u'bār'}, '{"foō":"bār"}') 94 | 95 | def test_NestedDictAndList(self): 96 | self.assertEncodesTo({'key' : {'subkey' : [1,2,3]}}, 97 | '{"key":{"subkey":[1,2,3]}}') 98 | def test_Tuple(self): 99 | self.assertEncodesTo((1,2), '[1,2]') 100 | def test_generator(self): 101 | def f(): 102 | for i in range(10): 103 | yield i 104 | self.assertEncodesTo(f(), '[0,1,2,3,4,5,6,7,8,9]') 105 | def test_default(self): 106 | class MyEncode(yajl.Encoder): 107 | def default(self, obj): 108 | return ['foo'] 109 | rc = MyEncode().encode(MyEncode) #not supported directly -- will call default 110 | assert rc == '["foo"]', ('Failed to encode JSON correctly', locals()) 111 | return True 112 | 113 | 114 | 115 | class LoadsTest(BasicJSONDecodeTests): 116 | def decode(self, json): 117 | return yajl.loads(json) 118 | 119 | class DumpsTest(BasicJSONEncodeTests): 120 | def encode(self, value): 121 | return yajl.dumps(value) 122 | 123 | class ErrorCasesTests(unittest.TestCase): 124 | def setUp(self): 125 | self.d = yajl.Decoder() 126 | 127 | def test_EmptyString(self): 128 | self.failUnlessRaises(ValueError, self.d.decode, '') 129 | 130 | def test_None(self): 131 | self.failUnlessRaises(ValueError, self.d.decode, None) 132 | 133 | 134 | class StreamBlockingDecodingTests(unittest.TestCase): 135 | def setUp(self): 136 | self.stream = StringIO('{"foo":["one","two", ["three", "four"]]}') 137 | 138 | def test_no_object(self): 139 | self.failUnlessRaises(TypeError, yajl.load) 140 | 141 | def test_bad_object(self): 142 | self.failUnlessRaises(TypeError, yajl.load, 'this is no stream!') 143 | 144 | def test_simple_decode(self): 145 | obj = yajl.load(self.stream) 146 | self.assertEquals(obj, {'foo' : ['one', 'two', ['three', 'four']]}) 147 | 148 | class StreamIterDecodingTests(object): # TODO: Change to unittest.TestCase when I start to think about iterative 149 | def setUp(self): 150 | self.stream = StringIO('{"foo":["one","two",["three", "four"]]}') 151 | 152 | def test_no_object(self): 153 | self.failUnlessRaises(TypeError, yajl.iterload) 154 | 155 | def test_bad_object(self): 156 | self.failUnlessRaises(TypeError, yajl.iterload, 'this is no stream!') 157 | 158 | def test_simple_decode(self): 159 | for k, v in yajl.iterload(self.stream): 160 | print(k, v) 161 | 162 | 163 | class StreamEncodingTests(unittest.TestCase): 164 | def test_blocking_encode(self): 165 | obj = {'foo' : ['one', 'two', ['three', 'four']]} 166 | stream = StringIO() 167 | buffer = yajl.dump(obj, stream) 168 | self.assertEquals(stream.getvalue(), '{"foo":["one","two",["three","four"]]}') 169 | 170 | class DumpsOptionsTests(unittest.TestCase): 171 | def test_indent_four(self): 172 | rc = yajl.dumps({'foo' : 'bar'}, indent=4) 173 | expected = '{\n "foo": "bar"\n}\n' 174 | self.assertEquals(rc, expected) 175 | 176 | def test_indent_zero(self): 177 | rc = yajl.dumps({'foo' : 'bar'}, indent=0) 178 | expected = '{\n"foo": "bar"\n}\n' 179 | self.assertEquals(rc, expected) 180 | 181 | def test_indent_str(self): 182 | self.failUnlessRaises(TypeError, yajl.dumps, {'foo' : 'bar'}, indent='4') 183 | 184 | def test_negative_indent(self): 185 | ''' Negative `indent` should not result in pretty printing ''' 186 | rc = yajl.dumps({'foo' : 'bar'}, indent=-1) 187 | self.assertEquals(rc, '{"foo":"bar"}') 188 | 189 | def test_none_indent(self): 190 | ''' None `indent` should not result in pretty printing ''' 191 | rc = yajl.dumps({'foo' : 'bar'}, indent=None) 192 | self.assertEquals(rc, '{"foo":"bar"}') 193 | 194 | class DumpOptionsTests(unittest.TestCase): 195 | stream = None 196 | def setUp(self): 197 | self.stream = StringIO() 198 | 199 | def test_indent_four(self): 200 | rc = yajl.dump({'foo' : 'bar'}, self.stream, indent=4) 201 | expected = '{\n "foo": "bar"\n}\n' 202 | self.assertEquals(self.stream.getvalue(), expected) 203 | 204 | def test_indent_zero(self): 205 | rc = yajl.dump({'foo' : 'bar'}, self.stream, indent=0) 206 | expected = '{\n"foo": "bar"\n}\n' 207 | self.assertEquals(self.stream.getvalue(), expected) 208 | 209 | def test_indent_str(self): 210 | self.failUnlessRaises(TypeError, yajl.dump, {'foo' : 'bar'}, self.stream, indent='4') 211 | 212 | def test_negative_indent(self): 213 | ''' Negative `indent` should not result in pretty printing ''' 214 | rc = yajl.dump({'foo' : 'bar'}, self.stream, indent=-1) 215 | self.assertEquals(self.stream.getvalue(), '{"foo":"bar"}') 216 | 217 | def test_none_indent(self): 218 | ''' None `indent` should not result in pretty printing ''' 219 | rc = yajl.dump({'foo' : 'bar'}, self.stream, indent=None) 220 | self.assertEquals(self.stream.getvalue(), '{"foo":"bar"}') 221 | 222 | class IssueSevenTest(unittest.TestCase): 223 | def test_latin1(self): 224 | ''' Testing with latin-1 for http://github.com/rtyler/py-yajl/issues/#issue/7 ''' 225 | char = 'f\xe9in' 226 | if not is_python3(): 227 | from tests import python2 228 | char = python2.IssueSevenTest_latin1_char 229 | # The `json` module uses "0123456789abcdef" for its code points 230 | # while the yajl library uses "0123456789ABCDEF", lower()'ing 231 | # to make sure the resulting strings match 232 | out = yajl.dumps(char).lower() 233 | self.assertEquals(out, '"f\\u00e9in"') 234 | 235 | out = yajl.dumps(out).lower() 236 | self.assertEquals(out, '"\\"f\\\\u00e9in\\""') 237 | 238 | out = yajl.loads(out) 239 | self.assertEquals(out, '"f\\u00e9in"') 240 | 241 | out = yajl.loads(out) 242 | self.assertEquals(out, char) 243 | 244 | def test_chinese(self): 245 | ''' Testing with simplified chinese for http://github.com/rtyler/py-yajl/issues/#issue/7 ''' 246 | char = '\u65e9\u5b89, \u7238\u7238' 247 | if not is_python3(): 248 | from tests import python2 249 | char = python2.IssueSevenTest_chinese_char 250 | out = yajl.dumps(char).lower() 251 | self.assertEquals(out, '"\\u65e9\\u5b89, \\u7238\\u7238"') 252 | 253 | out = yajl.dumps(out).lower() 254 | self.assertEquals(out, '"\\"\\\\u65e9\\\\u5b89, \\\\u7238\\\\u7238\\""') 255 | 256 | out = yajl.loads(out) 257 | self.assertEquals(out, '"\\u65e9\\u5b89, \\u7238\\u7238"') 258 | 259 | out = yajl.loads(out) 260 | self.assertEquals(out, char) 261 | 262 | 263 | class IssueEightTest(unittest.TestCase): 264 | def runTest(self): 265 | ''' http://github.com/rtyler/py-yajl/issues#issue/8 ''' 266 | encoded = yajl.dumps([(2,3,)]) 267 | decoded = yajl.loads(encoded) 268 | self.assertEquals(len(decoded), 1) 269 | self.assertEquals(decoded[0][0], 2) 270 | self.assertEquals(decoded[0][1], 3) 271 | 272 | class IssueNineTest(unittest.TestCase): 273 | def testListOfSets(self): 274 | ''' http://github.com/rtyler/py-yajl/issues#issue/9 ''' 275 | self.failUnlessRaises(TypeError, yajl.dumps, [set([2,3])]) 276 | 277 | def testSets(self): 278 | ''' http://github.com/rtyler/py-yajl/issues#issue/9 ''' 279 | self.failUnlessRaises(TypeError, yajl.dumps, set([2,3])) 280 | 281 | 282 | class IssueTenTest(unittest.TestCase): 283 | def testInt(self): 284 | ''' http://github.com/rtyler/py-yajl/issues#issue/10 ''' 285 | data = {1 : 2} 286 | result = yajl.loads(yajl.dumps(data)) 287 | self.assertEquals({'1': 2}, result) 288 | 289 | def testFloat(self): 290 | ''' http://github.com/rtyler/py-yajl/issues#issue/10 ''' 291 | data = {1.2 : 2} 292 | result = yajl.loads(yajl.dumps(data)) 293 | self.assertEquals({'1.2': 2}, result) 294 | 295 | def testLong(self): 296 | ''' http://github.com/rtyler/py-yajl/issues#issue/10 ''' 297 | if is_python3(): 298 | return 299 | data = {long(1) : 2} 300 | result = yajl.loads(yajl.dumps(data)) 301 | self.assertEquals({'1': 2}, result) 302 | 303 | class IssueTwelveTest(unittest.TestCase): 304 | def runTest(self): 305 | normal = {'a' : 'b', 'c' : 'd'} 306 | self.assertEquals(yajl.dumps(normal), '{"a":"b","c":"d"}') 307 | 308 | if not is_python3(): 309 | from tests import python2 310 | self.assertEquals(yajl.dumps(python2.IssueTwelveTest_dict), '{"a":"b","c":"d"}') 311 | 312 | class IssueThirteenTest(unittest.TestCase): 313 | def runTest(self): 314 | try: 315 | import json 316 | except ImportError: 317 | # Skip the test on 2.4/2.5 318 | return 319 | 320 | rc = yajl.monkeypatch() 321 | self.assertTrue(rc) 322 | self.assertEquals(sys.modules['json'], sys.modules['yajl']) 323 | 324 | 325 | class IssueSixteenTest(unittest.TestCase): 326 | def runTest(self): 327 | dumpable = [11889582081] 328 | 329 | rc = yajl.dumps(dumpable) 330 | self.assertEquals(rc, '[11889582081]') 331 | rc = yajl.loads(rc) 332 | self.assertEquals(rc, dumpable) 333 | 334 | 335 | class IssueTwentySevenTest(unittest.TestCase): 336 | "https://github.com/rtyler/py-yajl/issues/27" 337 | def runTest(self): 338 | u = u'[{"data":"Podstawow\u0105 opiek\u0119 zdrowotn\u0105"}]' 339 | self.assertEqual( 340 | yajl.dumps(yajl.loads(u)), 341 | '[{"data":"Podstawow\\u0105 opiek\\u0119 zdrowotn\\u0105"}]') 342 | 343 | 344 | if __name__ == '__main__': 345 | verbosity = '-v' in sys.argv and 2 or 1 346 | runner = unittest.TextTestRunner(verbosity=verbosity) 347 | if 'xml' in sys.argv: 348 | import xmlrunner 349 | runner = xmlrunner.XMLTestRunner(filename='Yajl-Tests.xml') 350 | suites = unittest.findTestCases(sys.modules[__name__]) 351 | results = runner.run(unittest.TestSuite(suites)) 352 | else: 353 | unittest.main() 354 | 355 | -------------------------------------------------------------------------------- /tests/xmlrunner.py: -------------------------------------------------------------------------------- 1 | """ 2 | XML Test Runner for PyUnit 3 | """ 4 | 5 | # Written by Sebastian Rittau and placed in 6 | # the Public Domain. With contributions by Paolo Borelli. 7 | 8 | __revision__ = "$Id: /private/python/stdlib/xmlrunner.py 16654 2007-11-12T12:46:35.368945Z srittau $" 9 | 10 | import os.path 11 | import re 12 | import sys 13 | import time 14 | import traceback 15 | import unittest 16 | try: 17 | from io import StringIO 18 | except ImportError: 19 | from StringIO import StringIO 20 | from xml.sax.saxutils import escape 21 | 22 | 23 | 24 | class _TestInfo(object): 25 | 26 | """Information about a particular test. 27 | 28 | Used by _XMLTestResult. 29 | 30 | """ 31 | 32 | def __init__(self, test, time): 33 | _pieces = test.id().split('.') 34 | (self._class, self._method) = ('.'.join(_pieces[:-1]), _pieces[-1]) 35 | self._time = time 36 | self._error = None 37 | self._failure = None 38 | 39 | 40 | def print_report(self, stream): 41 | """Print information about this test case in XML format to the 42 | supplied stream. 43 | 44 | """ 45 | stream.write(' ' % \ 46 | { 47 | "class": self._class, 48 | "method": self._method, 49 | "time": self._time, 50 | }) 51 | if self._failure != None: 52 | self._print_error(stream, 'failure', self._failure) 53 | if self._error != None: 54 | self._print_error(stream, 'error', self._error) 55 | stream.write('\n') 56 | 57 | def _print_error(self, stream, tagname, error): 58 | """Print information from a failure or error to the supplied stream.""" 59 | text = escape(str(error[1])) 60 | stream.write('\n') 61 | stream.write(' <%s type="%s">%s\n' \ 62 | % (tagname, issubclass(error[0], Exception) and error[0].__name__ or str(error[0]), text)) 63 | tb_stream = StringIO() 64 | traceback.print_tb(error[2], None, tb_stream) 65 | stream.write(escape(tb_stream.getvalue())) 66 | stream.write(' \n' % tagname) 67 | stream.write(' ') 68 | 69 | # Module level functions since Python 2.3 doesn't grok decorators 70 | def create_success(test, time): 71 | """Create a _TestInfo instance for a successful test.""" 72 | return _TestInfo(test, time) 73 | 74 | def create_failure(test, time, failure): 75 | """Create a _TestInfo instance for a failed test.""" 76 | info = _TestInfo(test, time) 77 | info._failure = failure 78 | return info 79 | 80 | def create_error(test, time, error): 81 | """Create a _TestInfo instance for an erroneous test.""" 82 | info = _TestInfo(test, time) 83 | info._error = error 84 | return info 85 | 86 | class _XMLTestResult(unittest.TestResult): 87 | 88 | """A test result class that stores result as XML. 89 | 90 | Used by XMLTestRunner. 91 | 92 | """ 93 | 94 | def __init__(self, classname): 95 | unittest.TestResult.__init__(self) 96 | self._test_name = classname 97 | self._start_time = None 98 | self._tests = [] 99 | self._error = None 100 | self._failure = None 101 | 102 | def startTest(self, test): 103 | unittest.TestResult.startTest(self, test) 104 | self._error = None 105 | self._failure = None 106 | self._start_time = time.time() 107 | 108 | def stopTest(self, test): 109 | time_taken = time.time() - self._start_time 110 | unittest.TestResult.stopTest(self, test) 111 | if self._error: 112 | info = create_error(test, time_taken, self._error) 113 | elif self._failure: 114 | info = create_failure(test, time_taken, self._failure) 115 | else: 116 | info = create_success(test, time_taken) 117 | self._tests.append(info) 118 | 119 | def addError(self, test, err): 120 | unittest.TestResult.addError(self, test, err) 121 | self._error = err 122 | 123 | def addFailure(self, test, err): 124 | unittest.TestResult.addFailure(self, test, err) 125 | self._failure = err 126 | 127 | def print_report(self, stream, time_taken, out, err): 128 | """Prints the XML report to the supplied stream. 129 | 130 | The time the tests took to perform as well as the captured standard 131 | output and standard error streams must be passed in.a 132 | 133 | """ 134 | stream.write('\n' % \ 137 | { 138 | "n": self._test_name, 139 | "t": self.testsRun, 140 | "time": time_taken, 141 | }) 142 | for info in self._tests: 143 | info.print_report(stream) 144 | stream.write(' \n' % out) 145 | stream.write(' \n' % err) 146 | stream.write('\n') 147 | 148 | 149 | class XMLTestRunner(object): 150 | 151 | """A test runner that stores results in XML format compatible with JUnit. 152 | 153 | XMLTestRunner(stream=None) -> XML test runner 154 | 155 | The XML file is written to the supplied stream. If stream is None, the 156 | results are stored in a file called TEST-..xml in the 157 | current working directory (if not overridden with the path property), 158 | where and are the module and class name of the test class. 159 | 160 | """ 161 | 162 | def __init__(self, *args, **kwargs): 163 | self._stream = kwargs.get('stream') 164 | self._filename = kwargs.get('filename') 165 | self._path = "." 166 | 167 | def run(self, test): 168 | """Run the given test case or test suite.""" 169 | class_ = test.__class__ 170 | classname = class_.__module__ + "." + class_.__name__ 171 | if self._stream == None: 172 | filename = "TEST-%s.xml" % classname 173 | if self._filename: 174 | filename = self._filename 175 | stream = open(os.path.join(self._path, filename), "w") 176 | stream.write('\n') 177 | else: 178 | stream = self._stream 179 | 180 | result = _XMLTestResult(classname) 181 | start_time = time.time() 182 | 183 | # TODO: Python 2.5: Use the with statement 184 | old_stdout = sys.stdout 185 | old_stderr = sys.stderr 186 | sys.stdout = StringIO() 187 | sys.stderr = StringIO() 188 | 189 | try: 190 | test(result) 191 | try: 192 | out_s = sys.stdout.getvalue() 193 | except AttributeError: 194 | out_s = "" 195 | try: 196 | err_s = sys.stderr.getvalue() 197 | except AttributeError: 198 | err_s = "" 199 | finally: 200 | sys.stdout = old_stdout 201 | sys.stderr = old_stderr 202 | 203 | time_taken = time.time() - start_time 204 | result.print_report(stream, time_taken, out_s, err_s) 205 | if self._stream == None: 206 | stream.close() 207 | 208 | return result 209 | 210 | def _set_path(self, path): 211 | self._path = path 212 | 213 | path = property(lambda self: self._path, _set_path, None, 214 | """The path where the XML files are stored. 215 | 216 | This property is ignored when the XML file is written to a file 217 | stream.""") 218 | 219 | 220 | class XMLTestRunnerTest(unittest.TestCase): 221 | def setUp(self): 222 | self._stream = StringIO() 223 | 224 | def _try_test_run(self, test_class, expected): 225 | 226 | """Run the test suite against the supplied test class and compare the 227 | XML result against the expected XML string. Fail if the expected 228 | string doesn't match the actual string. All time attribute in the 229 | expected string should have the value "0.000". All error and failure 230 | messages are reduced to "Foobar". 231 | 232 | """ 233 | 234 | runner = XMLTestRunner(self._stream) 235 | runner.run(unittest.makeSuite(test_class)) 236 | 237 | got = self._stream.getvalue() 238 | # Replace all time="X.YYY" attributes by time="0.000" to enable a 239 | # simple string comparison. 240 | got = re.sub(r'time="\d+\.\d+"', 'time="0.000"', got) 241 | # Likewise, replace all failure and error messages by a simple "Foobar" 242 | # string. 243 | got = re.sub(r'(?s).*?', r'Foobar', got) 244 | got = re.sub(r'(?s).*?', r'Foobar', got) 245 | 246 | self.assertEqual(expected, got) 247 | 248 | def test_no_tests(self): 249 | """Regression test: Check whether a test run without any tests 250 | matches a previous run. 251 | 252 | """ 253 | class TestTest(unittest.TestCase): 254 | pass 255 | self._try_test_run(TestTest, """ 256 | 257 | 258 | 259 | """) 260 | 261 | def test_success(self): 262 | """Regression test: Check whether a test run with a successful test 263 | matches a previous run. 264 | 265 | """ 266 | class TestTest(unittest.TestCase): 267 | def test_foo(self): 268 | pass 269 | self._try_test_run(TestTest, """ 270 | 271 | 272 | 273 | 274 | """) 275 | 276 | def test_failure(self): 277 | """Regression test: Check whether a test run with a failing test 278 | matches a previous run. 279 | 280 | """ 281 | class TestTest(unittest.TestCase): 282 | def test_foo(self): 283 | self.assert_(False) 284 | self._try_test_run(TestTest, """ 285 | 286 | Foobar 287 | 288 | 289 | 290 | 291 | """) 292 | 293 | def test_error(self): 294 | """Regression test: Check whether a test run with a erroneous test 295 | matches a previous run. 296 | 297 | """ 298 | class TestTest(unittest.TestCase): 299 | def test_foo(self): 300 | raise IndexError() 301 | self._try_test_run(TestTest, """ 302 | 303 | Foobar 304 | 305 | 306 | 307 | 308 | """) 309 | 310 | def test_stdout_capture(self): 311 | """Regression test: Check whether a test run with output to stdout 312 | matches a previous run. 313 | 314 | """ 315 | class TestTest(unittest.TestCase): 316 | def test_foo(self): 317 | print("Test") 318 | self._try_test_run(TestTest, """ 319 | 320 | 322 | 323 | 324 | """) 325 | 326 | def test_stderr_capture(self): 327 | """Regression test: Check whether a test run with output to stderr 328 | matches a previous run. 329 | 330 | """ 331 | class TestTest(unittest.TestCase): 332 | def test_foo(self): 333 | sys.stderr.write('Test\n') 334 | self._try_test_run(TestTest, """ 335 | 336 | 337 | 339 | 340 | """) 341 | 342 | class NullStream(object): 343 | """A file-like object that discards everything written to it.""" 344 | def write(self, buffer): 345 | pass 346 | 347 | def test_unittests_changing_stdout(self): 348 | """Check whether the XMLTestRunner recovers gracefully from unit tests 349 | that change stdout, but don't change it back properly. 350 | 351 | """ 352 | class TestTest(unittest.TestCase): 353 | def test_foo(self): 354 | sys.stdout = XMLTestRunnerTest.NullStream() 355 | 356 | runner = XMLTestRunner(self._stream) 357 | runner.run(unittest.makeSuite(TestTest)) 358 | 359 | def test_unittests_changing_stderr(self): 360 | """Check whether the XMLTestRunner recovers gracefully from unit tests 361 | that change stderr, but don't change it back properly. 362 | 363 | """ 364 | class TestTest(unittest.TestCase): 365 | def test_foo(self): 366 | sys.stderr = XMLTestRunnerTest.NullStream() 367 | 368 | runner = XMLTestRunner(self._stream) 369 | runner.run(unittest.makeSuite(TestTest)) 370 | 371 | 372 | class XMLTestProgram(unittest.TestProgram): 373 | def runTests(self): 374 | if self.testRunner is None: 375 | self.testRunner = XMLTestRunner() 376 | unittest.TestProgram.runTests(self) 377 | 378 | main = XMLTestProgram 379 | 380 | 381 | if __name__ == "__main__": 382 | main(module=None) 383 | -------------------------------------------------------------------------------- /yajl.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2009, R. Tyler Ballance 3 | * 4 | * Redistribution and use in source and binary forms, with or without 5 | * modification, are permitted provided that the following conditions are 6 | * met: 7 | * 8 | * 1. Redistributions of source code must retain the above copyright 9 | * notice, this list of conditions and the following disclaimer. 10 | * 11 | * 2. Redistributions in binary form must reproduce the above copyright 12 | * notice, this list of conditions and the following disclaimer in 13 | * the documentation and/or other materials provided with the 14 | * distribution. 15 | * 16 | * 3. Neither the name of R. Tyler Ballance nor the names of its 17 | * contributors may be used to endorse or promote products derived 18 | * from this software without specific prior written permission. 19 | * 20 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 21 | * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 22 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 23 | * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, 24 | * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 25 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 26 | * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 27 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 28 | * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING 29 | * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 30 | * POSSIBILITY OF SUCH DAMAGE. 31 | */ 32 | #include 33 | 34 | #include "py_yajl.h" 35 | 36 | static PyMethodDef yajldecoder_methods[] = { 37 | {"decode", (PyCFunction)(py_yajldecoder_decode), METH_VARARGS, NULL}, 38 | {NULL} 39 | }; 40 | 41 | static PyMethodDef yajlencoder_methods[] = { 42 | {"encode", (PyCFunction)(py_yajlencoder_encode), METH_VARARGS, NULL}, 43 | {"default", (PyCFunction)(py_yajlencoder_default), METH_VARARGS, NULL}, 44 | {NULL} 45 | }; 46 | 47 | static PyTypeObject YajlDecoderType = { 48 | #ifdef IS_PYTHON3 49 | PyVarObject_HEAD_INIT(NULL, 0) 50 | #else 51 | PyObject_HEAD_INIT(NULL) 52 | 0, /*ob_size*/ 53 | #endif 54 | "yajl.YajlDecoder", /*tp_name*/ 55 | sizeof(_YajlDecoder), /*tp_basicsize*/ 56 | 0, /*tp_itemsize*/ 57 | (destructor)yajldecoder_dealloc, /*tp_dealloc*/ 58 | 0, /*tp_print*/ 59 | 0, /*tp_getattr*/ 60 | 0, /*tp_setattr*/ 61 | 0, /*tp_compare*/ 62 | 0, /*tp_repr*/ 63 | 0, /*tp_as_number*/ 64 | 0, /*tp_as_sequence*/ 65 | 0, /*tp_as_mapping*/ 66 | 0, /*tp_hash */ 67 | 0, /*tp_call*/ 68 | 0, /*tp_str*/ 69 | 0, /*tp_getattro*/ 70 | 0, /*tp_setattro*/ 71 | 0, /*tp_as_buffer*/ 72 | Py_TPFLAGS_DEFAULT|Py_TPFLAGS_BASETYPE, /*tp_flags*/ 73 | "Yajl-based decoder", /* tp_doc */ 74 | 0, /* tp_traverse */ 75 | 0, /* tp_clear */ 76 | 0, /* tp_richcompare */ 77 | 0, /* tp_weaklistoffset */ 78 | 0, /* tp_iter */ 79 | 0, /* tp_iternext */ 80 | yajldecoder_methods, /* tp_methods */ 81 | NULL, /* tp_members */ 82 | 0, /* tp_getset */ 83 | 0, /* tp_base */ 84 | 0, /* tp_dict */ 85 | 0, /* tp_descr_get */ 86 | 0, /* tp_descr_set */ 87 | 0, /* tp_dictoffset */ 88 | (initproc)(yajldecoder_init),/* tp_init */ 89 | 0, /* tp_alloc */ 90 | }; 91 | 92 | static PyTypeObject YajlEncoderType = { 93 | #ifdef IS_PYTHON3 94 | PyVarObject_HEAD_INIT(NULL, 0) 95 | #else 96 | PyObject_HEAD_INIT(NULL) 97 | 0, /*ob_size*/ 98 | #endif 99 | "yajl.YajlEncoder", /*tp_name*/ 100 | sizeof(_YajlEncoder), /*tp_basicsize*/ 101 | 0, /*tp_itemsize*/ 102 | (destructor)yajlencoder_dealloc, /*tp_dealloc*/ 103 | 0, /*tp_print*/ 104 | 0, /*tp_getattr*/ 105 | 0, /*tp_setattr*/ 106 | 0, /*tp_compare*/ 107 | 0, /*tp_repr*/ 108 | 0, /*tp_as_number*/ 109 | 0, /*tp_as_sequence*/ 110 | 0, /*tp_as_mapping*/ 111 | 0, /*tp_hash */ 112 | 0, /*tp_call*/ 113 | 0, /*tp_str*/ 114 | 0, /*tp_getattro*/ 115 | 0, /*tp_setattro*/ 116 | 0, /*tp_as_buffer*/ 117 | Py_TPFLAGS_DEFAULT|Py_TPFLAGS_BASETYPE, /*tp_flags*/ 118 | "Yajl-based encoder", /* tp_doc */ 119 | 0, /* tp_traverse */ 120 | 0, /* tp_clear */ 121 | 0, /* tp_richcompare */ 122 | 0, /* tp_weaklistoffset */ 123 | 0, /* tp_iter */ 124 | 0, /* tp_iternext */ 125 | yajlencoder_methods, /* tp_methods */ 126 | NULL, /* tp_members */ 127 | 0, /* tp_getset */ 128 | 0, /* tp_base */ 129 | 0, /* tp_dict */ 130 | 0, /* tp_descr_get */ 131 | 0, /* tp_descr_set */ 132 | 0, /* tp_dictoffset */ 133 | (initproc)(yajlencoder_init),/* tp_init */ 134 | 0, /* tp_alloc */ 135 | }; 136 | 137 | static PyObject *py_loads(PYARGS) 138 | { 139 | PyObject *decoder = NULL; 140 | PyObject *result = NULL; 141 | PyObject *pybuffer = NULL; 142 | char *buffer = NULL; 143 | Py_ssize_t buflen = 0; 144 | 145 | if (!PyArg_ParseTuple(args, "O", &pybuffer)) 146 | return NULL; 147 | 148 | Py_INCREF(pybuffer); 149 | 150 | if (PyUnicode_Check(pybuffer)) { 151 | if (!(result = PyUnicode_AsUTF8String(pybuffer))) { 152 | Py_DECREF(pybuffer); 153 | return NULL; 154 | } 155 | Py_DECREF(pybuffer); 156 | pybuffer = result; 157 | result = NULL; 158 | } 159 | 160 | if (PyString_Check(pybuffer)) { 161 | if (PyString_AsStringAndSize(pybuffer, &buffer, &buflen)) { 162 | Py_DECREF(pybuffer); 163 | return NULL; 164 | } 165 | } else { 166 | /* really seems like this should be a TypeError, but 167 | tests/unit.py:ErrorCasesTests.test_None disagrees */ 168 | Py_DECREF(pybuffer); 169 | PyErr_SetString(PyExc_ValueError, "string or unicode expected"); 170 | return NULL; 171 | } 172 | 173 | decoder = PyObject_Call((PyObject *)(&YajlDecoderType), NULL, NULL); 174 | if (decoder == NULL) { 175 | return NULL; 176 | } 177 | 178 | result = _internal_decode( 179 | (_YajlDecoder *)decoder, buffer, (unsigned int)buflen); 180 | Py_DECREF(pybuffer); 181 | Py_XDECREF(decoder); 182 | return result; 183 | } 184 | 185 | static char *__config_gen_config(PyObject *indent, yajl_gen_config *config) 186 | { 187 | long indentLevel = -1; 188 | char *spaces = NULL; 189 | 190 | if (!indent) 191 | return NULL; 192 | 193 | if ((indent != Py_None) && (!PyLong_Check(indent)) 194 | #ifndef IS_PYTHON3 195 | && (!PyInt_Check(indent)) 196 | #endif 197 | ) { 198 | PyErr_SetObject(PyExc_TypeError, 199 | PyUnicode_FromString("`indent` must be int or None")); 200 | return NULL; 201 | } 202 | 203 | if (indent != Py_None) { 204 | indentLevel = PyLong_AsLong(indent); 205 | 206 | if (indentLevel >= 0) { 207 | config->beautify = 1; 208 | if (indentLevel == 0) { 209 | config->indentString = ""; 210 | } 211 | else { 212 | spaces = (char *)(malloc(sizeof(char) * (indentLevel + 1))); 213 | memset((void *)(spaces), (int)' ', indentLevel); 214 | spaces[indentLevel] = '\0'; 215 | config->indentString = spaces; 216 | } 217 | } 218 | } 219 | return spaces; 220 | } 221 | 222 | static PyObject *py_dumps(PYARGS) 223 | { 224 | PyObject *encoder = NULL; 225 | PyObject *obj = NULL; 226 | PyObject *result = NULL; 227 | PyObject *indent = NULL; 228 | yajl_gen_config config = { 0, NULL }; 229 | static char *kwlist[] = {"object", "indent", NULL}; 230 | char *spaces = NULL; 231 | 232 | if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O|O", kwlist, &obj, &indent)) { 233 | return NULL; 234 | } 235 | 236 | spaces = __config_gen_config(indent, &config); 237 | if (PyErr_Occurred()) { 238 | return NULL; 239 | } 240 | 241 | encoder = PyObject_Call((PyObject *)(&YajlEncoderType), NULL, NULL); 242 | if (encoder == NULL) { 243 | return NULL; 244 | } 245 | 246 | result = _internal_encode((_YajlEncoder *)encoder, obj, config); 247 | Py_XDECREF(encoder); 248 | if (spaces) { 249 | free(spaces); 250 | } 251 | return result; 252 | } 253 | 254 | static PyObject *__read = NULL; 255 | static PyObject *_internal_stream_load(PyObject *args, unsigned int blocking) 256 | { 257 | PyObject *decoder = NULL; 258 | PyObject *stream = NULL; 259 | PyObject *buffer = NULL; 260 | PyObject *result = NULL; 261 | #ifdef IS_PYTHON3 262 | PyObject *bufferstring = NULL; 263 | #endif 264 | 265 | if (!PyArg_ParseTuple(args, "O", &stream)) { 266 | goto bad_type; 267 | } 268 | 269 | if (__read == NULL) { 270 | __read = PyUnicode_FromString("read"); 271 | } 272 | 273 | if (!PyObject_HasAttr(stream, __read)) { 274 | goto bad_type; 275 | } 276 | 277 | buffer = PyObject_CallMethodObjArgs(stream, __read, NULL); 278 | 279 | if (!buffer) 280 | return NULL; 281 | 282 | #ifdef IS_PYTHON3 283 | bufferstring = PyUnicode_AsUTF8String(buffer); 284 | if (!bufferstring) 285 | return NULL; 286 | #endif 287 | 288 | decoder = PyObject_Call((PyObject *)(&YajlDecoderType), NULL, NULL); 289 | if (decoder == NULL) { 290 | return NULL; 291 | } 292 | 293 | #ifdef IS_PYTHON3 294 | result = _internal_decode((_YajlDecoder *)decoder, PyBytes_AsString(bufferstring), 295 | PyBytes_Size(bufferstring)); 296 | Py_XDECREF(bufferstring); 297 | #else 298 | result = _internal_decode((_YajlDecoder *)decoder, PyString_AsString(buffer), 299 | PyString_Size(buffer)); 300 | #endif 301 | Py_XDECREF(decoder); 302 | Py_XDECREF(buffer); 303 | return result; 304 | 305 | bad_type: 306 | PyErr_SetObject(PyExc_TypeError, PyUnicode_FromString("Must pass a single stream object")); 307 | return NULL; 308 | } 309 | 310 | static PyObject *py_load(PYARGS) 311 | { 312 | return _internal_stream_load(args, 1); 313 | } 314 | static PyObject *py_iterload(PYARGS) 315 | { 316 | return _internal_stream_load(args, 0); 317 | } 318 | 319 | static PyObject *__write = NULL; 320 | static PyObject *_internal_stream_dump(PyObject *object, PyObject *stream, unsigned int blocking, 321 | yajl_gen_config config) 322 | { 323 | PyObject *encoder = NULL; 324 | PyObject *buffer = NULL; 325 | 326 | if (__write == NULL) { 327 | __write = PyUnicode_FromString("write"); 328 | } 329 | 330 | if (!PyObject_HasAttr(stream, __write)) { 331 | goto bad_type; 332 | } 333 | 334 | encoder = PyObject_Call((PyObject *)(&YajlEncoderType), NULL, NULL); 335 | if (encoder == NULL) { 336 | return NULL; 337 | } 338 | 339 | buffer = _internal_encode((_YajlEncoder *)encoder, object, config); 340 | PyObject_CallMethodObjArgs(stream, __write, buffer, NULL); 341 | Py_XDECREF(encoder); 342 | Py_XDECREF(buffer); 343 | return Py_True; 344 | 345 | bad_type: 346 | PyErr_SetObject(PyExc_TypeError, PyUnicode_FromString("Must pass a stream object")); 347 | return NULL; 348 | } 349 | 350 | static PyObject *py_dump(PYARGS) 351 | { 352 | PyObject *object = NULL; 353 | PyObject *indent = NULL; 354 | PyObject *stream = NULL; 355 | PyObject *result = NULL; 356 | yajl_gen_config config = { 0, NULL }; 357 | static char *kwlist[] = {"object", "stream", "indent", NULL}; 358 | char *spaces = NULL; 359 | 360 | if (!PyArg_ParseTupleAndKeywords(args, kwargs, "OO|O", kwlist, &object, &stream, &indent)) { 361 | return NULL; 362 | } 363 | 364 | spaces = __config_gen_config(indent, &config); 365 | if (PyErr_Occurred()) { 366 | return NULL; 367 | } 368 | result = _internal_stream_dump(object, stream, 0, config); 369 | if (spaces) { 370 | free(spaces); 371 | } 372 | return result; 373 | } 374 | 375 | static PyObject *py_monkeypatch(PYARGS) 376 | { 377 | PyObject *sys = PyImport_ImportModule("sys"); 378 | PyObject *modules = PyObject_GetAttrString(sys, "modules"); 379 | PyObject *yajl = PyDict_GetItemString(modules, "yajl"); 380 | 381 | if (!yajl) { 382 | return Py_False; 383 | } 384 | 385 | PyDict_SetItemString(modules, "json_old", PyDict_GetItemString(modules, "json")); 386 | PyDict_SetItemString(modules, "json", yajl); 387 | 388 | Py_XDECREF(sys); 389 | Py_XDECREF(modules); 390 | return Py_True; 391 | } 392 | 393 | static struct PyMethodDef yajl_methods[] = { 394 | {"dumps", (PyCFunctionWithKeywords)(py_dumps), METH_VARARGS | METH_KEYWORDS, 395 | "yajl.dumps(obj [, indent=None])\n\n\ 396 | Returns an encoded JSON string of the specified `obj`\n\ 397 | \n\ 398 | If `indent` is a non-negative integer, then JSON array elements \n\ 399 | and object members will be pretty-printed with that indent level. \n\ 400 | An indent level of 0 will only insert newlines. None (the default) \n\ 401 | selects the most compact representation.\n\ 402 | "}, 403 | {"loads", (PyCFunction)(py_loads), METH_VARARGS, 404 | "yajl.loads(string)\n\n\ 405 | Returns a decoded object based on the given JSON `string`"}, 406 | {"load", (PyCFunction)(py_load), METH_VARARGS, 407 | "yajl.load(fp)\n\n\ 408 | Returns a decoded object based on the JSON read from the `fp` stream-like\n\ 409 | object; *Note:* It is expected that `fp` supports the `read()` method"}, 410 | {"dump", (PyCFunctionWithKeywords)(py_dump), METH_VARARGS | METH_KEYWORDS, 411 | "yajl.dump(obj, fp [, indent=None])\n\n\ 412 | Encodes the given `obj` and writes it to the `fp` stream-like object. \n\ 413 | *Note*: It is expected that `fp` supports the `write()` method\n\ 414 | \n\ 415 | If `indent` is a non-negative integer, then JSON array elements \n\ 416 | and object members will be pretty-printed with that indent level. \n\ 417 | An indent level of 0 will only insert newlines. None (the default) \n\ 418 | selects the most compact representation.\n\ 419 | "}, 420 | /* 421 | {"iterload", (PyCFunction)(py_iterload), METH_VARARGS, NULL}, 422 | */ 423 | {"monkeypatch", (PyCFunction)(py_monkeypatch), METH_NOARGS, 424 | "yajl.monkeypatch()\n\n\ 425 | Monkey-patches the yajl module into sys.modules as \"json\"\n\ 426 | "}, 427 | {NULL} 428 | }; 429 | 430 | 431 | #ifdef IS_PYTHON3 432 | static struct PyModuleDef yajlmodule = { 433 | PyModuleDef_HEAD_INIT, 434 | "yajl", 435 | #else 436 | PyMODINIT_FUNC inityajl(void) 437 | { 438 | 439 | PyObject *module = Py_InitModule3("yajl", yajl_methods, 440 | #endif 441 | "Providing a pythonic interface to the yajl (Yet Another JSON Library) parser\n\n\ 442 | The interface is similar to that of simplejson or jsonlib providing a consistent syntax for JSON\n\ 443 | encoding and decoding. Unlike simplejson or jsonlib, yajl is **fast** :)\n\n\ 444 | The following benchmark was done on a dual core MacBook Pro with a fairly large (100K) JSON document:\n\ 445 | json.loads():\t\t21351.313ms\n\ 446 | simplejson.loads():\t1378.6492ms\n\ 447 | yajl.loads():\t\t502.4572ms\n\ 448 | \n\ 449 | json.dumps():\t\t7760.6348ms\n\ 450 | simplejson.dumps():\t930.9748ms\n\ 451 | yajl.dumps():\t\t681.0221ms" 452 | #ifdef IS_PYTHON3 453 | , -1, yajl_methods, NULL, NULL, NULL, NULL 454 | }; 455 | 456 | PyMODINIT_FUNC PyInit_yajl(void) 457 | { 458 | PyObject *module = PyModule_Create(&yajlmodule); 459 | #else 460 | ); 461 | #endif 462 | 463 | PyObject *version = PyUnicode_FromString(MOD_VERSION); 464 | PyModule_AddObject(module, "__version__", version); 465 | 466 | YajlDecoderType.tp_new = PyType_GenericNew; 467 | if (PyType_Ready(&YajlDecoderType) < 0) { 468 | goto bad_exit; 469 | } 470 | 471 | Py_INCREF(&YajlDecoderType); 472 | PyModule_AddObject(module, "Decoder", (PyObject *)(&YajlDecoderType)); 473 | 474 | YajlEncoderType.tp_new = PyType_GenericNew; 475 | if (PyType_Ready(&YajlEncoderType) < 0) { 476 | goto bad_exit; 477 | } 478 | 479 | Py_INCREF(&YajlEncoderType); 480 | PyModule_AddObject(module, "Encoder", (PyObject *)(&YajlEncoderType)); 481 | 482 | #ifdef IS_PYTHON3 483 | return module; 484 | #endif 485 | 486 | bad_exit: 487 | #ifdef IS_PYTHON3 488 | return NULL; 489 | #else 490 | return; 491 | #endif 492 | } 493 | 494 | -------------------------------------------------------------------------------- /yajl_hacks.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2010, R. Tyler Ballance 3 | * 4 | * Redistribution and use in source and binary forms, with or without 5 | * modification, are permitted provided that the following conditions are 6 | * met: 7 | * 8 | * 1. Redistributions of source code must retain the above copyright 9 | * notice, this list of conditions and the following disclaimer. 10 | * 11 | * 2. Redistributions in binary form must reproduce the above copyright 12 | * notice, this list of conditions and the following disclaimer in 13 | * the documentation and/or other materials provided with the 14 | * distribution. 15 | * 16 | * 3. Neither the name of R. Tyler Ballance nor the names of its 17 | * contributors may be used to endorse or promote products derived 18 | * from this software without specific prior written permission. 19 | * 20 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 21 | * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 22 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 23 | * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, 24 | * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 25 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 26 | * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 27 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 28 | * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING 29 | * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 30 | * POSSIBILITY OF SUCH DAMAGE. 31 | */ 32 | #include 33 | 34 | 35 | /* 36 | * This code was yanked largely from yajl_gen.c 37 | * it is merely a set of hacks 38 | */ 39 | 40 | typedef enum { 41 | yajl_gen_start, 42 | yajl_gen_map_start, 43 | yajl_gen_map_key, 44 | yajl_gen_map_val, 45 | yajl_gen_array_start, 46 | yajl_gen_in_array, 47 | yajl_gen_complete, 48 | yajl_gen_error 49 | } yajl_gen_state; 50 | 51 | struct yajl_gen_t 52 | { 53 | unsigned int depth; 54 | unsigned int pretty; 55 | const char * indentString; 56 | yajl_gen_state state[YAJL_MAX_DEPTH]; 57 | yajl_print_t print; 58 | void * ctx; /* yajl_buf */ 59 | /* memory allocation routines */ 60 | yajl_alloc_funcs alloc; 61 | }; 62 | 63 | #define INSERT_SEP \ 64 | if (g->state[g->depth] == yajl_gen_map_key || \ 65 | g->state[g->depth] == yajl_gen_in_array) { \ 66 | g->print(g->ctx, ",", 1); \ 67 | if (g->pretty) g->print(g->ctx, "\n", 1); \ 68 | } else if (g->state[g->depth] == yajl_gen_map_val) { \ 69 | g->print(g->ctx, ":", 1); \ 70 | if (g->pretty) g->print(g->ctx, " ", 1); \ 71 | } 72 | 73 | #define INSERT_WHITESPACE \ 74 | if (g->pretty) { \ 75 | if (g->state[g->depth] != yajl_gen_map_val) { \ 76 | unsigned int _i; \ 77 | for (_i=0;_idepth;_i++) \ 78 | g->print(g->ctx, g->indentString, \ 79 | strlen(g->indentString)); \ 80 | } \ 81 | } 82 | /* check that we're not complete, or in error state. in a valid state 83 | * to be generating */ 84 | #define ENSURE_VALID_STATE \ 85 | if (g->state[g->depth] == yajl_gen_error) { \ 86 | return yajl_gen_in_error_state;\ 87 | } else if (g->state[g->depth] == yajl_gen_complete) { \ 88 | return yajl_gen_generation_complete; \ 89 | } 90 | 91 | #define APPENDED_ATOM \ 92 | switch (g->state[g->depth]) { \ 93 | case yajl_gen_start: \ 94 | g->state[g->depth] = yajl_gen_complete; \ 95 | break; \ 96 | case yajl_gen_map_start: \ 97 | case yajl_gen_map_key: \ 98 | g->state[g->depth] = yajl_gen_map_val; \ 99 | break; \ 100 | case yajl_gen_array_start: \ 101 | g->state[g->depth] = yajl_gen_in_array; \ 102 | break; \ 103 | case yajl_gen_map_val: \ 104 | g->state[g->depth] = yajl_gen_map_key; \ 105 | break; \ 106 | default: \ 107 | break; \ 108 | } \ 109 | 110 | #define FINAL_NEWLINE \ 111 | if (g->pretty && g->state[g->depth] == yajl_gen_complete) \ 112 | g->print(g->ctx, "\n", 1); 113 | 114 | yajl_gen_status yajl_gen_raw_string(yajl_gen g, const unsigned char * str, unsigned int len) 115 | { 116 | ENSURE_VALID_STATE; INSERT_SEP; INSERT_WHITESPACE; 117 | g->print(g->ctx, "\"", 1); 118 | g->print(g->ctx, str, len); 119 | g->print(g->ctx, "\"", 1); 120 | APPENDED_ATOM; 121 | FINAL_NEWLINE; 122 | return yajl_gen_status_ok; 123 | } 124 | --------------------------------------------------------------------------------