Skip to content
This repository has been archived by the owner on Jan 31, 2024. It is now read-only.

Commit

Permalink
[oilshell] Respect iteration order for OrderedDict.
Browse files Browse the repository at this point in the history
This also makes the code more consistent -- PyObject_GetIter() is used
in both dict and list use cases.  In ProcessObject().

- Add unit tests
- Fix up runtests.sh to run the tests correctly!
  • Loading branch information
Andy C committed May 31, 2022
1 parent eb561e9 commit d10820b
Show file tree
Hide file tree
Showing 3 changed files with 50 additions and 6 deletions.
15 changes: 13 additions & 2 deletions encoder.c
Expand Up @@ -116,11 +116,18 @@ static yajl_gen_status ProcessObject(_YajlEncoder *self, PyObject *object)
}
if (PyDict_Check(object)) {
PyObject *key, *value;
Py_ssize_t position = 0;

status = yajl_gen_map_open(handle);
if (status == yajl_max_depth_exceeded) goto exit;
while (PyDict_Next(object, &position, &key, &value)) {

/* Oil patch: use PyObject_GetIter instead of PyDict_Next to respect
* __iter__ in OrderedDict. This is also more consistent: the 'list'
* case above already uses PyIter_Next!
* */
iterator = PyObject_GetIter(object);
if (iterator == NULL)
goto exit;
while (key = PyIter_Next(iterator)) {
PyObject *newKey = key;

if (!PyString_Check(key)) {
Expand All @@ -130,13 +137,17 @@ static yajl_gen_status ProcessObject(_YajlEncoder *self, PyObject *object)
}

status = ProcessObject(self, newKey);
/* Oil note: This seems like it's never executed? */
if (key != newKey) {
Py_XDECREF(newKey);
}
if (status == yajl_gen_in_error_state) return status;
if (status == yajl_max_depth_exceeded) goto exit;

value = PyDict_GetItem(object, key);
status = ProcessObject(self, value);
/* Py_XDECREF(value); */

if (status == yajl_gen_in_error_state) return status;
if (status == yajl_max_depth_exceeded) goto exit;
}
Expand Down
17 changes: 16 additions & 1 deletion runtests.sh
@@ -1,3 +1,18 @@
#!/bin/sh
#
# Patched this script for Oil

python setup.py build && PYTHONPATH=.:build/lib.linux-x86_64-2.6 python tests/unit.py && zcat test_data/issue_11.gz| PYTHONPATH=build/lib.linux-x86_64-2.6 ./tests/issue_11.py && python3 setup.py build && PYTHONPATH=build/lib.linux-x86_64-3.1 python3 tests/unit.py
set -o errexit
set -o nounset

rm -f -v yajl.so
python2 setup.py build_ext --inplace

PYTHONPATH=. python tests/unit.py

zcat test_data/issue_11.gz | PYTHONPATH=. ./tests/issue_11.py

# Not supporting Python 3
return

python3 setup.py build && PYTHONPATH=build/lib.linux-x86_64-3.1 python3 tests/unit.py
24 changes: 21 additions & 3 deletions tests/unit.py
Expand Up @@ -59,9 +59,10 @@ class EncoderBase(unittest.TestCase):
def encode(self, value):
return yajl.dumps(value)

def assertEncodesTo(self, value, json):
rc = self.encode(value)
assert rc == json, ('Failed to encode JSON correctly', locals())
def assertEncodesTo(self, value, expected):
actual = self.encode(value)
#assert rc == json, ('Failed to encode JSON correctly', locals())
self.assertEquals(expected, actual)
return True

class BasicJSONEncodeTests(EncoderBase):
Expand All @@ -80,6 +81,23 @@ def test_List(self):
def test_Dict(self):
self.assertEncodesTo({'key' : 'value'}, '{"key":"value"}')

def test_OrderedDict(self):
# Oil patch
from collections import OrderedDict

d = OrderedDict([('z', 1), ('y', 2), ('x', 3)])
d['a'] = 42
d['z'] = 50
print(yajl.dumps(d))

self.assertEncodesTo(d, '{"z":50,"y":2,"x":3,"a":42}')

# Nested ordered dict

d["order"] = OrderedDict([('spam', None), ('eggs', None), ('ham', None)])
self.assertEncodesTo(d,
'{"z":50,"y":2,"x":3,"a":42,"order":{"spam":null,"eggs":null,"ham":null}}')

# Python 3 version
#def test_UnicodeDict(self):
# self.assertEncodesTo({'foō' : 'bār'}, '{"foō":"bār"}')
Expand Down

0 comments on commit d10820b

Please sign in to comment.