File py3.patch of Package python-python-cjson
From f9060f9981dfc6bc7aed6ced93f10477b0a77d60 Mon Sep 17 00:00:00 2001
From: Felipe Machado <felipou@gmail.com>
Date: Wed, 9 Aug 2017 15:48:27 -0300
Subject: [PATCH] Python 3 support - initial attempt
---
cjson.c | 268 ++++++++++++++++++++++++++--------------------------
jsontest.py | 16 ++--
2 files changed, 144 insertions(+), 140 deletions(-)
diff --git a/cjson.c b/cjson.c
index d3ecc81..a44e7e4 100644
--- a/cjson.c
+++ b/cjson.c
@@ -8,6 +8,16 @@
#include <ctype.h>
#include <math.h>
+#if PY_MAJOR_VERSION >= 3
+#define IS_PY3K
+#endif
+
+#if PY_MAJOR_VERSION >= 3
+#define STRING_TYPE PyUnicodeObject
+#else
+#define STRING_TYPE PyStringObject
+#endif
+
typedef struct JSONData {
char *str; // the actual json string
char *end; // pointer to the string end
@@ -16,7 +26,6 @@ typedef struct JSONData {
} JSONData;
static PyObject* encode_object(PyObject *object);
-static PyObject* encode_string(PyObject *object);
static PyObject* encode_unicode(PyObject *object);
static PyObject* encode_tuple(PyObject *object);
static PyObject* encode_list(PyObject *object);
@@ -68,6 +77,17 @@ typedef int Py_ssize_t;
#define skipSpaces(d) while(isspace(*((d)->ptr))) (d)->ptr++
+static inline void
+PyUnicode_ConcatAndDel(PyObject **left, PyObject *right)
+{
+ PyObject *newobj;
+ newobj = PyUnicode_Concat(*left, right);
+ Py_DECREF(*left);
+ Py_DECREF(right);
+ *left = newobj;
+}
+
+
/* ------------------------------ Decoding ----------------------------- */
static PyObject*
@@ -161,12 +181,7 @@ decode_string(JSONData *jsondata)
len = ptr - jsondata->ptr - 1;
- if (has_unicode || jsondata->all_unicode)
- object = PyUnicode_DecodeUnicodeEscape(jsondata->ptr+1, len, NULL);
- else if (string_escape)
- object = PyString_DecodeEscape(jsondata->ptr+1, len, NULL, 0, NULL);
- else
- object = PyString_FromStringAndSize(jsondata->ptr+1, len);
+ object = PyUnicode_DecodeUnicodeEscape(jsondata->ptr+1, len, NULL);
if (object == NULL) {
PyObject *type, *value, *tb, *reason;
@@ -182,7 +197,7 @@ decode_string(JSONData *jsondata)
PyErr_Format(JSON_DecodeError, "cannot decode string starting"
" at position " SSIZE_T_F ": %s",
(Py_ssize_t)(jsondata->ptr - jsondata->str),
- reason ? PyString_AsString(reason) : "bad format");
+ reason ? PyUnicode_AsUTF8(reason) : "bad format");
Py_XDECREF(reason);
} else {
PyErr_Format(JSON_DecodeError,
@@ -292,14 +307,14 @@ decode_number(JSONData *jsondata)
skipDigits(ptr);
}
- str = PyString_FromStringAndSize(jsondata->ptr, ptr - jsondata->ptr);
+ str = PyUnicode_FromStringAndSize(jsondata->ptr, ptr - jsondata->ptr);
if (str == NULL)
return NULL;
if (is_float)
- object = PyFloat_FromString(str, NULL);
+ object = PyFloat_FromString(str);
else
- object = PyInt_FromString(PyString_AS_STRING(str), NULL, 10);
+ object = PyLong_FromUnicodeObject(str, 10);
Py_DECREF(str);
@@ -579,75 +594,6 @@ decode_json(JSONData *jsondata)
/* ------------------------------ 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 PyStringObject* op = (PyStringObject*) string;
- size_t newsize = 2 + 6 * op->ob_size;
- PyObject *v;
-
- if (op->ob_size > (PY_SSIZE_T_MAX-2)/6) {
- PyErr_SetString(PyExc_OverflowError,
- "string is too large to make repr");
- return NULL;
- }
- v = PyString_FromStringAndSize((char *)NULL, newsize);
- if (v == NULL) {
- return NULL;
- }
- else {
- register Py_ssize_t i;
- register char c;
- register char *p;
- int quote;
-
- quote = '"';
-
- p = PyString_AS_STRING(v);
- *p++ = quote;
- for (i = 0; i < op->ob_size; 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) {
- /* For performance, we don't want to call
- * PyOS_snprintf here (extra layers of
- * function call). */
- sprintf(p, "\\u%04x", c & 0xff);
- p += 6;
- }
- 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;
- }
-}
-
/*
* This function is an almost verbatim copy of unicodeescape_string() from
* Python's unicodeobject.c with the following differences:
@@ -662,7 +608,7 @@ encode_unicode(PyObject *unicode)
PyObject *repr;
Py_UNICODE *s;
Py_ssize_t size;
- char *p;
+ Py_UNICODE *p;
static const char *hexdigit = "0123456789abcdef";
#ifdef Py_UNICODE_WIDE
@@ -694,11 +640,11 @@ encode_unicode(PyObject *unicode)
return NULL;
}
- repr = PyString_FromStringAndSize(NULL, 2 + expandsize*size + 1);
+ repr = PyUnicode_FromStringAndSize(NULL, 2 + expandsize*size + 1);
if (repr == NULL)
return NULL;
- p = PyString_AS_STRING(repr);
+ p = PyUnicode_AS_UNICODE(repr);
*p++ = '"';
@@ -706,7 +652,7 @@ encode_unicode(PyObject *unicode)
Py_UNICODE ch = *s++;
/* Escape quotes */
- if ((ch == (Py_UNICODE) PyString_AS_STRING(repr)[0] || ch == '\\')) {
+ if ((ch == (Py_UNICODE) PyUnicode_AS_UNICODE(repr)[0] || ch == '\\')) {
*p++ = '\\';
*p++ = (char) ch;
continue;
@@ -801,10 +747,11 @@ encode_unicode(PyObject *unicode)
*p++ = (char) ch;
}
- *p++ = PyString_AS_STRING(repr)[0];
+ *p++ = PyUnicode_AS_UNICODE(repr)[0];
*p = '\0';
- _PyString_Resize(&repr, p - PyString_AS_STRING(repr));
+ PyUnicode_Resize(&repr, p - PyUnicode_AS_UNICODE(repr));
+
return repr;
}
@@ -825,9 +772,9 @@ encode_tuple(PyObject *tuple)
PyObject *pieces, *result = NULL;
PyTupleObject *v = (PyTupleObject*) tuple;
- n = v->ob_size;
+ n = v->ob_base.ob_size;
if (n == 0)
- return PyString_FromString("[]");
+ return PyUnicode_FromString("[]");
pieces = PyTuple_New(n);
if (pieces == NULL)
@@ -843,29 +790,29 @@ encode_tuple(PyObject *tuple)
/* Add "[]" decorations to the first and last items. */
assert(n > 0);
- s = PyString_FromString("[");
+ s = PyUnicode_FromString("[");
if (s == NULL)
goto Done;
temp = PyTuple_GET_ITEM(pieces, 0);
- PyString_ConcatAndDel(&s, temp);
+ PyUnicode_ConcatAndDel(&s, temp);
PyTuple_SET_ITEM(pieces, 0, s);
if (s == NULL)
goto Done;
- s = PyString_FromString("]");
+ s = PyUnicode_FromString("]");
if (s == NULL)
goto Done;
temp = PyTuple_GET_ITEM(pieces, n-1);
- PyString_ConcatAndDel(&temp, s);
+ PyUnicode_ConcatAndDel(&temp, s);
PyTuple_SET_ITEM(pieces, n-1, temp);
if (temp == NULL)
goto Done;
/* Paste them all together with ", " between. */
- s = PyString_FromString(", ");
+ s = PyUnicode_FromString(", ");
if (s == NULL)
goto Done;
- result = _PyString_Join(s, pieces);
+ result = PyUnicode_Join(s, pieces);
Py_DECREF(s);
Done:
@@ -899,8 +846,8 @@ encode_list(PyObject *list)
return NULL;
}
- if (v->ob_size == 0) {
- result = PyString_FromString("[]");
+ if (v->ob_base.ob_size == 0) {
+ result = PyUnicode_FromString("[]");
goto Done;
}
@@ -910,7 +857,7 @@ encode_list(PyObject *list)
/* 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) {
+ for (i = 0; i < v->ob_base.ob_size; ++i) {
int status;
s = encode_object(v->ob_item[i]);
if (s == NULL)
@@ -923,29 +870,29 @@ encode_list(PyObject *list)
/* Add "[]" decorations to the first and last items. */
assert(PyList_GET_SIZE(pieces) > 0);
- s = PyString_FromString("[");
+ s = PyUnicode_FromString("[");
if (s == NULL)
goto Done;
temp = PyList_GET_ITEM(pieces, 0);
- PyString_ConcatAndDel(&s, temp);
+ PyUnicode_ConcatAndDel(&s, temp);
PyList_SET_ITEM(pieces, 0, s);
if (s == NULL)
goto Done;
- s = PyString_FromString("]");
+ s = PyUnicode_FromString("]");
if (s == NULL)
goto Done;
temp = PyList_GET_ITEM(pieces, PyList_GET_SIZE(pieces) - 1);
- PyString_ConcatAndDel(&temp, s);
+ PyUnicode_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(", ");
+ s = PyUnicode_FromString(", ");
if (s == NULL)
goto Done;
- result = _PyString_Join(s, pieces);
+ result = PyUnicode_Join(s, pieces);
Py_DECREF(s);
Done:
@@ -984,7 +931,7 @@ encode_dict(PyObject *dict)
}
if (mp->ma_used == 0) {
- result = PyString_FromString("{}");
+ result = PyUnicode_FromString("{}");
goto Done;
}
@@ -992,7 +939,7 @@ encode_dict(PyObject *dict)
if (pieces == NULL)
goto Done;
- colon = PyString_FromString(": ");
+ colon = PyUnicode_FromString(": ");
if (colon == NULL)
goto Done;
@@ -1002,7 +949,7 @@ encode_dict(PyObject *dict)
while (PyDict_Next((PyObject *)mp, &i, &key, &value)) {
int status;
- if (!PyString_Check(key) && !PyUnicode_Check(key)) {
+ if (!PyUnicode_Check(key)) {
PyErr_SetString(JSON_EncodeError, "JSON encodable dictionaries "
"must have string/unicode keys");
goto Done;
@@ -1010,9 +957,10 @@ encode_dict(PyObject *dict)
/* Prevent repr from deleting value during key format. */
Py_INCREF(value);
- s = encode_object(key);
- PyString_Concat(&s, colon);
- PyString_ConcatAndDel(&s, encode_object(value));
+ temp = encode_object(key);
+ s = PyUnicode_Concat(temp, colon);
+ Py_DECREF(temp);
+ PyUnicode_ConcatAndDel(&s, encode_object(value));
Py_DECREF(value);
if (s == NULL)
goto Done;
@@ -1024,35 +972,36 @@ encode_dict(PyObject *dict)
/* Add "{}" decorations to the first and last items. */
assert(PyList_GET_SIZE(pieces) > 0);
- s = PyString_FromString("{");
+ s = PyUnicode_FromString("{");
if (s == NULL)
goto Done;
temp = PyList_GET_ITEM(pieces, 0);
- PyString_ConcatAndDel(&s, temp);
+ PyUnicode_ConcatAndDel(&s, temp);
PyList_SET_ITEM(pieces, 0, s);
if (s == NULL)
goto Done;
- s = PyString_FromString("}");
+ s = PyUnicode_FromString("}");
if (s == NULL)
goto Done;
temp = PyList_GET_ITEM(pieces, PyList_GET_SIZE(pieces) - 1);
- PyString_ConcatAndDel(&temp, s);
+ PyUnicode_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(", ");
+ s = PyUnicode_FromString(", ");
if (s == NULL)
goto Done;
- result = _PyString_Join(s, pieces);
+ result = PyUnicode_Join(s, pieces);
Py_DECREF(s);
Done:
Py_XDECREF(pieces);
Py_XDECREF(colon);
Py_ReprLeave((PyObject *)mp);
+
return result;
}
@@ -1061,29 +1010,27 @@ static PyObject*
encode_object(PyObject *object)
{
if (object == Py_True) {
- return PyString_FromString("true");
+ return PyUnicode_FromString("true");
} else if (object == Py_False) {
- return PyString_FromString("false");
+ return PyUnicode_FromString("false");
} else if (object == Py_None) {
- return PyString_FromString("null");
- } else if (PyString_Check(object)) {
- return encode_string(object);
+ return PyUnicode_FromString("null");
} 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");
+ return PyUnicode_FromString("NaN");
} else if (Py_IS_INFINITY(val)) {
if (val > 0) {
- return PyString_FromString("Infinity");
+ return PyUnicode_FromString("Infinity");
} else {
- return PyString_FromString("-Infinity");
+ return PyUnicode_FromString("-Infinity");
}
} else {
return PyObject_Repr(object);
}
- } else if (PyInt_Check(object) || PyLong_Check(object)) {
+ } else if (PyLong_Check(object)) {
return PyObject_Str(object);
} else if (PyList_Check(object)) {
PyObject *result;
@@ -1130,6 +1077,7 @@ JSON_decode(PyObject *self, PyObject *args, PyObject *kwargs)
static char *kwlist[] = {"json", "all_unicode", NULL};
int all_unicode = False; // by default return unicode only when needed
PyObject *object, *string, *str;
+ Py_ssize_t str_size;
JSONData jsondata;
if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O|i:decode", kwlist,
@@ -1146,13 +1094,13 @@ JSON_decode(PyObject *self, PyObject *args, PyObject *kwargs)
str = string;
}
- if (PyString_AsStringAndSize(str, &(jsondata.str), NULL) == -1) {
+ if (PyBytes_AsStringAndSize(str, &(jsondata.str), &str_size) == -1) {
Py_DECREF(str);
return NULL; // not a string object or it contains null bytes
}
jsondata.ptr = jsondata.str;
- jsondata.end = jsondata.str + PyString_GET_SIZE(str);
+ jsondata.end = jsondata.str + str_size;
jsondata.all_unicode = all_unicode;
object = decode_json(&jsondata);
@@ -1198,37 +1146,93 @@ PyDoc_STRVAR(module_doc,
/* Initialization function for the module (*must* be called initcjson) */
+struct module_state {
+ PyObject *error;
+};
+
+#if PY_MAJOR_VERSION >= 3
+//#############################################################################
+//# PYTHON 3 ##################################################################
+//#############################################################################
+
+#define GETSTATE(m) ((struct module_state*)PyModule_GetState(m))
+
+static int cjson_traverse(PyObject *m, visitproc visit, void *arg) {
+ Py_VISIT(GETSTATE(m)->error);
+ return 0;
+}
+
+static int cjson_clear(PyObject *m) {
+ Py_CLEAR(GETSTATE(m)->error);
+ return 0;
+}
+
+
+static struct PyModuleDef moduledef = {
+ PyModuleDef_HEAD_INIT,
+ "cjson",
+ module_doc,
+ sizeof(struct module_state),
+ cjson_methods,
+ NULL,
+ cjson_traverse,
+ cjson_clear,
+ NULL
+};
+
+#define INITERROR return NULL
+
+PyMODINIT_FUNC
+PyInit_cjson(void)
+//#############################################################################
+#else
+//#############################################################################
+//# PYTHON 2 ##################################################################
+//#############################################################################
+
+#define GETSTATE(m) (&_state)
+static struct module_state _state;
+
+#define INITERROR return
+
PyMODINIT_FUNC
initcjson(void)
+//#############################################################################
+#endif
{
PyObject *m;
+#if PY_MAJOR_VERSION >= 3
+ m = PyModule_Create(&moduledef);
+#else
m = Py_InitModule3("cjson", cjson_methods, module_doc);
+#endif
if (m == NULL)
- return;
+ INITERROR;
JSON_Error = PyErr_NewException("cjson.Error", NULL, NULL);
if (JSON_Error == NULL)
- return;
+ INITERROR;
Py_INCREF(JSON_Error);
PyModule_AddObject(m, "Error", JSON_Error);
JSON_EncodeError = PyErr_NewException("cjson.EncodeError", JSON_Error, NULL);
if (JSON_EncodeError == NULL)
- return;
+ INITERROR;
Py_INCREF(JSON_EncodeError);
PyModule_AddObject(m, "EncodeError", JSON_EncodeError);
JSON_DecodeError = PyErr_NewException("cjson.DecodeError", JSON_Error, NULL);
if (JSON_DecodeError == NULL)
- return;
+ INITERROR;
Py_INCREF(JSON_DecodeError);
PyModule_AddObject(m, "DecodeError", JSON_DecodeError);
// Module version (the MODULE_VERSION macro is defined by setup.py)
PyModule_AddStringConstant(m, "__version__", string(MODULE_VERSION));
+#if PY_MAJOR_VERSION >= 3
+ return m;
+#endif
}
-
-
diff --git a/jsontest.py b/jsontest.py
index 0f79307..19c24fa 100644
--- a/jsontest.py
+++ b/jsontest.py
@@ -128,7 +128,7 @@ def testReadBadEscapedHexCharacter(self):
self.assertRaises(_exception, self.doReadBadEscapedHexCharacter)
def doReadBadEscapedHexCharacter(self):
- cjson.decode('"\u10K5"')
+ cjson.decode(u'"\\u10K5"')
def testReadBadObjectKey(self):
self.assertRaises(_exception, self.doReadBadObjectKey)
@@ -141,7 +141,7 @@ def testReadBadArray(self):
def doReadBadArray(self):
cjson.decode('[1,2,3,,]')
-
+
def testReadBadObjectSyntax(self):
self.assertRaises(_exception, self.doReadBadObjectSyntax)
@@ -159,7 +159,7 @@ def testReadIntegerValue(self):
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)
@@ -176,7 +176,7 @@ def doReadBadNumber(self):
def testReadSmallObject(self):
obj = cjson.decode('{ "name" : "Patrick", "age":44} ')
- self.assertEqual({ "age" : 44, "name" : "Patrick" }, obj)
+ self.assertEqual({ "age" : 44, "name" : "Patrick" }, obj)
def testReadEmptyArray(self):
obj = cjson.decode('[]')
@@ -194,7 +194,7 @@ def testWriteSmallArray(self):
def testWriteSmallObject(self):
s = cjson.encode({ "name" : "Patrick", "age": 44 })
- self.assertEqual('{"age":44,"name":"Patrick"}', _removeWhitespace(s))
+ self.assertEqual('{"name":"Patrick","age":44}', _removeWhitespace(s))
def testWriteFloat(self):
n = 3.44556677
@@ -276,7 +276,7 @@ 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]]',
+ self.assertEqual('[{"name":"Patrick","age":44,"Employed?":true,"Female?":false,"grandchildren":null},"used","abused","confused",1,2,[3,4,5]]',
_removeWhitespace(cjson.encode(obj)))
@@ -290,7 +290,7 @@ def testReadWriteCopies(self):
def testStringEncoding(self):
s = cjson.encode([1, 2, 3])
- self.assertEqual(unicode("[1,2,3]", "utf-8"), _removeWhitespace(s))
+ self.assertEqual("[1,2,3]", _removeWhitespace(s))
def testReadEmptyObjectAtEndOfArray(self):
self.assertEqual(["a","b","c",{}],
@@ -329,7 +329,7 @@ def testWriteLongUnicode(self):
u'\u1234\u1234\u1234\u1234\u1234\u1234')
self.assertEqual(r'"\U0001d11e\U0001d11e\U0001d11e\U0001d11e'
r'\u1234\u1234\u1234\u1234\u1234\u1234"', s)
-
+
def main():
unittest.main()