1
0
mirror of https://github.com/ultrajson/ultrajson.git synced 2024-11-23 08:22:12 +01:00

Drop support for EOL Python 2

This commit is contained in:
Hugo 2020-03-15 10:51:19 +02:00
parent a2c14dd829
commit ff8e64caa1
14 changed files with 63 additions and 177 deletions

@ -8,10 +8,9 @@ jobs:
strategy:
fail-fast: false
matrix:
python-version: ["2.7", "3.5", "3.6", "3.7", "3.8", "3.9"]
python-version: ["3.5", "3.6", "3.7", "3.8", "3.9"]
os: [ubuntu-18.04, ubuntu-16.04, macos-latest, windows-2019]
exclude:
- { python-version: 2.7, os: windows-2019 }
- { python-version: 3.9, os: ubuntu-16.04 }
- { python-version: 3.9, os: macos-latest }
- { python-version: 3.9, os: windows-2019 }
@ -72,7 +71,7 @@ jobs:
- name: Install dependencies
run: |
python -m pip install --upgrade pip
python -m pip install --upgrade pytest six
python -m pip install --upgrade pytest
python -m pip install .
- name: Tests

17
.gitignore vendored

@ -38,19 +38,20 @@ pip-log.txt
pip-delete-this-directory.txt
# Unit test / coverage reports
htmlcov/
.tox/
.nox/
.coverage
.coverage.*
.cache
nosetests.xml
coverage.xml
*.cover
*.py,cover
.cache
.coverage
.coverage.*
.hypothesis/
.nox/
.pytest_cache/
.testmondata
.tox/
cover/
coverage.xml
htmlcov/
nosetests.xml
# Translations
*.mo

@ -3,12 +3,13 @@ repos:
rev: v2.1.0
hooks:
- id: pyupgrade
args: ["--py3-plus"]
- repo: https://github.com/psf/black
rev: 19.10b0
hooks:
- id: black
args: ["--target-version", "py27"]
args: ["--target-version", "py35"]
- repo: https://gitlab.com/pycqa/flake8
rev: 3.7.9

@ -7,7 +7,6 @@ arch:
- s390x
python:
- "2.7"
- "3.5"
- "3.8"
- "3.9-dev"
@ -17,7 +16,7 @@ jobs:
install:
- pip install -U pip
- pip install -U pytest six
- pip install -U pytest
- pip install .
script: pytest

@ -29,7 +29,7 @@ UltraJSON
:alt: Code style: Black
:target: https://github.com/psf/black
UltraJSON is an ultra fast JSON encoder and decoder written in pure C with bindings for Python 2.7 and 3.5+.
UltraJSON is an ultra fast JSON encoder and decoder written in pure C with bindings for Python 3.5+.
To install it just run pip as usual:

@ -2,4 +2,4 @@
requires = ["setuptools>=42", "wheel", "setuptools_scm[toml]>=3.4"]
[tool.black]
target_version = ["py27"]
target_version = ["py35"]

@ -36,7 +36,7 @@ http://www.opensource.apple.com/source/tcl/tcl-14/tcl/license.terms
* Copyright (c) 1994 Sun Microsystems, Inc.
*/
#include "py_defines.h"
#include <Python.h>
#include <ultrajson.h>
@ -90,7 +90,7 @@ static JSOBJ Object_newArray(void *prv)
static JSOBJ Object_newInteger(void *prv, JSINT32 value)
{
return PyInt_FromLong( (long) value);
return PyLong_FromLong( (long) value);
}
static JSOBJ Object_newLong(void *prv, JSINT64 value)
@ -147,7 +147,7 @@ PyObject* JSONToObj(PyObject* self, PyObject *args, PyObject *kwargs)
return NULL;
}
if (PyString_Check(arg))
if (PyBytes_Check(arg))
{
sarg = arg;
}
@ -172,7 +172,7 @@ PyObject* JSONToObj(PyObject* self, PyObject *args, PyObject *kwargs)
dconv_s2d_init(DCONV_S2D_ALLOW_TRAILING_JUNK, 0.0, 0.0, "Infinity", "NaN");
ret = JSON_DecodeObject(&decoder, PyString_AS_STRING(sarg), PyString_GET_SIZE(sarg));
ret = JSON_DecodeObject(&decoder, PyBytes_AS_STRING(sarg), PyBytes_GET_SIZE(sarg));
dconv_s2d_free();

