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()
 
openSUSE Build Service is sponsored by