FazBrowse GitHub Viewer | Trending |
URL:
| Home
Tools: [Download Repo ZIP]   [Original HTTPS Page]

GitHub Viewer

// -*- coding: utf-8 -*- // :Project: python-rapidjson -- Python extension module // :Author: Ken Robbins // :License: MIT License // :Copyright: © 2015 Ken Robbins // :Copyright: © 2015-2026 Lele Gaifax // #include #include #include #include #include #include #include #include #include "rapidjson/reader.h" #include "rapidjson/schema.h" #include "rapidjson/stringbuffer.h" #include "rapidjson/writer.h" #include "rapidjson/prettywriter.h" #include "rapidjson/error/en.h" using namespace rapidjson; /* On some MacOS combo, using Py_IS_XXX() macros does not work (see https://github.com/python-rapidjson/python-rapidjson/issues/78). OTOH, MSVC < 2015 does not have std::isxxx() (see https://stackoverflow.com/questions/38441740/where-is-isnan-in-msvc-2010). Oh well... */ #if defined (_MSC_VER) && (_MSC_VER < 1900) #define IS_NAN(x) Py_IS_NAN(x) #define IS_INF(x) Py_IS_INFINITY(x) #else #define IS_NAN(x) std::isnan(x) #define IS_INF(x) std::isinf(x) #endif static PyObject* decimal_type = NULL; static PyObject* timezone_type = NULL; static PyObject* timezone_utc = NULL; static PyObject* uuid_type = NULL; static PyObject* validation_error = NULL; static PyObject* decode_error = NULL; /* These are the names of often used methods or literal values, interned in the module initialization function, to avoid repeated creation/destruction of PyUnicode values from plain C strings. We cannot use _Py_IDENTIFIER() because that upsets the GNU C++ compiler in -pedantic mode. */ static PyObject* astimezone_name = NULL; static PyObject* hex_name = NULL; static PyObject* timestamp_name = NULL; static PyObject* total_seconds_name = NULL; static PyObject* utcoffset_name = NULL; static PyObject* is_infinite_name = NULL; static PyObject* is_nan_name = NULL; static PyObject* start_object_name = NULL; static PyObject* end_object_name = NULL; static PyObject* default_name = NULL; static PyObject* end_array_name = NULL; static PyObject* string_name = NULL; static PyObject* read_name = NULL; static PyObject* write_name = NULL; static PyObject* encoding_name = NULL; static PyObject* minus_inf_string_value = NULL; static PyObject* nan_string_value = NULL; static PyObject* plus_inf_string_value = NULL; struct HandlerContext { PyObject* object; const char* key; SizeType keyLength; bool isObject; bool keyValuePairs; bool copiedKey; }; enum DatetimeMode { DM_NONE = 0, // Formats DM_ISO8601 = 1 UINT_MAX) { PyErr_SetString(PyExc_ValueError, "Invalid chunk_size, must be an integer between 4 and" " UINT_MAX"); return NULL; } chunkSize = (size_t) size; } else { PyErr_SetString(PyExc_TypeError, "chunk_size must be an unsigned integer value or None"); return NULL; } } Py_ssize_t jsonStrLen; const char* jsonStr; PyObject* asUnicode = NULL; if (PyUnicode_Check(jsonObject)) { jsonStr = PyUnicode_AsUTF8AndSize(jsonObject, &jsonStrLen); if (jsonStr == NULL) return NULL; } else if (PyBytes_Check(jsonObject) || PyByteArray_Check(jsonObject)) { asUnicode = PyUnicode_FromEncodedObject(jsonObject, "utf-8", NULL); if (asUnicode == NULL) return NULL; jsonStr = PyUnicode_AsUTF8AndSize(asUnicode, &jsonStrLen); if (jsonStr == NULL) { Py_DECREF(asUnicode); return NULL; } } else if (PyObject_HasAttr(jsonObject, read_name)) { jsonStr = NULL; jsonStrLen = 0; } else { PyErr_SetString( PyExc_TypeError, "Expected string or UTF-8 encoded bytes or bytearray or a file-like object"); return NULL; } DecoderObject* d = (DecoderObject*) self; PyObject* result = do_decode(self, jsonStr, jsonStrLen, jsonObject, chunkSize, NULL, d->numberMode, d->datetimeMode, d->uuidMode, d->parseMode); if (asUnicode != NULL) Py_DECREF(asUnicode); return result; } static PyObject* decoder_new(PyTypeObject* type, PyObject* args, PyObject* kwargs) { DecoderObject* d; PyObject* datetimeModeObj = NULL; unsigned datetimeMode = DM_NONE; PyObject* uuidModeObj = NULL; unsigned uuidMode = UM_NONE; PyObject* numberModeObj = NULL; unsigned numberMode = NM_NAN; PyObject* parseModeObj = NULL; unsigned parseMode = PM_NONE; static char const* kwlist[] = { "number_mode", "datetime_mode", "uuid_mode", "parse_mode", NULL }; if (!PyArg_ParseTupleAndKeywords(args, kwargs, "|OOOO:Decoder", (char**) kwlist, &numberModeObj, &datetimeModeObj, &uuidModeObj, &parseModeObj)) return NULL; if (numberModeObj) { if (numberModeObj == Py_None) { numberMode = NM_NONE; } else if (PyLong_Check(numberModeObj)) { int mode = PyLong_AsLong(numberModeObj); if (mode < 0 || mode >= NM_MAX) { PyErr_SetString(PyExc_ValueError, "Invalid number_mode"); return NULL; } numberMode = (unsigned) mode; if (numberMode & NM_DECIMAL && numberMode & NM_NATIVE) { PyErr_SetString(PyExc_ValueError, "Combining NM_NATIVE with NM_DECIMAL is not supported"); return NULL; } } } if (datetimeModeObj) { if (datetimeModeObj == Py_None) { datetimeMode = DM_NONE; } else if (PyLong_Check(datetimeModeObj)) { int mode = PyLong_AsLong(datetimeModeObj); if (!valid_datetime_mode(mode)) { PyErr_SetString(PyExc_ValueError, "Invalid datetime_mode"); return NULL; } datetimeMode = (unsigned) mode; if (datetimeMode && datetime_mode_format(datetimeMode) != DM_ISO8601) { PyErr_SetString(PyExc_ValueError, "Invalid datetime_mode, can deserialize only from" " ISO8601"); return NULL; } } else { PyErr_SetString(PyExc_TypeError, "datetime_mode must be a non-negative integer value or None"); return NULL; } } if (uuidModeObj) { if (uuidModeObj == Py_None) { uuidMode = UM_NONE; } else if (PyLong_Check(uuidModeObj)) { int mode = PyLong_AsLong(uuidModeObj); if (mode < 0 || mode >= UM_MAX) { PyErr_SetString(PyExc_ValueError, "Invalid uuid_mode"); return NULL; } uuidMode = (unsigned) mode; } else { PyErr_SetString(PyExc_TypeError, "uuid_mode must be an integer value or None"); return NULL; } } if (parseModeObj) { if (parseModeObj == Py_None) { parseMode = PM_NONE; } else if (PyLong_Check(parseModeObj)) { int mode = PyLong_AsLong(parseModeObj); if (mode < 0 || mode >= PM_MAX) { PyErr_SetString(PyExc_ValueError, "Invalid parse_mode"); return NULL; } parseMode = (unsigned) mode; } else { PyErr_SetString(PyExc_TypeError, "parse_mode must be an integer value or None"); return NULL; } } d = (DecoderObject*) type->tp_alloc(type, 0); if (d == NULL) return NULL; d->datetimeMode = datetimeMode; d->uuidMode = uuidMode; d->numberMode = numberMode; d->parseMode = parseMode; return (PyObject*) d; } ///////////// // Encoder // ///////////// struct DictItem { std::string key; PyObject* value; DictItem(std::string k, PyObject* v) : key(k), value(v) {} bool operator UINT_MAX) { \ PyErr_SetString(PyExc_ValueError, "Out of range string size"); \ return false; \ } } while(0) if (object == Py_None) { writer->Null(); } else if (PyBool_Check(object)) { writer->Bool(object == Py_True); } else if (numberMode & NM_DECIMAL && (is_decimal = PyObject_IsInstance(object, decimal_type))) { if (is_decimal == -1) { return false; } if (!(numberMode & NM_NAN)) { bool is_inf_or_nan; PyObject* is_inf = PyObject_CallMethodObjArgs(object, is_infinite_name, NULL); if (is_inf == NULL) { return false; } is_inf_or_nan = is_inf == Py_True; Py_DECREF(is_inf); if (!is_inf_or_nan) { PyObject* is_nan = PyObject_CallMethodObjArgs(object, is_nan_name, NULL); if (is_nan == NULL) { return false; } is_inf_or_nan = is_nan == Py_True; Py_DECREF(is_nan); } if (is_inf_or_nan) { PyErr_SetString(PyExc_ValueError, "Out of range decimal values are not JSON compliant"); return false; } } PyObject* decStrObj = PyObject_Str(object); if (decStrObj == NULL) return false; Py_ssize_t size; const char* decStr = PyUnicode_AsUTF8AndSize(decStrObj, &size); if (decStr == NULL) { Py_DECREF(decStrObj); return false; } writer->RawValue(decStr, size, kNumberType); Py_DECREF(decStrObj); } else if (PyLong_Check(object)) { if (numberMode & NM_NATIVE) { int overflow; long long i = PyLong_AsLongLongAndOverflow(object, &overflow); if (i == -1 && PyErr_Occurred()) return false; if (overflow == 0) { writer->Int64(i); } else { unsigned long long ui = PyLong_AsUnsignedLongLong(object); if (PyErr_Occurred()) return false; writer->Uint64(ui); } } else { // Mimic stdlib json: subclasses of int may override __repr__, but we still // want to encode them as integers in JSON; one example within the standard // library is IntEnum PyObject* intStrObj = PyLong_Type.tp_repr(object); if (intStrObj == NULL) return false; Py_ssize_t size; const char* intStr = PyUnicode_AsUTF8AndSize(intStrObj, &size); if (intStr == NULL) { Py_DECREF(intStrObj); return false; } writer->RawValue(intStr, size, kNumberType); Py_DECREF(intStrObj); } } else if (PyFloat_Check(object)) { double d = PyFloat_AS_DOUBLE(object); if (IS_NAN(d)) { if (numberMode & NM_NAN) { writer->RawValue("NaN", 3, kNumberType); } else { PyErr_SetString(PyExc_ValueError, "Out of range float values are not JSON compliant"); return false; } } else if (IS_INF(d)) { if (!(numberMode & NM_NAN)) { PyErr_SetString(PyExc_ValueError, "Out of range float values are not JSON compliant"); return false; } else if (d < 0) { writer->RawValue("-Infinity", 9, kNumberType); } else { writer->RawValue("Infinity", 8, kNumberType); } } else { // The RJ dtoa() produces "strange" results for particular values, see #101: // use Python's repr() to emit a raw value instead of writer->Double(d) PyObject* dr = PyFloat_Type.tp_repr(object); if (dr == NULL) return false; Py_ssize_t l; const char* rs = PyUnicode_AsUTF8AndSize(dr, &l); if (rs == NULL) { Py_DECREF(dr); return false; } writer->RawValue(rs, l, kNumberType); Py_DECREF(dr); } } else if (PyUnicode_Check(object)) { Py_ssize_t l; const char* s = PyUnicode_AsUTF8AndSize(object, &l); if (s == NULL) return false; ASSERT_VALID_SIZE(l); writer->String(s, (SizeType) l); } else if (bytesMode == BM_UTF8 && (PyBytes_Check(object) || PyByteArray_Check(object))) { PyObject* unicodeObj = PyUnicode_FromEncodedObject(object, "utf-8", NULL); if (unicodeObj == NULL) return false; Py_ssize_t l; const char* s = PyUnicode_AsUTF8AndSize(unicodeObj, &l); if (s == NULL) { Py_DECREF(unicodeObj); return false; } ASSERT_VALID_SIZE(l); writer->String(s, (SizeType) l); Py_DECREF(unicodeObj); } else if (PyList_CheckExact(object) || (!(iterableMode & IM_ONLY_LISTS) && PyList_Check(object))) { writer->StartArray(); Py_ssize_t size = PyList_GET_SIZE(object); for (Py_ssize_t i = 0; i < size; i++) { if (Py_EnterRecursiveCall(" while JSONifying list object")) return false; PyObject* item = PyList_GET_ITEM(object, i); bool r = RECURSE(item); Py_LeaveRecursiveCall(); if (!r) return false; } writer->EndArray(); } else if (!(iterableMode & IM_ONLY_LISTS) && PyTuple_Check(object)) { writer->StartArray(); Py_ssize_t size = PyTuple_GET_SIZE(object); for (Py_ssize_t i = 0; i < size; i++) { if (Py_EnterRecursiveCall(" while JSONifying tuple object")) return false; PyObject* item = PyTuple_GET_ITEM(object, i); bool r = RECURSE(item); Py_LeaveRecursiveCall(); if (!r) return false; } writer->EndArray(); } else if ((PyDict_CheckExact(object) || (!(mappingMode & MM_ONLY_DICTS) && PyDict_Check(object))) && ((mappingMode & MM_SKIP_NON_STRING_KEYS) || (mappingMode & MM_COERCE_KEYS_TO_STRINGS) || all_keys_are_string(object))) { writer->StartObject(); Py_ssize_t pos = 0; PyObject* key; PyObject* item; PyObject* coercedKey = NULL; if (!(mappingMode & MM_SORT_KEYS)) { while (PyDict_Next(object, &pos, &key, &item)) { if (mappingMode & MM_COERCE_KEYS_TO_STRINGS) { if (!PyUnicode_Check(key)) { coercedKey = PyObject_Str(key); if (coercedKey == NULL) return false; key = coercedKey; } } if (coercedKey || PyUnicode_Check(key)) { Py_ssize_t l; const char* key_str = PyUnicode_AsUTF8AndSize(key, &l); if (key_str == NULL) { Py_XDECREF(coercedKey); return false; } ASSERT_VALID_SIZE(l); writer->Key(key_str, (SizeType) l); if (Py_EnterRecursiveCall(" while JSONifying dict object")) { Py_XDECREF(coercedKey); return false; } bool r = RECURSE(item); Py_LeaveRecursiveCall(); if (!r) { Py_XDECREF(coercedKey); return false; } } else if (!(mappingMode & MM_SKIP_NON_STRING_KEYS)) { PyErr_SetString(PyExc_TypeError, "keys must be strings"); // No need to dispose coercedKey here, because it can be set *only* // when mapping_mode is MM_COERCE_KEYS_TO_STRINGS assert(!coercedKey); return false; } Py_CLEAR(coercedKey); } } else { std::vector items; while (PyDict_Next(object, &pos, &key, &item)) { if (mappingMode & MM_COERCE_KEYS_TO_STRINGS) { if (!PyUnicode_Check(key)) { coercedKey = PyObject_Str(key); if (coercedKey == NULL) return false; key = coercedKey; } } if (coercedKey || PyUnicode_Check(key)) { Py_ssize_t l; const char* key_str = PyUnicode_AsUTF8AndSize(key, &l); if (key_str == NULL) { Py_XDECREF(coercedKey); return false; } ASSERT_VALID_SIZE(l); items.push_back(DictItem(std::string(key_str, l), item)); } else if (!(mappingMode & MM_SKIP_NON_STRING_KEYS)) { PyErr_SetString(PyExc_TypeError, "keys must be strings"); assert(!coercedKey); return false; } Py_CLEAR(coercedKey); } std::sort(items.begin(), items.end()); for (size_t i=0, s=items.size(); i < s; i++) { writer->Key(items[i].key.c_str(), (SizeType) items[i].key.length()); if (Py_EnterRecursiveCall(" while JSONifying dict object")) return false; bool r = RECURSE(items[i].value); Py_LeaveRecursiveCall(); if (!r) return false; } } writer->EndObject(); } else if (datetimeMode != DM_NONE && (PyTime_Check(object) || PyDateTime_Check(object))) { unsigned year, month, day, hour, min, sec, microsec; PyObject* dtObject = object; PyObject* asUTC = NULL; const int ISOFORMAT_LEN = 42; char isoformat[ISOFORMAT_LEN]; memset(isoformat, 0, ISOFORMAT_LEN); // The timezone is always shorter than this, but gcc12 emits a warning about // sprintf() that *may* produce longer results, because we pass int values when // concretely they are constrained to 24*3600 seconds: pacify gcc using a bigger // buffer const int TIMEZONE_LEN = 24; char timeZone[TIMEZONE_LEN] = { 0 }; if (!(datetimeMode & DM_IGNORE_TZ) && PyObject_HasAttr(object, utcoffset_name)) { PyObject* utcOffset = PyObject_CallMethodObjArgs(object, utcoffset_name, NULL); if (utcOffset == NULL) return false; if (utcOffset == Py_None) { // Naive value: maybe assume it's in UTC instead of local time if (datetimeMode & DM_NAIVE_IS_UTC) { if (PyDateTime_Check(object)) { hour = PyDateTime_DATE_GET_HOUR(dtObject); min = PyDateTime_DATE_GET_MINUTE(dtObject); sec = PyDateTime_DATE_GET_SECOND(dtObject); microsec = PyDateTime_DATE_GET_MICROSECOND(dtObject); year = PyDateTime_GET_YEAR(dtObject); month = PyDateTime_GET_MONTH(dtObject); day = PyDateTime_GET_DAY(dtObject); asUTC = PyDateTimeAPI->DateTime_FromDateAndTime( year, month, day, hour, min, sec, microsec, timezone_utc, PyDateTimeAPI->DateTimeType); } else { hour = PyDateTime_TIME_GET_HOUR(dtObject); min = PyDateTime_TIME_GET_MINUTE(dtObject); sec = PyDateTime_TIME_GET_SECOND(dtObject); microsec = PyDateTime_TIME_GET_MICROSECOND(dtObject); asUTC = PyDateTimeAPI->Time_FromTime( hour, min, sec, microsec, timezone_utc, PyDateTimeAPI->TimeType); } if (asUTC == NULL) { Py_DECREF(utcOffset); return false; } dtObject = asUTC; if (datetime_mode_format(datetimeMode) == DM_ISO8601) strcpy(timeZone, "+00:00"); } } else { // Timezone-aware value if (datetimeMode & DM_SHIFT_TO_UTC) { // If it's not already in UTC, shift the value if (PyObject_IsTrue(utcOffset)) { asUTC = PyObject_CallMethodObjArgs(object, astimezone_name, timezone_utc, NULL); if (asUTC == NULL) { Py_DECREF(utcOffset); return false; } dtObject = asUTC; } if (datetime_mode_format(datetimeMode) == DM_ISO8601) strcpy(timeZone, "+00:00"); } else if (datetime_mode_format(datetimeMode) == DM_ISO8601) { int seconds_from_utc = 0; if (PyObject_IsTrue(utcOffset)) { PyObject* tsObj = PyObject_CallMethodObjArgs(utcOffset, total_seconds_name, NULL); if (tsObj == NULL) { Py_DECREF(utcOffset); return false; } seconds_from_utc = (int) PyFloat_AsDouble(tsObj); Py_DECREF(tsObj); } char sign = '+'; if (seconds_from_utc < 0) { sign = '-'; seconds_from_utc = -seconds_from_utc; } unsigned tz_hour = seconds_from_utc / 3600; unsigned tz_min = (seconds_from_utc % 3600) / 60; snprintf(timeZone, TIMEZONE_LEN-1, "%c%02u:%02u", sign, tz_hour, tz_min); } } Py_DECREF(utcOffset); } if (datetime_mode_format(datetimeMode) == DM_ISO8601) { int size; if (PyDateTime_Check(dtObject)) { year = PyDateTime_GET_YEAR(dtObject); month = PyDateTime_GET_MONTH(dtObject); day = PyDateTime_GET_DAY(dtObject); hour = PyDateTime_DATE_GET_HOUR(dtObject); min = PyDateTime_DATE_GET_MINUTE(dtObject); sec = PyDateTime_DATE_GET_SECOND(dtObject); microsec = PyDateTime_DATE_GET_MICROSECOND(dtObject); if (microsec > 0) { size = snprintf(isoformat, ISOFORMAT_LEN-1, "\"%04u-%02u-%02uT%02u:%02u:%02u.%06u%s\"", year, month, day, hour, min, sec, microsec, timeZone); } else { size = snprintf(isoformat, ISOFORMAT_LEN-1, "\"%04u-%02u-%02uT%02u:%02u:%02u%s\"", year, month, day, hour, min, sec, timeZone); } } else { hour = PyDateTime_TIME_GET_HOUR(dtObject); min = PyDateTime_TIME_GET_MINUTE(dtObject); sec = PyDateTime_TIME_GET_SECOND(dtObject); microsec = PyDateTime_TIME_GET_MICROSECOND(dtObject); if (microsec > 0) { size = snprintf(isoformat, ISOFORMAT_LEN-1, "\"%02u:%02u:%02u.%06u%s\"", hour, min, sec, microsec, timeZone); } else { size = snprintf(isoformat, ISOFORMAT_LEN-1, "\"%02u:%02u:%02u%s\"", hour, min, sec, timeZone); } } writer->RawValue(isoformat, size, kStringType); } else /* if (datetimeMode & DM_UNIX_TIME) */ { if (PyDateTime_Check(dtObject)) { PyObject* timestampObj = PyObject_CallMethodObjArgs(dtObject, timestamp_name, NULL); if (timestampObj == NULL) { Py_XDECREF(asUTC); return false; } double timestamp = PyFloat_AsDouble(timestampObj); Py_DECREF(timestampObj); if (datetimeMode & DM_ONLY_SECONDS) { writer->Int64((int64_t) timestamp); } else { // Writer.SetMaxDecimalPlaces(6) truncates the value, // so for example 1514893636.276703 would come out as // 1514893636.276702, because its exact double value is // 1514893636.2767028808593750000000000... char tsStr[12 + 1 + 6 + 1]; // Temporarily switch to a POSIX locale, in case the outer world is // configured differently: by chance I got one doctest failure, and I // can only imagine that recent Sphinx (that is, 4.2+) initializes the // locale to something that implies a decimal separator different from // a dot ".", say a comma "," when LANG is "it_IT"... not the best // thing to do when emitting JSON! const char* locale = setlocale(LC_NUMERIC, NULL); setlocale(LC_NUMERIC, "C"); int size = snprintf(tsStr, 12 + 1 + 6, "%.6f", timestamp); setlocale(LC_NUMERIC, locale); // Remove trailing 0s while (tsStr[size-2] != '.' && tsStr[size-1] == '0') size--; writer->RawValue(tsStr, size, kNumberType); } } else { hour = PyDateTime_TIME_GET_HOUR(dtObject); min = PyDateTime_TIME_GET_MINUTE(dtObject); sec = PyDateTime_TIME_GET_SECOND(dtObject); microsec = PyDateTime_TIME_GET_MICROSECOND(dtObject); long timestamp = hour * 3600 + min * 60 + sec; if (datetimeMode & DM_ONLY_SECONDS) writer->Int64(timestamp); else writer->Double(timestamp + (microsec / 1000000.0)); } } Py_XDECREF(asUTC); } else if (datetimeMode != DM_NONE && PyDate_Check(object)) { unsigned year = PyDateTime_GET_YEAR(object); unsigned month = PyDateTime_GET_MONTH(object); unsigned day = PyDateTime_GET_DAY(object); if (datetime_mode_format(datetimeMode) == DM_ISO8601) { const int ISOFORMAT_LEN = 18; char isoformat[ISOFORMAT_LEN]; int size; memset(isoformat, 0, ISOFORMAT_LEN); size = snprintf(isoformat, ISOFORMAT_LEN-1, "\"%04u-%02u-%02u\"", year, month, day); writer->RawValue(isoformat, size, kStringType); } else /* datetime_mode_format(datetimeMode) == DM_UNIX_TIME */ { // A date object, take its midnight timestamp PyObject* midnightObj; PyObject* timestampObj; if (datetimeMode & (DM_SHIFT_TO_UTC | DM_NAIVE_IS_UTC)) midnightObj = PyDateTimeAPI->DateTime_FromDateAndTime( year, month, day, 0, 0, 0, 0, timezone_utc, PyDateTimeAPI->DateTimeType); else midnightObj = PyDateTime_FromDateAndTime(year, month, day, 0, 0, 0, 0); if (midnightObj == NULL) { return false; } timestampObj = PyObject_CallMethodObjArgs(midnightObj, timestamp_name, NULL); Py_DECREF(midnightObj); if (timestampObj == NULL) { return false; } double timestamp = PyFloat_AsDouble(timestampObj); Py_DECREF(timestampObj); if (datetimeMode & DM_ONLY_SECONDS) { writer->Int64((int64_t) timestamp); } else { // Writer.SetMaxDecimalPlaces(6) truncates the value, // so for example 1514893636.276703 would come out as // 1514893636.276702, because its exact double value is // 1514893636.2767028808593750000000000... char tsStr[12 + 1 + 6 + 1]; // Temporarily switch to a POSIX locale, in case the outer // world is configured differently, see above const char* locale = setlocale(LC_NUMERIC, NULL); setlocale(LC_NUMERIC, "C"); setlocale(LC_NUMERIC, locale); int size = snprintf(tsStr, 12 + 1 + 6, "%.6f", timestamp); // Remove trailing 0s while (tsStr[size-2] != '.' && tsStr[size-1] == '0') size--; writer->RawValue(tsStr, size, kNumberType); } } } else if (uuidMode != UM_NONE && PyObject_TypeCheck(object, (PyTypeObject*) uuid_type)) { PyObject* hexval; if (uuidMode == UM_CANONICAL) hexval = PyObject_Str(object); else hexval = PyObject_GetAttr(object, hex_name); if (hexval == NULL) return false; Py_ssize_t size; const char* s = PyUnicode_AsUTF8AndSize(hexval, &size); if (s == NULL) { Py_DECREF(hexval); return false; } if (RAPIDJSON_UNLIKELY(size != 32 && size != 36)) { PyErr_Format(PyExc_ValueError, "Bad UUID hex, expected a string of either 32 or 36 chars," " got %.200R", hexval); Py_DECREF(hexval); return false; } char quoted[39]; quoted[0] = quoted[size + 1] = '"'; memcpy(quoted + 1, s, size); writer->RawValue(quoted, (SizeType) size + 2, kStringType); Py_DECREF(hexval); } else if (!(iterableMode & IM_ONLY_LISTS) && PyIter_Check(object)) { PyObject* iterator = PyObject_GetIter(object); if (iterator == NULL) return false; writer->StartArray(); PyObject* item; while ((item = PyIter_Next(iterator))) { if (Py_EnterRecursiveCall(" while JSONifying iterable object")) { Py_DECREF(item); Py_DECREF(iterator); return false; } bool r = RECURSE(item); Py_LeaveRecursiveCall(); Py_DECREF(item); if (!r) { Py_DECREF(iterator); return false; } } Py_DECREF(iterator); // PyIter_Next() may exit with an error if (PyErr_Occurred()) return false; writer->EndArray(); } else if (PyObject_TypeCheck(object, &RawJSON_Type)) { const char* jsonStr; Py_ssize_t l; jsonStr = PyUnicode_AsUTF8AndSize(((RawJSON*) object)->value, &l); if (jsonStr == NULL) return false; ASSERT_VALID_SIZE(l); writer->RawValue(jsonStr, (SizeType) l, kStringType); } else if (defaultFn) { PyObject* retval = PyObject_CallFunctionObjArgs(defaultFn, object, NULL); if (retval == NULL) return false; if (Py_EnterRecursiveCall(" while JSONifying default function result")) { Py_DECREF(retval); return false; } bool r = RECURSE(retval); Py_LeaveRecursiveCall(); Py_DECREF(retval); if (!r) return false; } else { PyErr_Format(PyExc_TypeError, "%R is not JSON serializable", object); return false; } // Catch possible error raised in associated stream operations return PyErr_Occurred() ? false : true; #undef RECURSE #undef ASSERT_VALID_SIZE } typedef struct { PyObject_HEAD bool ensureAscii; unsigned writeMode; char indentChar; unsigned indentCount; unsigned datetimeMode; unsigned uuidMode; unsigned numberMode; unsigned bytesMode; unsigned iterableMode; unsigned mappingMode; } EncoderObject; PyDoc_STRVAR(dumps_docstring, "dumps(obj, *, skipkeys=False, ensure_ascii=True, write_mode=WM_COMPACT," " indent=4, default=None, sort_keys=False, number_mode=None," " datetime_mode=None, uuid_mode=None, bytes_mode=BM_UTF8," " iterable_mode=IM_ANY_ITERABLE, mapping_mode=MM_ANY_MAPPING," " allow_nan=True)\n" "\n" "Encode a Python object into a JSON string."); static PyObject* dumps(PyObject* self, PyObject* args, PyObject* kwargs) { /* Converts a Python object to a JSON-encoded string. */ PyObject* value; int ensureAscii = true; PyObject* indent = NULL; PyObject* defaultFn = NULL; PyObject* numberModeObj = NULL; unsigned numberMode = NM_NAN; PyObject* datetimeModeObj = NULL; unsigned datetimeMode = DM_NONE; PyObject* uuidModeObj = NULL; unsigned uuidMode = UM_NONE; PyObject* bytesModeObj = NULL; unsigned bytesMode = BM_UTF8; PyObject* writeModeObj = NULL; unsigned writeMode = WM_COMPACT; PyObject* iterableModeObj = NULL; unsigned iterableMode = IM_ANY_ITERABLE; PyObject* mappingModeObj = NULL; unsigned mappingMode = MM_ANY_MAPPING; char indentChar = ' '; unsigned indentCount = 4; static char const* kwlist[] = { "obj", "skipkeys", // alias of MM_SKIP_NON_STRING_KEYS "ensure_ascii", "indent", "default", "sort_keys", // alias of MM_SORT_KEYS "number_mode", "datetime_mode", "uuid_mode", "bytes_mode", "write_mode", "iterable_mode", "mapping_mode", /* compatibility with stdlib json */ "allow_nan", NULL }; int skipKeys = false; int sortKeys = false; int allowNan = -1; if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O|$ppOOpOOOOOOOp:rapidjson.dumps", (char**) kwlist, &value, &skipKeys, &ensureAscii, &indent, &defaultFn, &sortKeys, &numberModeObj, &datetimeModeObj, &uuidModeObj, &bytesModeObj, &writeModeObj, &iterableModeObj, &mappingModeObj, &allowNan)) return NULL; if (defaultFn && !PyCallable_Check(defaultFn)) { if (defaultFn == Py_None) { defaultFn = NULL; } else { PyErr_SetString(PyExc_TypeError, "default must be a callable"); return NULL; } } if (!accept_indent_arg(indent, writeMode, indentCount, indentChar)) return NULL; if (!accept_write_mode_arg(writeModeObj, writeMode)) return NULL; if (!accept_number_mode_arg(numberModeObj, allowNan, numberMode)) return NULL; if (!accept_datetime_mode_arg(datetimeModeObj, datetimeMode)) return NULL; if (!accept_uuid_mode_arg(uuidModeObj, uuidMode)) return NULL; if (!accept_bytes_mode_arg(bytesModeObj, bytesMode)) return NULL; if (!accept_iterable_mode_arg(iterableModeObj, iterableMode)) return NULL; if (!accept_mapping_mode_arg(mappingModeObj, mappingMode)) return NULL; if (skipKeys) mappingMode |= MM_SKIP_NON_STRING_KEYS; if (sortKeys) mappingMode |= MM_SORT_KEYS; return do_encode(value, defaultFn, ensureAscii ? true : false, writeMode, indentChar, indentCount, numberMode, datetimeMode, uuidMode, bytesMode, iterableMode, mappingMode); } PyDoc_STRVAR(dump_docstring, "dump(obj, stream, *, skipkeys=False, ensure_ascii=True," " write_mode=WM_COMPACT, indent=4, default=None, sort_keys=False," " number_mode=None, datetime_mode=None, uuid_mode=None, bytes_mode=BM_UTF8," " iterable_mode=IM_ANY_ITERABLE, mapping_mode=MM_ANY_MAPPING," " chunk_size=65536, allow_nan=True)\n" "\n" "Encode a Python object into a JSON stream."); static PyObject* dump(PyObject* self, PyObject* args, PyObject* kwargs) { /* Converts a Python object to a JSON-encoded stream. */ PyObject* value; PyObject* stream; int ensureAscii = true; PyObject* indent = NULL; PyObject* defaultFn = NULL; PyObject* numberModeObj = NULL; unsigned numberMode = NM_NAN; PyObject* datetimeModeObj = NULL; unsigned datetimeMode = DM_NONE; PyObject* uuidModeObj = NULL; unsigned uuidMode = UM_NONE; PyObject* bytesModeObj = NULL; unsigned bytesMode = BM_UTF8; PyObject* writeModeObj = NULL; unsigned writeMode = WM_COMPACT; PyObject* iterableModeObj = NULL; unsigned iterableMode = IM_ANY_ITERABLE; PyObject* mappingModeObj = NULL; unsigned mappingMode = MM_ANY_MAPPING; char indentChar = ' '; unsigned indentCount = 4; PyObject* chunkSizeObj = NULL; size_t chunkSize = 65536; int allowNan = -1; static char const* kwlist[] = { "obj", "stream", "skipkeys", // alias of MM_SKIP_NON_STRING_KEYS "ensure_ascii", "indent", "default", "sort_keys", // alias of MM_SORT_KEYS "number_mode", "datetime_mode", "uuid_mode", "bytes_mode", "chunk_size", "write_mode", "iterable_mode", "mapping_mode", /* compatibility with stdlib json */ "allow_nan", NULL }; int skipKeys = false; int sortKeys = false; if (!PyArg_ParseTupleAndKeywords(args, kwargs, "OO|$ppOOpOOOOOOOOp:rapidjson.dump", (char**) kwlist, &value, &stream, &skipKeys, &ensureAscii, &indent, &defaultFn, &sortKeys, &numberModeObj, &datetimeModeObj, &uuidModeObj, &bytesModeObj, &chunkSizeObj, &writeModeObj, &iterableModeObj, &mappingModeObj, &allowNan)) return NULL; if (defaultFn && !PyCallable_Check(defaultFn)) { if (defaultFn == Py_None) { defaultFn = NULL; } else { PyErr_SetString(PyExc_TypeError, "default must be a callable"); return NULL; } } if (!accept_indent_arg(indent, writeMode, indentCount, indentChar)) return NULL; if (!accept_write_mode_arg(writeModeObj, writeMode)) return NULL; if (!accept_number_mode_arg(numberModeObj, allowNan, numberMode)) return NULL; if (!accept_datetime_mode_arg(datetimeModeObj, datetimeMode)) return NULL; if (!accept_uuid_mode_arg(uuidModeObj, uuidMode)) return NULL; if (!accept_bytes_mode_arg(bytesModeObj, bytesMode)) return NULL; if (!accept_chunk_size_arg(chunkSizeObj, chunkSize)) return NULL; if (!accept_iterable_mode_arg(iterableModeObj, iterableMode)) return NULL; if (!accept_mapping_mode_arg(mappingModeObj, mappingMode)) return NULL; if (skipKeys) mappingMode |= MM_SKIP_NON_STRING_KEYS; if (sortKeys) mappingMode |= MM_SORT_KEYS; return do_stream_encode(value, stream, chunkSize, defaultFn, ensureAscii ? true : false, writeMode, indentChar, indentCount, numberMode, datetimeMode, uuidMode, bytesMode, iterableMode, mappingMode); } PyDoc_STRVAR(encoder_doc, "Encoder(skip_invalid_keys=False, ensure_ascii=True, write_mode=WM_COMPACT," " indent=4, sort_keys=False, number_mode=None, datetime_mode=None," " uuid_mode=None, bytes_mode=None, iterable_mode=IM_ANY_ITERABLE," " mapping_mode=MM_ANY_MAPPING)\n\n" "Create and return a new Encoder instance."); static PyMemberDef encoder_members[] = { {"ensure_ascii", T_BOOL, offsetof(EncoderObject, ensureAscii), READONLY, "whether the output should contain only ASCII characters."}, {"indent_char", T_CHAR, offsetof(EncoderObject, indentChar), READONLY, "What will be used as end-of-line character."}, {"indent_count", T_UINT, offsetof(EncoderObject, indentCount), READONLY, "The indentation width."}, {"datetime_mode", T_UINT, offsetof(EncoderObject, datetimeMode), READONLY, "Whether and how datetime values should be encoded."}, {"uuid_mode", T_UINT, offsetof(EncoderObject, uuidMode), READONLY, "Whether and how UUID values should be encoded"}, {"number_mode", T_UINT, offsetof(EncoderObject, numberMode), READONLY, "The encoding behavior with regards to numeric values."}, {"bytes_mode", T_UINT, offsetof(EncoderObject, bytesMode), READONLY, "How bytes values should be treated."}, {"write_mode", T_UINT, offsetof(EncoderObject, writeMode), READONLY, "Whether the output should be pretty printed or not."}, {"iterable_mode", T_UINT, offsetof(EncoderObject, iterableMode), READONLY, "Whether iterable values other than lists shall be encoded as JSON arrays or not."}, {"mapping_mode", T_UINT, offsetof(EncoderObject, mappingMode), READONLY, "Whether mapping values other than dicts shall be encoded as JSON objects or not."}, {NULL} }; static PyObject* encoder_get_skip_invalid_keys(EncoderObject* e, void* closure) { return PyBool_FromLong(e->mappingMode & MM_SKIP_NON_STRING_KEYS); } static PyObject* encoder_get_sort_keys(EncoderObject* e, void* closure) { return PyBool_FromLong(e->mappingMode & MM_SORT_KEYS); } // Backward compatibility, previously they were members of EncoderObject static PyGetSetDef encoder_props[] = { {"skip_invalid_keys", (getter) encoder_get_skip_invalid_keys, NULL, "Whether invalid keys shall be skipped."}, {"sort_keys", (getter) encoder_get_sort_keys, NULL, "Whether dictionary keys shall be sorted alphabetically."}, {NULL} }; static PyTypeObject Encoder_Type = { PyVarObject_HEAD_INIT(NULL, 0) "rapidjson.Encoder", /* tp_name */ sizeof(EncoderObject), /* tp_basicsize */ 0, /* tp_itemsize */ 0, /* tp_dealloc */ 0, /* tp_print */ 0, /* tp_getattr */ 0, /* tp_setattr */ 0, /* tp_compare */ 0, /* tp_repr */ 0, /* tp_as_number */ 0, /* tp_as_sequence */ 0, /* tp_as_mapping */ 0, /* tp_hash */ (ternaryfunc) encoder_call, /* tp_call */ 0, /* tp_str */ 0, /* tp_getattro */ 0, /* tp_setattro */ 0, /* tp_as_buffer */ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */ encoder_doc, /* tp_doc */ 0, /* tp_traverse */ 0, /* tp_clear */ 0, /* tp_richcompare */ 0, /* tp_weaklistoffset */ 0, /* tp_iter */ 0, /* tp_iternext */ 0, /* tp_methods */ encoder_members, /* tp_members */ encoder_props, /* tp_getset */ 0, /* tp_base */ 0, /* tp_dict */ 0, /* tp_descr_get */ 0, /* tp_descr_set */ 0, /* tp_dictoffset */ 0, /* tp_init */ 0, /* tp_alloc */ encoder_new, /* tp_new */ PyObject_Del, /* tp_free */ }; #define Encoder_CheckExact(v) (Py_TYPE(v) == &Encoder_Type) #define Encoder_Check(v) PyObject_TypeCheck(v, &Encoder_Type) #define DUMPS_INTERNAL_CALL \ (dumps_internal(&writer, \ value, \ defaultFn, \ numberMode, \ datetimeMode, \ uuidMode, \ bytesMode, \ iterableMode, \ mappingMode) \ ? PyUnicode_FromString(buf.GetString()) : NULL) static PyObject* do_encode(PyObject* value, PyObject* defaultFn, bool ensureAscii, unsigned writeMode, char indentChar, unsigned indentCount, unsigned numberMode, unsigned datetimeMode, unsigned uuidMode, unsigned bytesMode, unsigned iterableMode, unsigned mappingMode) { if (writeMode == WM_COMPACT) { if (ensureAscii) { GenericStringBuffer buf; Writer writer(buf); return DUMPS_INTERNAL_CALL; } else { StringBuffer buf; Writer writer(buf); return DUMPS_INTERNAL_CALL; } } else if (ensureAscii) { GenericStringBuffer buf; PrettyWriter writer(buf); writer.SetIndent(indentChar, indentCount); if (writeMode & WM_SINGLE_LINE_ARRAY) { writer.SetFormatOptions(kFormatSingleLineArray); } return DUMPS_INTERNAL_CALL; } else { StringBuffer buf; PrettyWriter writer(buf); writer.SetIndent(indentChar, indentCount); if (writeMode & WM_SINGLE_LINE_ARRAY) { writer.SetFormatOptions(kFormatSingleLineArray); } return DUMPS_INTERNAL_CALL; } } #define DUMP_INTERNAL_CALL \ (dumps_internal(&writer, \ value, \ defaultFn, \ numberMode, \ datetimeMode, \ uuidMode, \ bytesMode, \ iterableMode, \ mappingMode) \ ? Py_INCREF(Py_None), Py_None : NULL) static PyObject* do_stream_encode(PyObject* value, PyObject* stream, size_t chunkSize, PyObject* defaultFn, bool ensureAscii, unsigned writeMode, char indentChar, unsigned indentCount, unsigned numberMode, unsigned datetimeMode, unsigned uuidMode, unsigned bytesMode, unsigned iterableMode, unsigned mappingMode) { PyWriteStreamWrapper os(stream, chunkSize); if (writeMode == WM_COMPACT) { if (ensureAscii) { Writer writer(os); return DUMP_INTERNAL_CALL; } else { Writer writer(os); return DUMP_INTERNAL_CALL; } } else if (ensureAscii) { PrettyWriter writer(os); writer.SetIndent(indentChar, indentCount); if (writeMode & WM_SINGLE_LINE_ARRAY) { writer.SetFormatOptions(kFormatSingleLineArray); } return DUMP_INTERNAL_CALL; } else { PrettyWriter writer(os); writer.SetIndent(indentChar, indentCount); if (writeMode & WM_SINGLE_LINE_ARRAY) { writer.SetFormatOptions(kFormatSingleLineArray); } return DUMP_INTERNAL_CALL; } } static PyObject* encoder_call(PyObject* self, PyObject* args, PyObject* kwargs) { static char const* kwlist[] = { "obj", "stream", "chunk_size", NULL }; PyObject* value; PyObject* stream = NULL; PyObject* chunkSizeObj = NULL; size_t chunkSize = 65536; PyObject* defaultFn = NULL; PyObject* result; if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O|O$O", (char**) kwlist, &value, &stream, &chunkSizeObj)) return NULL; EncoderObject* e = (EncoderObject*) self; if (stream != NULL && stream != Py_None) { if (!PyObject_HasAttr(stream, write_name)) { PyErr_SetString(PyExc_TypeError, "Expected a writable stream"); return NULL; } if (!accept_chunk_size_arg(chunkSizeObj, chunkSize)) return NULL; if (PyObject_HasAttr(self, default_name)) { defaultFn = PyObject_GetAttr(self, default_name); } result = do_stream_encode(value, stream, chunkSize, defaultFn, e->ensureAscii, e->writeMode, e->indentChar, e->indentCount, e->numberMode, e->datetimeMode, e->uuidMode, e->bytesMode, e->iterableMode, e->mappingMode); } else { if (PyObject_HasAttr(self, default_name)) { defaultFn = PyObject_GetAttr(self, default_name); } result = do_encode(value, defaultFn, e->ensureAscii, e->writeMode, e->indentChar, e->indentCount, e->numberMode, e->datetimeMode, e->uuidMode, e->bytesMode, e->iterableMode, e->mappingMode); } if (defaultFn != NULL) Py_DECREF(defaultFn); return result; } static PyObject* encoder_new(PyTypeObject* type, PyObject* args, PyObject* kwargs) { EncoderObject* e; int ensureAscii = true; PyObject* indent = NULL; PyObject* numberModeObj = NULL; unsigned numberMode = NM_NAN; PyObject* datetimeModeObj = NULL; unsigned datetimeMode = DM_NONE; PyObject* uuidModeObj = NULL; unsigned uuidMode = UM_NONE; PyObject* bytesModeObj = NULL; unsigned bytesMode = BM_UTF8; PyObject* writeModeObj = NULL; unsigned writeMode = WM_COMPACT; PyObject* iterableModeObj = NULL; unsigned iterableMode = IM_ANY_ITERABLE; PyObject* mappingModeObj = NULL; unsigned mappingMode = MM_ANY_MAPPING; char indentChar = ' '; unsigned indentCount = 4; static char const* kwlist[] = { "skip_invalid_keys", // alias of MM_SKIP_NON_STRING_KEYS "ensure_ascii", "indent", "sort_keys", // alias of MM_SORT_KEYS "number_mode", "datetime_mode", "uuid_mode", "bytes_mode", "write_mode", "iterable_mode", "mapping_mode", NULL }; int skipInvalidKeys = false; int sortKeys = false; if (!PyArg_ParseTupleAndKeywords(args, kwargs, "|ppOpOOOOOOO:Encoder", (char**) kwlist, &skipInvalidKeys, &ensureAscii, &indent, &sortKeys, &numberModeObj, &datetimeModeObj, &uuidModeObj, &bytesModeObj, &writeModeObj, &iterableModeObj, &mappingModeObj)) return NULL; if (!accept_indent_arg(indent, writeMode, indentCount, indentChar)) return NULL; if (!accept_write_mode_arg(writeModeObj, writeMode)) return NULL; if (!accept_number_mode_arg(numberModeObj, -1, numberMode)) return NULL; if (!accept_datetime_mode_arg(datetimeModeObj, datetimeMode)) return NULL; if (!accept_uuid_mode_arg(uuidModeObj, uuidMode)) return NULL; if (!accept_bytes_mode_arg(bytesModeObj, bytesMode)) return NULL; if (!accept_iterable_mode_arg(iterableModeObj, iterableMode)) return NULL; if (!accept_mapping_mode_arg(mappingModeObj, mappingMode)) return NULL; if (skipInvalidKeys) mappingMode |= MM_SKIP_NON_STRING_KEYS; if (sortKeys) mappingMode |= MM_SORT_KEYS; e = (EncoderObject*) type->tp_alloc(type, 0); if (e == NULL) return NULL; e->ensureAscii = ensureAscii ? true : false; e->writeMode = writeMode; e->indentChar = indentChar; e->indentCount = indentCount; e->datetimeMode = datetimeMode; e->uuidMode = uuidMode; e->numberMode = numberMode; e->bytesMode = bytesMode; e->iterableMode = iterableMode; e->mappingMode = mappingMode; return (PyObject*) e; } /////////////// // Validator // /////////////// typedef struct { PyObject_HEAD SchemaDocument *schema; } ValidatorObject; PyDoc_STRVAR(validator_doc, "Validator(json_schema)\n" "\n" "Create and return a new Validator instance from the given `json_schema`" " string."); static PyTypeObject Validator_Type = { PyVarObject_HEAD_INIT(NULL, 0) "rapidjson.Validator", /* tp_name */ sizeof(ValidatorObject), /* tp_basicsize */ 0, /* tp_itemsize */ (destructor) validator_dealloc, /* tp_dealloc */ 0, /* tp_print */ 0, /* tp_getattr */ 0, /* tp_setattr */ 0, /* tp_compare */ 0, /* tp_repr */ 0, /* tp_as_number */ 0, /* tp_as_sequence */ 0, /* tp_as_mapping */ 0, /* tp_hash */ (ternaryfunc) validator_call, /* tp_call */ 0, /* tp_str */ 0, /* tp_getattro */ 0, /* tp_setattro */ 0, /* tp_as_buffer */ Py_TPFLAGS_DEFAULT, /* tp_flags */ validator_doc, /* tp_doc */ 0, /* tp_traverse */ 0, /* tp_clear */ 0, /* tp_richcompare */ 0, /* tp_weaklistoffset */ 0, /* tp_iter */ 0, /* tp_iternext */ 0, /* tp_methods */ 0, /* tp_members */ 0, /* tp_getset */ 0, /* tp_base */ 0, /* tp_dict */ 0, /* tp_descr_get */ 0, /* tp_descr_set */ 0, /* tp_dictoffset */ 0, /* tp_init */ 0, /* tp_alloc */ validator_new, /* tp_new */ PyObject_Del, /* tp_free */ }; static PyObject* validator_call(PyObject* self, PyObject* args, PyObject* kwargs) { PyObject* jsonObject; if (!PyArg_ParseTuple(args, "O", &jsonObject)) return NULL; const char* jsonStr; PyObject* asUnicode = NULL; if (PyUnicode_Check(jsonObject)) { jsonStr = PyUnicode_AsUTF8(jsonObject); if (jsonStr == NULL) return NULL; } else if (PyBytes_Check(jsonObject) || PyByteArray_Check(jsonObject)) { asUnicode = PyUnicode_FromEncodedObject(jsonObject, "utf-8", NULL); if (asUnicode == NULL) return NULL; jsonStr = PyUnicode_AsUTF8(asUnicode); if (jsonStr == NULL) { Py_DECREF(asUnicode); return NULL; } } else { PyErr_SetString(PyExc_TypeError, "Expected string or UTF-8 encoded bytes or bytearray"); return NULL; } Document d; bool error; Py_BEGIN_ALLOW_THREADS error = d.Parse(jsonStr).HasParseError(); Py_END_ALLOW_THREADS if (error) { if (asUnicode != NULL) Py_DECREF(asUnicode); PyErr_SetString(decode_error, "Invalid JSON"); return NULL; } SchemaValidator validator(*((ValidatorObject*) self)->schema); bool accept; Py_BEGIN_ALLOW_THREADS accept = d.Accept(validator); Py_END_ALLOW_THREADS if (asUnicode != NULL) Py_DECREF(asUnicode); if (!accept) { StringBuffer sptr; StringBuffer dptr; Py_BEGIN_ALLOW_THREADS validator.GetInvalidSchemaPointer().StringifyUriFragment(sptr); validator.GetInvalidDocumentPointer().StringifyUriFragment(dptr); Py_END_ALLOW_THREADS PyObject* error = Py_BuildValue("sss", validator.GetInvalidSchemaKeyword(), sptr.GetString(), dptr.GetString()); PyErr_SetObject(validation_error, error); if (error != NULL) Py_DECREF(error); sptr.Clear(); dptr.Clear(); return NULL; } Py_RETURN_NONE; } static void validator_dealloc(PyObject* self) { ValidatorObject* s = (ValidatorObject*) self; delete s->schema; Py_TYPE(self)->tp_free(self); } static PyObject* validator_new(PyTypeObject* type, PyObject* args, PyObject* kwargs) { PyObject* jsonObject; if (!PyArg_ParseTuple(args, "O", &jsonObject)) return NULL; const char* jsonStr; PyObject* asUnicode = NULL; if (PyUnicode_Check(jsonObject)) { jsonStr = PyUnicode_AsUTF8(jsonObject); if (jsonStr == NULL) return NULL; } else if (PyBytes_Check(jsonObject) || PyByteArray_Check(jsonObject)) { asUnicode = PyUnicode_FromEncodedObject(jsonObject, "utf-8", NULL); if (asUnicode == NULL) return NULL; jsonStr = PyUnicode_AsUTF8(asUnicode); if (jsonStr == NULL) { Py_DECREF(asUnicode); return NULL; } } else { PyErr_SetString(PyExc_TypeError, "Expected string or UTF-8 encoded bytes or bytearray"); return NULL; } Document d; bool error; Py_BEGIN_ALLOW_THREADS error = d.Parse(jsonStr).HasParseError(); Py_END_ALLOW_THREADS if (asUnicode != NULL) Py_DECREF(asUnicode); if (error) { PyErr_SetString(decode_error, "Invalid JSON"); return NULL; } ValidatorObject* v = (ValidatorObject*) type->tp_alloc(type, 0); if (v == NULL) return NULL; v->schema = new SchemaDocument(d); return (PyObject*) v; } //////////// // Module // //////////// static PyMethodDef functions[] = { {"loads", (PyCFunction) loads, METH_VARARGS | METH_KEYWORDS, loads_docstring}, {"load", (PyCFunction) load, METH_VARARGS | METH_KEYWORDS, load_docstring}, {"dumps", (PyCFunction) dumps, METH_VARARGS | METH_KEYWORDS, dumps_docstring}, {"dump", (PyCFunction) dump, METH_VARARGS | METH_KEYWORDS, dump_docstring}, {NULL, NULL, 0, NULL} /* sentinel */ }; static int module_exec(PyObject* m) { PyObject* datetimeModule; PyObject* decimalModule; PyObject* uuidModule; if (PyType_Ready(&Decoder_Type) < 0) return -1; if (PyType_Ready(&Encoder_Type) < 0) return -1; if (PyType_Ready(&Validator_Type) < 0) return -1; if (PyType_Ready(&RawJSON_Type) < 0) return -1; PyDateTime_IMPORT; if(!PyDateTimeAPI) return -1; datetimeModule = PyImport_ImportModule("datetime"); if (datetimeModule == NULL) return -1; decimalModule = PyImport_ImportModule("decimal"); if (decimalModule == NULL) return -1; decimal_type = PyObject_GetAttrString(decimalModule, "Decimal"); Py_DECREF(decimalModule); if (decimal_type == NULL) return -1; timezone_type = PyObject_GetAttrString(datetimeModule, "timezone"); Py_DECREF(datetimeModule); if (timezone_type == NULL) return -1; timezone_utc = PyObject_GetAttrString(timezone_type, "utc"); if (timezone_utc == NULL) return -1; uuidModule = PyImport_ImportModule("uuid"); if (uuidModule == NULL) return -1; uuid_type = PyObject_GetAttrString(uuidModule, "UUID"); Py_DECREF(uuidModule); if (uuid_type == NULL) return -1; astimezone_name = PyUnicode_InternFromString("astimezone"); if (astimezone_name == NULL) return -1; hex_name = PyUnicode_InternFromString("hex"); if (hex_name == NULL) return -1; timestamp_name = PyUnicode_InternFromString("timestamp"); if (timestamp_name == NULL) return -1; total_seconds_name = PyUnicode_InternFromString("total_seconds"); if (total_seconds_name == NULL) return -1; utcoffset_name = PyUnicode_InternFromString("utcoffset"); if (utcoffset_name == NULL) return -1; is_infinite_name = PyUnicode_InternFromString("is_infinite"); if (is_infinite_name == NULL) return -1; is_nan_name = PyUnicode_InternFromString("is_nan"); if (is_infinite_name == NULL) return -1; minus_inf_string_value = PyUnicode_InternFromString("-Infinity"); if (minus_inf_string_value == NULL) return -1; nan_string_value = PyUnicode_InternFromString("nan"); if (nan_string_value == NULL) return -1; plus_inf_string_value = PyUnicode_InternFromString("+Infinity"); if (plus_inf_string_value == NULL) return -1; start_object_name = PyUnicode_InternFromString("start_object"); if (start_object_name == NULL) return -1; end_object_name = PyUnicode_InternFromString("end_object"); if (end_object_name == NULL) return -1; default_name = PyUnicode_InternFromString("default"); if (default_name == NULL) return -1; end_array_name = PyUnicode_InternFromString("end_array"); if (end_array_name == NULL) return -1; string_name = PyUnicode_InternFromString("string"); if (string_name == NULL) return -1; read_name = PyUnicode_InternFromString("read"); if (read_name == NULL) return -1; write_name = PyUnicode_InternFromString("write"); if (write_name == NULL) return -1; encoding_name = PyUnicode_InternFromString("encoding"); if (encoding_name == NULL) return -1; #define STRINGIFY(x) XSTRINGIFY(x) #define XSTRINGIFY(x) #x if (PyModule_AddIntConstant(m, "DM_NONE", DM_NONE) || PyModule_AddIntConstant(m, "DM_ISO8601", DM_ISO8601) || PyModule_AddIntConstant(m, "DM_UNIX_TIME", DM_UNIX_TIME) || PyModule_AddIntConstant(m, "DM_ONLY_SECONDS", DM_ONLY_SECONDS) || PyModule_AddIntConstant(m, "DM_IGNORE_TZ", DM_IGNORE_TZ) || PyModule_AddIntConstant(m, "DM_NAIVE_IS_UTC", DM_NAIVE_IS_UTC) || PyModule_AddIntConstant(m, "DM_SHIFT_TO_UTC", DM_SHIFT_TO_UTC) || PyModule_AddIntConstant(m, "UM_NONE", UM_NONE) || PyModule_AddIntConstant(m, "UM_HEX", UM_HEX) || PyModule_AddIntConstant(m, "UM_CANONICAL", UM_CANONICAL) || PyModule_AddIntConstant(m, "NM_NONE", NM_NONE) || PyModule_AddIntConstant(m, "NM_NAN", NM_NAN) || PyModule_AddIntConstant(m, "NM_DECIMAL", NM_DECIMAL) || PyModule_AddIntConstant(m, "NM_NATIVE", NM_NATIVE) || PyModule_AddIntConstant(m, "PM_NONE", PM_NONE) || PyModule_AddIntConstant(m, "PM_COMMENTS", PM_COMMENTS) || PyModule_AddIntConstant(m, "PM_TRAILING_COMMAS", PM_TRAILING_COMMAS) || PyModule_AddIntConstant(m, "BM_NONE", BM_NONE) || PyModule_AddIntConstant(m, "BM_UTF8", BM_UTF8) || PyModule_AddIntConstant(m, "WM_COMPACT", WM_COMPACT) || PyModule_AddIntConstant(m, "WM_PRETTY", WM_PRETTY) || PyModule_AddIntConstant(m, "WM_SINGLE_LINE_ARRAY", WM_SINGLE_LINE_ARRAY) || PyModule_AddIntConstant(m, "IM_ANY_ITERABLE", IM_ANY_ITERABLE) || PyModule_AddIntConstant(m, "IM_ONLY_LISTS", IM_ONLY_LISTS) || PyModule_AddIntConstant(m, "MM_ANY_MAPPING", MM_ANY_MAPPING) || PyModule_AddIntConstant(m, "MM_ONLY_DICTS", MM_ONLY_DICTS) || PyModule_AddIntConstant(m, "MM_COERCE_KEYS_TO_STRINGS", MM_COERCE_KEYS_TO_STRINGS) || PyModule_AddIntConstant(m, "MM_SKIP_NON_STRING_KEYS", MM_SKIP_NON_STRING_KEYS) || PyModule_AddIntConstant(m, "MM_SORT_KEYS", MM_SORT_KEYS) || PyModule_AddStringConstant(m, "__version__", STRINGIFY(PYTHON_RAPIDJSON_VERSION)) || PyModule_AddStringConstant(m, "__author__", "Ken Robbins " ", Lele Gaifax ") || PyModule_AddStringConstant(m, "__rapidjson_version__", RAPIDJSON_VERSION_STRING) || PyModule_AddStringConstant(m, "__rapidjson_exact_version__", #ifdef RAPIDJSON_EXACT_VERSION STRINGIFY(RAPIDJSON_EXACT_VERSION) #else // This may happen for several reasons, under CI // test or when the RJ library does not come from // the git submodule "not available" #endif ) ) return -1; Py_INCREF(&Decoder_Type); if (PyModule_AddObject(m, "Decoder", (PyObject*) &Decoder_Type) < 0) { Py_DECREF(&Decoder_Type); return -1; } Py_INCREF(&Encoder_Type); if (PyModule_AddObject(m, "Encoder", (PyObject*) &Encoder_Type) < 0) { Py_DECREF(&Encoder_Type); return -1; } Py_INCREF(&Validator_Type); if (PyModule_AddObject(m, "Validator", (PyObject*) &Validator_Type) < 0) { Py_DECREF(&Validator_Type); return -1; } Py_INCREF(&RawJSON_Type); if (PyModule_AddObject(m, "RawJSON", (PyObject*) &RawJSON_Type) < 0) { Py_DECREF(&RawJSON_Type); return -1; } validation_error = PyErr_NewException("rapidjson.ValidationError", PyExc_ValueError, NULL); if (validation_error == NULL) return -1; Py_INCREF(validation_error); if (PyModule_AddObject(m, "ValidationError", validation_error) < 0) { Py_DECREF(validation_error); return -1; } decode_error = PyErr_NewException("rapidjson.JSONDecodeError", PyExc_ValueError, NULL); if (decode_error == NULL) return -1; Py_INCREF(decode_error); if (PyModule_AddObject(m, "JSONDecodeError", decode_error) < 0) { Py_DECREF(decode_error); return -1; } return 0; } static struct PyModuleDef_Slot slots[] = { {Py_mod_exec, (void*) module_exec}, {0, NULL} }; static PyModuleDef module = { PyModuleDef_HEAD_INIT, /* m_base */ "rapidjson", /* m_name */ PyDoc_STR("Fast, simple JSON encoder and decoder. Based on RapidJSON C++ library."), 0, /* m_size */ functions, /* m_methods */ slots, /* m_slots */ NULL, /* m_traverse */ NULL, /* m_clear */ NULL /* m_free */ }; PyMODINIT_FUNC PyInit_rapidjson() { return PyModuleDef_Init(&module); }

Back | FazBrowse Home | New Git URL