mirror of
https://github.com/ultrajson/ultrajson.git
synced 2024-05-07 18:06:14 +02:00
Use lowercase strings for bool dict keys
Fixes #613 Also, * Consolidate key conversion for sorted and unsorted cases. * Fix memory leak of the "null" string when handling None dict key.
This commit is contained in:
parent
1c8188dedc
commit
a08b75b970
|
@ -232,19 +232,49 @@ static char *List_iterGetName(JSOBJ obj, JSONTypeContext *tc, size_t *outLen)
|
|||
|
||||
//=============================================================================
|
||||
// Dict iteration functions
|
||||
// itemName might converted to string (Python_Str). Do refCounting
|
||||
// itemName might converted to string (PyObject_Str). Do refCounting
|
||||
// itemValue is borrowed from object (which is dict). No refCounting
|
||||
//=============================================================================
|
||||
|
||||
static int Dict_convertKey(PyObject** pkey)
|
||||
{
|
||||
PyObject* key = *pkey;
|
||||
if (PyUnicode_Check(key))
|
||||
{
|
||||
*pkey = PyUnicode_AsEncodedString(key, NULL, "surrogatepass");
|
||||
return 1;
|
||||
}
|
||||
if (PyBytes_Check(key))
|
||||
{
|
||||
Py_INCREF(key);
|
||||
return 1;
|
||||
}
|
||||
if (UNLIKELY(PyBool_Check(key)))
|
||||
{
|
||||
*pkey = PyBytes_FromString(key == Py_True ? "true" : "false");
|
||||
return 1;
|
||||
}
|
||||
if (UNLIKELY(key == Py_None))
|
||||
{
|
||||
*pkey = PyBytes_FromString("null");
|
||||
return 1;
|
||||
}
|
||||
key = PyObject_Str(key);
|
||||
if (!key)
|
||||
{
|
||||
PRINTMARK();
|
||||
return -1;
|
||||
}
|
||||
*pkey = PyUnicode_AsEncodedString(key, NULL, "surrogatepass");
|
||||
Py_DECREF(key);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int Dict_iterNext(JSOBJ obj, JSONTypeContext *tc)
|
||||
{
|
||||
PyObject* itemNameTmp;
|
||||
|
||||
if (GET_TC(tc)->itemName)
|
||||
{
|
||||
Py_DECREF(GET_TC(tc)->itemName);
|
||||
GET_TC(tc)->itemName = NULL;
|
||||
}
|
||||
Py_CLEAR(GET_TC(tc)->itemName);
|
||||
|
||||
if (!(GET_TC(tc)->itemName = PyIter_Next(GET_TC(tc)->iterator)))
|
||||
{
|
||||
|
@ -258,46 +288,19 @@ static int Dict_iterNext(JSOBJ obj, JSONTypeContext *tc)
|
|||
return 0;
|
||||
}
|
||||
|
||||
if (PyUnicode_Check(GET_TC(tc)->itemName))
|
||||
itemNameTmp = GET_TC(tc)->itemName;
|
||||
if (Dict_convertKey(&GET_TC(tc)->itemName) < 0)
|
||||
{
|
||||
itemNameTmp = GET_TC(tc)->itemName;
|
||||
GET_TC(tc)->itemName = PyUnicode_AsEncodedString (GET_TC(tc)->itemName, NULL, "surrogatepass");
|
||||
Py_DECREF(itemNameTmp);
|
||||
}
|
||||
else
|
||||
if (!PyBytes_Check(GET_TC(tc)->itemName))
|
||||
{
|
||||
if (UNLIKELY(GET_TC(tc)->itemName == Py_None))
|
||||
{
|
||||
itemNameTmp = PyUnicode_FromString("null");
|
||||
GET_TC(tc)->itemName = PyUnicode_AsUTF8String(itemNameTmp);
|
||||
Py_DECREF(Py_None);
|
||||
return 1;
|
||||
}
|
||||
|
||||
itemNameTmp = GET_TC(tc)->itemName;
|
||||
GET_TC(tc)->itemName = PyObject_Str(GET_TC(tc)->itemName);
|
||||
Py_DECREF(itemNameTmp);
|
||||
if (PyErr_Occurred())
|
||||
{
|
||||
PRINTMARK();
|
||||
return -1;
|
||||
}
|
||||
itemNameTmp = GET_TC(tc)->itemName;
|
||||
GET_TC(tc)->itemName = PyUnicode_AsEncodedString (GET_TC(tc)->itemName, NULL, "surrogatepass");
|
||||
Py_DECREF(itemNameTmp);
|
||||
return -1;
|
||||
}
|
||||
Py_DECREF(itemNameTmp);
|
||||
PRINTMARK();
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void Dict_iterEnd(JSOBJ obj, JSONTypeContext *tc)
|
||||
{
|
||||
if (GET_TC(tc)->itemName)
|
||||
{
|
||||
Py_DECREF(GET_TC(tc)->itemName);
|
||||
GET_TC(tc)->itemName = NULL;
|
||||
}
|
||||
Py_CLEAR(GET_TC(tc)->itemName);
|
||||
Py_CLEAR(GET_TC(tc)->iterator);
|
||||
Py_DECREF(GET_TC(tc)->dictObj);
|
||||
PRINTMARK();
|
||||
|
@ -318,7 +321,6 @@ static int SortedDict_iterNext(JSOBJ obj, JSONTypeContext *tc)
|
|||
{
|
||||
PyObject *items = NULL, *item = NULL, *key = NULL, *value = NULL;
|
||||
Py_ssize_t i, nitems;
|
||||
PyObject* keyTmp;
|
||||
|
||||
// Upon first call, obtain a list of the keys and sort them. This follows the same logic as the
|
||||
// standard library's _json.c sort_keys handler.
|
||||
|
@ -350,27 +352,11 @@ static int SortedDict_iterNext(JSOBJ obj, JSONTypeContext *tc)
|
|||
key = PyList_GetItem(items, i);
|
||||
value = PyDict_GetItem(GET_TC(tc)->dictObj, key);
|
||||
|
||||
// Subject the key to the same type restrictions and conversions as in Dict_iterGetValue.
|
||||
if (PyUnicode_Check(key))
|
||||
if (Dict_convertKey(&key) < 0)
|
||||
{
|
||||
key = PyUnicode_AsEncodedString(key, NULL, "surrogatepass");
|
||||
key = NULL; // key is not owned at this point
|
||||
goto error;
|
||||
}
|
||||
else if (!PyBytes_Check(key))
|
||||
{
|
||||
key = PyObject_Str(key);
|
||||
if (PyErr_Occurred())
|
||||
{
|
||||
goto error;
|
||||
}
|
||||
keyTmp = key;
|
||||
key = PyUnicode_AsEncodedString(key, NULL, "surrogatepass");
|
||||
Py_DECREF(keyTmp);
|
||||
}
|
||||
else
|
||||
{
|
||||
Py_INCREF(key);
|
||||
}
|
||||
|
||||
item = PyTuple_Pack(2, key, value);
|
||||
if (item == NULL)
|
||||
{
|
||||
|
|
|
@ -242,7 +242,7 @@ def test_encode_dict_values_ref_counting():
|
|||
@pytest.mark.skipif(
|
||||
hasattr(sys, "pypy_version_info"), reason="PyPy uses incompatible GC"
|
||||
)
|
||||
@pytest.mark.parametrize("key", ["key", b"key", 1, True, None])
|
||||
@pytest.mark.parametrize("key", ["key", b"key", 1, True, False, None])
|
||||
@pytest.mark.parametrize("sort_keys", [False, True])
|
||||
def test_encode_dict_key_ref_counting(key, sort_keys):
|
||||
import gc
|
||||
|
@ -975,9 +975,13 @@ def test_reject_bytes_false():
|
|||
assert ujson.dumps(data, reject_bytes=False) == '{"a":"b"}'
|
||||
|
||||
|
||||
def test_encode_none_key():
|
||||
data = {None: None}
|
||||
assert ujson.dumps(data) == '{"null":null}'
|
||||
def test_encode_special_keys():
|
||||
data = {None: 0, True: 1, False: 2}
|
||||
assert ujson.dumps(data) == '{"null":0,"true":1,"false":2}'
|
||||
data = {None: 0}
|
||||
assert ujson.dumps(data, sort_keys=True) == '{"null":0}'
|
||||
data = {True: 1, False: 2}
|
||||
assert ujson.dumps(data, sort_keys=True) == '{"false":2,"true":1}'
|
||||
|
||||
|
||||
def test_default_function():
|
||||
|
@ -1069,8 +1073,12 @@ def test_obj_str_exception(sort_keys):
|
|||
def __str__(self):
|
||||
raise NotImplementedError
|
||||
|
||||
key = Obj()
|
||||
getrefcount = getattr(sys, "getrefcount", lambda x: 0)
|
||||
old = getrefcount(key)
|
||||
with pytest.raises(NotImplementedError):
|
||||
ujson.dumps({Obj(): 1}, sort_keys=sort_keys)
|
||||
ujson.dumps({key: 1}, sort_keys=sort_keys)
|
||||
assert getrefcount(key) == old
|
||||
|
||||
|
||||
def no_memory_leak(func_code, n=None):
|
||||
|
|
Loading…
Reference in New Issue