@ -36,7 +36,7 @@ http://www.opensource.apple.com/source/tcl/tcl-14/tcl/license.terms
* Copyright (c) 1994 Sun Microsystems, Inc.
*/
#include "py_defines.h"
#include <Python.h>
#include <stdio.h>
#include <ultrajson.h>
@ -98,14 +98,14 @@ void initObjToJSON(void)
static void *PyIntToINT64(JSOBJ _obj, JSONTypeContext *tc, void *outValue, size_t *_outLen)
{
PyObject *obj = (PyObject *) _obj;
*((JSINT64 *) outValue) = PyInt_AS_LONG (obj);
*((JSINT64 *) outValue) = PyLong_AsLong (obj);
return NULL;
}
#else
static void *PyIntToINT32(JSOBJ _obj, JSONTypeContext *tc, void *outValue, size_t *_outLen)
{
PyObject *obj = (PyObject *) _obj;
*((JSINT32 *) outValue) = PyInt_AS_LONG (obj);
*((JSINT32 *) outValue) = PyLong_AsLong (obj);
return NULL;
}
#endif
@ -132,15 +132,14 @@ static void *PyFloatToDOUBLE(JSOBJ _obj, JSONTypeContext *tc, void *outValue, si
static void *PyStringToUTF8(JSOBJ _obj, JSONTypeContext *tc, void *outValue, size_t *_outLen)
{
PyObject *obj = (PyObject *) _obj;
*_outLen = PyString_GET_SIZE(obj);
return PyString_AS_STRING(obj);
*_outLen = PyBytes_GET_SIZE(obj);
return PyBytes_AS_STRING(obj);
}
static void *PyUnicodeToUTF8(JSOBJ _obj, JSONTypeContext *tc, void *outValue, size_t *_outLen)
{
PyObject *obj = (PyObject *) _obj;
PyObject *newObj;
#if (PY_VERSION_HEX >= 0x03030000)
if (PyUnicode_IS_COMPACT_ASCII(obj))
{
Py_ssize_t len;
@ -148,7 +147,6 @@ static void *PyUnicodeToUTF8(JSOBJ _obj, JSONTypeContext *tc, void *outValue, si
*_outLen = len;
return data;
}
#endif
newObj = PyUnicode_AsUTF8String(obj);
if(!newObj)
{
@ -157,8 +155,8 @@ static void *PyUnicodeToUTF8(JSOBJ _obj, JSONTypeContext *tc, void *outValue, si
GET_TC(tc)->newObj = newObj;
*_outLen = PyString_GET_SIZE(newObj);
return PyString_AS_STRING(newObj);
*_outLen = PyBytes_GET_SIZE(newObj);
return PyBytes_AS_STRING(newObj);
}
static void *PyRawJSONToUTF8(JSOBJ _obj, JSONTypeContext *tc, void *outValue, size_t *_outLen)
@ -266,20 +264,18 @@ static int Dict_iterNext(JSOBJ obj, JSONTypeContext *tc)
Py_DECREF(itemNameTmp);
}
else
if (!PyString_Check(GET_TC(tc)->itemName))
if (!PyBytes_Check(GET_TC(tc)->itemName))
{
if (UNLIKELY(GET_TC(tc)->itemName == Py_None))
{
GET_TC(tc)->itemName = PyString_FromString("null");
GET_TC(tc)->itemName = PyUnicode_FromString("null");
return 1;
}
GET_TC(tc)->itemName = PyObject_Str(GET_TC(tc)->itemName);
#if PY_MAJOR_VERSION >= 3
itemNameTmp = GET_TC(tc)->itemName;
GET_TC(tc)->itemName = PyUnicode_AsUTF8String (GET_TC(tc)->itemName);
Py_DECREF(itemNameTmp);
#endif
}
else
{
@ -308,17 +304,15 @@ static JSOBJ Dict_iterGetValue(JSOBJ obj, JSONTypeContext *tc)
static char *Dict_iterGetName(JSOBJ obj, JSONTypeContext *tc, size_t *outLen)
{
*outLen = PyString_GET_SIZE(GET_TC(tc)->itemName);
return PyString_AS_STRING(GET_TC(tc)->itemName);
*outLen = PyBytes_GET_SIZE(GET_TC(tc)->itemName);
return PyBytes_AS_STRING(GET_TC(tc)->itemName);
}
static int SortedDict_iterNext(JSOBJ obj, JSONTypeContext *tc)
{
PyObject *items = NULL, *item = NULL, *key = NULL, *value = NULL;
Py_ssize_t i, nitems;
#if PY_MAJOR_VERSION >= 3
PyObject* keyTmp;
#endif
// Upon first call, obtain a list of the keys and sort them. This follows the same logic as the
// stanard library's _json.c sort_keys handler.
@ -355,14 +349,12 @@ static int SortedDict_iterNext(JSOBJ obj, JSONTypeContext *tc)
{
key = PyUnicode_AsUTF8String(key);
}
else if (!PyString_Check(key))
else if (!PyBytes_Check(key))
{
key = PyObject_Str(key);
#if PY_MAJOR_VERSION >= 3
keyTmp = key;
key = PyUnicode_AsUTF8String(key);
Py_DECREF(keyTmp);
#endif
}
else
{
@ -421,8 +413,8 @@ static JSOBJ SortedDict_iterGetValue(JSOBJ obj, JSONTypeContext *tc)
static char *SortedDict_iterGetName(JSOBJ obj, JSONTypeContext *tc, size_t *outLen)
{
*outLen = PyString_GET_SIZE(GET_TC(tc)->itemName);
return PyString_AS_STRING(GET_TC(tc)->itemName);
*outLen = PyBytes_GET_SIZE(GET_TC(tc)->itemName);
return PyBytes_AS_STRING(GET_TC(tc)->itemName);
}
static void SetupDictIter(PyObject *dictObj, TypeContext *pc, JSONObjectEncoder *enc)
@ -522,7 +514,7 @@ static void Object_beginTypeContext (JSOBJ _obj, JSONTypeContext *tc, JSONObject
return;
}
else
if (PyInt_Check(obj))
if (PyLong_Check(obj))
{
PRINTMARK();
#ifdef _LP64
@ -533,7 +525,7 @@ static void Object_beginTypeContext (JSOBJ _obj, JSONTypeContext *tc, JSONObject
return;
}
else
if (PyString_Check(obj))
if (PyBytes_Check(obj))
{
PRINTMARK();
pc->PyTypeToJSON = PyStringToUTF8; tc->type = JT_UTF8;
@ -644,7 +636,7 @@ ISITERABLE:
goto INVALID;
}
if (!PyString_Check(toJSONResult) && !PyUnicode_Check(toJSONResult))
if (!PyBytes_Check(toJSONResult) && !PyUnicode_Check(toJSONResult))
{
Py_DECREF(toJSONResult);
PyErr_Format (PyExc_TypeError, "expected string");
@ -662,13 +654,9 @@ ISITERABLE:
PyErr_Clear();
objRepr = PyObject_Repr(obj);
#if PY_MAJOR_VERSION >= 3
PyObject* str = PyUnicode_AsEncodedString(objRepr, "utf-8", "~E~");
PyErr_Format (PyExc_TypeError, "%s is not JSON serializable", PyString_AS_STRING(str));
PyErr_Format (PyExc_TypeError, "%s is not JSON serializable", PyBytes_AS_STRING(str));
Py_XDECREF(str);
#else
PyErr_Format (PyExc_TypeError, "%s is not JSON serializable", PyString_AS_STRING(objRepr));
#endif
Py_DECREF(objRepr);
INVALID:
@ -841,7 +829,7 @@ PyObject* objToJSON(PyObject* self, PyObject *args, PyObject *kwargs)
return NULL;
}
newobj = PyString_FromString (ret);
newobj = PyUnicode_FromString (ret);
if (ret != buffer)
{

@ -1,53 +0,0 @@
/*
Developed by ESN, an Electronic Arts Inc. studio.
Copyright (c) 2014, Electronic Arts Inc.
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
* Neither the name of ESN, Electronic Arts Inc. nor the
names of its contributors may be used to endorse or promote products
derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL ELECTRONIC ARTS INC. BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
Portions of code from MODP_ASCII - Ascii transformations (upper/lower, etc)
https://github.com/client9/stringencoders
Copyright (c) 2007 Nick Galbreath -- nickg [at] modp [dot] com. All rights reserved.
Numeric decoder derived from from TCL library
https://opensource.apple.com/source/tcl/tcl-14/tcl/license.terms
* Copyright (c) 1988-1993 The Regents of the University of California.
* Copyright (c) 1994 Sun Microsystems, Inc.
*/
#include <Python.h>
#if PY_MAJOR_VERSION >= 3
#define PyInt_Check PyLong_Check
#define PyInt_AS_LONG PyLong_AsLong
#define PyInt_FromLong PyLong_FromLong
#define PyString_Check PyBytes_Check
#define PyString_GET_SIZE PyBytes_GET_SIZE
#define PyString_AS_STRING PyBytes_AS_STRING
#define PyString_FromString PyUnicode_FromString
#endif

@ -36,7 +36,7 @@ http://www.opensource.apple.com/source/tcl/tcl-14/tcl/license.terms
* Copyright (c) 1994 Sun Microsystems, Inc.
*/
#include "py_defines.h"
#include <Python.h>
#include "version.h"
/* objToJSON */
@ -65,8 +65,6 @@ static PyMethodDef ujsonMethods[] = {
{NULL, NULL, 0, NULL} /* Sentinel */
};
#if PY_MAJOR_VERSION >= 3
static struct PyModuleDef moduledef = {
PyModuleDef_HEAD_INIT,
"ujson",
@ -79,35 +77,21 @@ static struct PyModuleDef moduledef = {
NULL /* m_free */
};
#define PYMODINITFUNC PyObject *PyInit_ujson(void)
#define PYMODULE_CREATE() PyModule_Create(&moduledef)
#define MODINITERROR return NULL
#else
#define PYMODINITFUNC PyMODINIT_FUNC initujson(void)
#define PYMODULE_CREATE() Py_InitModule("ujson", ujsonMethods)
#define MODINITERROR return
#endif
PYMODINITFUNC
PyObject *PyInit_ujson(void)
{
PyObject *module;
PyObject *version_string;
initObjToJSON();
module = PYMODULE_CREATE();
module = PyModule_Create(&moduledef);
if (module == NULL)
{
MODINITERROR;
return NULL;
}
version_string = PyString_FromString (UJSON_VERSION);
version_string = PyUnicode_FromString (UJSON_VERSION);
PyModule_AddObject (module, "__version__", version_string);
#if PY_MAJOR_VERSION >= 3
return module;
#endif
}

@ -2,7 +2,7 @@
max_line_length = 88
[tool:isort]
known_third_party = pytest,setuptools,six,ujson
known_third_party = pytest,setuptools,ujson
force_grid_wrap = 0
include_trailing_comma = True
line_length = 88

@ -9,13 +9,12 @@ Development Status :: 5 - Production/Stable
Intended Audience :: Developers
License :: OSI Approved :: BSD License
Programming Language :: C
Programming Language :: Python :: 2
Programming Language :: Python :: 2.7
Programming Language :: Python :: 3
Programming Language :: Python :: 3.5
Programming Language :: Python :: 3.6
Programming Language :: Python :: 3.7
Programming Language :: Python :: 3.8
Programming Language :: Python :: 3 :: Only
"""
dconv_source_files = glob("./deps/double-conversion/double-conversion/*.cc")
@ -84,6 +83,6 @@ setup(
"write_to_template": version_template,
},
setup_requires=["setuptools_scm"],
python_requires=">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*",
python_requires=">=3.5",
classifiers=[x for x in CLASSIFIERS.split("\n") if x],
)

@ -1,5 +1,4 @@
# coding=UTF-8
from __future__ import division, print_function, unicode_literals
import json
import os
@ -376,7 +375,7 @@ def benchmark_complex_object():
results_new_benchmark("Complex object")
COUNT = 100
with open(os.path.join(os.path.dirname(__file__), "sample.json"), "r") as f:
with open(os.path.join(os.path.dirname(__file__), "sample.json")) as f:
test_object = json.load(f)
run_encode(COUNT)

@ -1,8 +1,7 @@
# coding=UTF-8
from __future__ import print_function, unicode_literals
import decimal
import functools
import io
import json
import math
import re
@ -10,13 +9,7 @@ import sys
from collections import OrderedDict
import pytest
import six
import ujson
from six.moves import range, zip
json_unicode = (
functools.partial(json.dumps, encoding="utf-8") if six.PY2 else json.dumps
)
def assert_almost_equal(a, b):
@ -144,7 +137,7 @@ def test_encode_control_escaping():
enc = ujson.encode(test_input)
dec = ujson.decode(enc)
assert test_input == dec
assert enc == json_unicode(test_input)
assert enc == json.dumps(test_input)
# Characters outside of Basic Multilingual Plane(larger than
@ -164,11 +157,8 @@ def test_encode_unicode_bmp():
decoded = ujson.loads(encoded)
assert s == decoded
# ujson outputs an UTF-8 encoded str object
if six.PY2:
encoded = ujson.dumps(s, ensure_ascii=False).decode("utf-8")
else:
encoded = ujson.dumps(s, ensure_ascii=False)
# ujson outputs a UTF-8 encoded str object
encoded = ujson.dumps(s, ensure_ascii=False)
# json outputs an unicode object
encoded_json = json.dumps(s, ensure_ascii=False)
@ -187,11 +177,8 @@ def test_encode_symbols():
decoded = ujson.loads(encoded)
assert s == decoded
# ujson outputs an UTF-8 encoded str object
if six.PY2:
encoded = ujson.dumps(s, ensure_ascii=False).decode("utf-8")
else:
encoded = ujson.dumps(s, ensure_ascii=False)
# ujson outputs a UTF-8 encoded str object
encoded = ujson.dumps(s, ensure_ascii=False)
# json outputs an unicode object
encoded_json = json.dumps(s, ensure_ascii=False)
@ -251,9 +238,7 @@ def test_encode_dict_key_ref_counting():
def test_encode_to_utf8():
test_input = b"\xe6\x97\xa5\xd1\x88"
if not six.PY2:
test_input = test_input.decode("utf-8")
test_input = b"\xe6\x97\xa5\xd1\x88".decode("utf-8")
enc = ujson.encode(test_input, ensure_ascii=False)
dec = ujson.decode(enc)
assert enc == json.dumps(test_input, ensure_ascii=False)
@ -325,7 +310,7 @@ def test_decode_null_character():
def test_dump_to_file():
f = six.StringIO()
f = io.StringIO()
ujson.dump([1, 2, 3], f)
assert "[1,2,3]" == f.getvalue()
@ -349,7 +334,7 @@ def test_dump_file_args_error():
def test_load_file():
f = six.StringIO("[1,2,3,4]")
f = io.StringIO("[1,2,3,4]")
assert [1, 2, 3, 4] == ujson.load(f)
@ -393,22 +378,15 @@ def test_decode_number_with32bit_sign_bit():
def test_encode_big_escape():
for x in range(10):
if six.PY2:
base = "\xc3\xa5"
else:
base = "\u00e5".encode("utf-8")
base = "\u00e5".encode()
test_input = base * 1024 * 1024 * 2
ujson.encode(test_input)
def test_decode_big_escape():
for x in range(10):
if six.PY2:
base = "\xc3\xa5"
quote = '"'
else:
base = "\u00e5".encode("utf-8")
quote = b'"'
base = "\u00e5".encode()
quote = b'"'
test_input = quote + (base * 1024 * 1024 * 2) + quote
ujson.decode(test_input)
@ -489,7 +467,6 @@ def test_decode_array_empty():
assert [] == obj
@pytest.mark.skipif(six.PY2, reason="Only raises on Python 3")
def test_encoding_invalid_unicode_character():
s = "\udc7f"
with pytest.raises(UnicodeEncodeError):
@ -622,19 +599,11 @@ class SomeObject:
return "Some Object"
if sys.version_info.major == 2:
EMPTY_SET_ERROR = "set([]) is not JSON serializable"
FILLED_SET_ERROR = "set([1, 2, 3]) is not JSON serializable"
else:
EMPTY_SET_ERROR = "set() is not JSON serializable"
FILLED_SET_ERROR = "{1, 2, 3} is not JSON serializable"
@pytest.mark.parametrize(
"test_input, expected_exception, expected_message",
[
(set(), TypeError, EMPTY_SET_ERROR),
({1, 2, 3}, TypeError, FILLED_SET_ERROR),
(set(), TypeError, "set() is not JSON serializable"),
({1, 2, 3}, TypeError, "{1, 2, 3} is not JSON serializable"),
(SomeObject(), TypeError, "Some Object is not JSON serializable"),
],
)
@ -749,7 +718,7 @@ def test_encode_unicode(test_input):
enc = ujson.encode(test_input)
dec = ujson.decode(enc)
assert enc == json_unicode(test_input)
assert enc == json.dumps(test_input)
assert dec == json.loads(enc)