Skip to content

Commit

Permalink
feat(serializer): preserve key ordering of OrderedDict
Browse files Browse the repository at this point in the history
BREAKING CHANGE: Key order is now preserved if using OrderedDict in both the Amber serializer and JSON serializer.
  • Loading branch information
Noah Negin-Ulster committed Dec 30, 2022
1 parent 80e2fcd commit 0a2289a
Show file tree
Hide file tree
Showing 6 changed files with 52 additions and 7 deletions.
7 changes: 6 additions & 1 deletion src/syrupy/extensions/amber/serializer.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import os
from collections import OrderedDict
from types import (
GeneratorType,
MappingProxyType,
Expand Down Expand Up @@ -246,9 +247,13 @@ def serialize_namedtuple(cls, data: NamedTuple, **kwargs: Any) -> str:
def serialize_dict(
cls, data: Dict["PropertyName", "SerializableData"], **kwargs: Any
) -> str:
keys = (
data.keys() if isinstance(data, (OrderedDict,)) else cls.sort(data.keys())
)

return cls.__serialize_iterable(
data=data,
resolve_entries=(cls.sort(data.keys()), item_getter, None),
resolve_entries=(keys, item_getter, None),
open_paren="{",
close_paren="}",
separator=": ",
Expand Down
15 changes: 11 additions & 4 deletions src/syrupy/extensions/json/__init__.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import datetime
import json
from collections import OrderedDict
from types import GeneratorType
from typing import (
TYPE_CHECKING,
Expand Down Expand Up @@ -69,8 +70,14 @@ def _filter(

filtered_dct: Dict[Any, Any]
if isinstance(data, (dict,)):
filtered_dct = {}
for key, value in data.items():
filtered_dct = OrderedDict()
keys = (
cls.sort(data.keys())
if not isinstance(data, (OrderedDict,))
else data.keys()
)
for key in keys:
value = data[key]
if exclude and exclude(prop=key, path=path):
continue
if not isinstance(key, (str,)):
Expand All @@ -86,7 +93,7 @@ def _filter(
return filtered_dct

if cls.__is_namedtuple(data):
filtered_dct = {}
filtered_dct = OrderedDict()
for key in cls.sort(data._fields):
value = getattr(data, key)
filtered_dct[key] = cls._filter(
Expand Down Expand Up @@ -135,4 +142,4 @@ def serialize(
data = self._filter(
data=data, depth=0, path=(), exclude=exclude, matcher=matcher
)
return json.dumps(data, indent=2, ensure_ascii=False, sort_keys=True) + "\n"
return json.dumps(data, indent=2, ensure_ascii=False, sort_keys=False) + "\n"
Original file line number Diff line number Diff line change
Expand Up @@ -310,6 +310,12 @@
# name: test_numbers.2
0.3333333333333333
# ---
# name: test_ordered_dict
OrderedDict({
'b': 0,
'a': 1,
})
# ---
# name: test_parameter_with_dot[value.with.dot]
'value.with.dot'
# ---
Expand Down
12 changes: 11 additions & 1 deletion tests/syrupy/extensions/amber/test_amber_serializer.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
from collections import namedtuple
from collections import (
OrderedDict,
namedtuple,
)

import pytest

Expand Down Expand Up @@ -218,3 +221,10 @@ def test_parameter_with_dot(parameter_with_dot, snapshot):
def test_doubly_parametrized(parameter_1, parameter_2, snapshot):
assert parameter_1 == snapshot
assert parameter_2 == snapshot


def test_ordered_dict(snapshot):
d = OrderedDict()
d["b"] = 0
d["a"] = 1
assert snapshot == d
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"b": 0,
"a": {
"b": true,
"a": false
}
}
12 changes: 11 additions & 1 deletion tests/syrupy/extensions/json/test_json_serializer.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
from collections import namedtuple
from collections import (
OrderedDict,
namedtuple,
)

import pytest

Expand Down Expand Up @@ -222,3 +225,10 @@ def test_parameter_with_dot(parameter_with_dot, snapshot_json):
def test_doubly_parametrized(parameter_1, parameter_2, snapshot_json):
assert parameter_1 == snapshot_json
assert parameter_2 == snapshot_json


def test_ordered_dict(snapshot_json):
d = OrderedDict()
d["b"] = 0
d["a"] = OrderedDict(b=True, a=False)
assert snapshot_json == d

1 comment on commit 0a2289a

@github-actions
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Benchmark

Benchmark suite Current: 0a2289a Previous: 02abef5 Ratio
benchmarks/test_1000x.py::test_1000x_reads 0.7739783843137239 iter/sec (stddev: 0.05302717353296172) 0.8381195242511715 iter/sec (stddev: 0.04240394140227035) 1.08
benchmarks/test_1000x.py::test_1000x_writes 0.7539326694711668 iter/sec (stddev: 0.05860716427765634) 0.8626650008455868 iter/sec (stddev: 0.05153168408309042) 1.14
benchmarks/test_standard.py::test_standard 0.7197422881665411 iter/sec (stddev: 0.06798472867402014) 0.7465173870618954 iter/sec (stddev: 0.1502009356924296) 1.04

This comment was automatically generated by workflow using github-action-benchmark.

Please sign in to comment.