-
-
Notifications
You must be signed in to change notification settings - Fork 8.2k
extmod/modujson: Allow json.loads() to preserve dict order. #6135
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Conversation
@@ -224,6 +224,12 @@ STATIC mp_obj_t mod_ujson_load(mp_obj_t stream_obj) { | |||
break; | |||
case '{': | |||
next = mp_obj_new_dict(0); | |||
#if MICROPY_PY_UJSON_ORDERED_DICT |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is this a new option? Is it set anywhere?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It is a new option, and no it's not set by default anywhere. The expectation is it'll be set in make option or board profile if someone wants it... or if it's a desieable enough feature it could be made the default
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hmm, doesn't that make it almost impossible to discover? Someone has to go look at the code to see "hey, look at that! I can enable MICROPY_PY_UJSON_ORDERED_DICT!". Just wondering...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah it's certainly not ideal... At the very least I should enable it on the coverage build and add a unit test - this initial PR was thrown up very quickly in the middle of a project rebase/test/release cycle on my end and I didn't want it to be forgotten.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just put in in mpconfig.h, like all other options.
ef1bc9e
to
8bf982d
Compare
Arguably, this could be good to have enabled by As there are performance differences between regular and ordered dicts still in micropython I'm happy to have it as a separate feature enable flag for now. I've now added the flag in mpconfig.h and added/updated unit test in coverage build. |
tests/extmod/ujson_loads.py
Outdated
|
||
def my_print(o): | ||
if isinstance(o, dict): | ||
print("sorted dict", sorted(o.items())) | ||
if is_coverage: # Has ordered json loads enabled | ||
print("dict", o.items()) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just to check, this assumes the CPython used to run the test will also preserve dict order. Do all CPython versions do this, and if not: from which version on? I.e. we whould be sure this always works.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah yes, that is the assumption yes. This has been the case since Python 3.6, though this aspect was written in as official in 3.7
https://docs.python.org/3.6/whatsnew/3.6.html#whatsnew36-compactdict
Arguably we should look at bringing in this same dict implementation as an option in micropython, it was shown to be significantly more efficient than the previous cpython dict and would likely be better than our current ordered dict implementation at least (certainly for very large dicts)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
... and yes this is why the new unit test is failing, cpython 3.5 is used on travis :-(
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Perhaps rather than making this a #define
feature I should have just added the object_pairs_hook
argument like in cpython:
https://docs.python.org/3.4/library/json.html#json.load
It would be more work to add this level of generic support rather than just added an ordered flag however
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It would be more work to add this level of generic support rather than just added an ordered flag however
Would also be larger in code size.
Perhaps change the check so that when coverage is enabled, it just checks whether the returned dict is the same as sorted(dict).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Checking loaded dict == sorted dict
results the same as the comparison I've already got in place - it works on micropython with my patch and cpython 3.6+ but does not work on cpython3.5. I can put an extra try block in there to loads with object_pairs_hook=OrderedDict
for cpython though.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah but the idea is that CPython never runs any code for which it's behavior changes between versions, i.e. code guarded by is_coverage only gets executed by MicroPython, not CPython, and the else clause is where the expected result gets printed. Pseudocode:
if is_coverage:
print(is_dict_with_same_content_and_sorted(a,b))
else:
print(True)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah I didn't realise that's how it would work, that's brilliant, thanks. I've updated as such.
2064a42
to
6be22fb
Compare
6be22fb
to
659d752
Compare
659d752
to
a64f4bc
Compare
a64f4bc
to
71f39fd
Compare
@@ -224,6 +224,12 @@ STATIC mp_obj_t mod_ujson_load(mp_obj_t stream_obj) { | |||
break; | |||
case '{': | |||
next = mp_obj_new_dict(0); | |||
#if MICROPY_PY_UJSON_ORDERED_DICT | |||
// keep the locals ordered |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same comment as for other commit (plus this isn't about locals here)
71f39fd
to
7fbe7d4
Compare
Codecov Report
@@ Coverage Diff @@
## master #6135 +/- ##
==========================================
- Coverage 98.28% 98.28% -0.01%
==========================================
Files 154 154
Lines 19988 19997 +9
==========================================
+ Hits 19646 19654 +8
- Misses 342 343 +1
Continue to review full report at Codecov.
|
dcd5ccc
to
0e61ba2
Compare
When MICROPY_PY_UJSON_ORDERED_DICT is enabled.
0e61ba2
to
e6e1463
Compare
merge hexky_s2 board
When MICROPY_PY_UJSON_ORDERED_DICT is enabled.
Migrating away from using my previous #5323 PR to make all dict's ordered has unearthed a few other places we were relying on such functionality.