Skip to content

Commit 57f1099

Browse files
authored
fix: allow hashable types to be used as dictionary keys (#103)
* test: add failing test case * fix: always sort serialized * test: add dict keys test case * chore: update changelog * refactor: use serialize as sort key
1 parent 6e99629 commit 57f1099

File tree

4 files changed

+75
-26
lines changed

4 files changed

+75
-26
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ From v1.0.0 onwards, this project adheres to [Semantic Versioning](https://semve
88

99
## Master (Unreleased)
1010

11+
- Fix issue with using hashables as dict keys or in sets (#103)
1112
- Add support for custom objects repr (#101)
1213
- Add support for nested test classes (#99)
1314
- Remove `_snapshot_subdirectory_name` from `SnapshotFossilizer` (#99)

src/syrupy/extensions/amber.py

Lines changed: 1 addition & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
import hashlib
21
import os
32
from types import GeneratorType
43
from typing import (
@@ -79,18 +78,10 @@ def read_file(cls, filepath: str) -> "SnapshotFossil":
7978

8079
@classmethod
8180
def sort(cls, iterable: Iterable[Any]) -> Iterable[Any]:
82-
def _sort_key(value: Any) -> Any:
83-
if isinstance(value, frozenset):
84-
h = hashlib.sha256()
85-
for element in cls.sort(value):
86-
h.update(str(element).encode("utf-8"))
87-
return h.hexdigest()
88-
return value
89-
9081
try:
9182
return sorted(iterable)
9283
except TypeError:
93-
return sorted(iterable, key=_sort_key)
84+
return sorted(iterable, key=cls.serialize)
9485

9586
@classmethod
9687
def with_indent(cls, string: str, depth: int) -> str:

tests/__snapshots__/test_extension_amber.ambr

Lines changed: 46 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,27 @@
102102
],
103103
}
104104
---
105+
# name: test_dict[actual2]
106+
<class 'dict'> {
107+
'a': 'Some ttext.',
108+
1: True,
109+
<class 'ExampleTuple'> (
110+
1,
111+
2,
112+
3,
113+
4,
114+
): <class 'dict'> {
115+
'e': False,
116+
},
117+
<class 'frozenset'> {
118+
'1',
119+
'2',
120+
}: <class 'list'> [
121+
'1',
122+
2,
123+
],
124+
}
125+
---
105126
# name: test_empty_snapshot
106127
None
107128
---
@@ -139,22 +160,44 @@
139160
# name: test_reflection
140161
SnapshotAssertion(name='snapshot', num_executions=0)
141162
---
142-
# name: test_set
163+
# name: test_set[actual0]
143164
<class 'set'> {
144165
'a',
145166
'is',
146167
'set',
147168
'this',
148169
}
149170
---
150-
# name: test_set.1
171+
# name: test_set[actual1]
151172
<class 'set'> {
173+
'contains',
174+
'frozen',
152175
<class 'frozenset'> {
153176
'1',
154177
'2',
155178
},
179+
}
180+
---
181+
# name: test_set[actual2]
182+
<class 'set'> {
156183
'contains',
157-
'frozen',
184+
'tuple',
185+
<class 'tuple'> (
186+
1,
187+
2,
188+
),
189+
}
190+
---
191+
# name: test_set[actual3]
192+
<class 'set'> {
193+
'contains',
194+
'namedtuple',
195+
<class 'ExampleTuple'> (
196+
1,
197+
2,
198+
3,
199+
4,
200+
),
158201
}
159202
---
160203
# name: test_string[0]

tests/test_extension_amber.py

Lines changed: 27 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -49,30 +49,44 @@ def test_multiple_snapshots(snapshot):
4949
assert snapshot == "Third."
5050

5151

52+
ExampleTuple = namedtuple("ExampleTuple", ["a", "b", "c", "d"])
53+
54+
55+
def test_tuple(snapshot):
56+
assert snapshot == ("this", "is", ("a", "tuple"))
57+
assert snapshot == ExampleTuple(a="this", b="is", c="a", d={"named", "tuple"})
58+
59+
60+
@pytest.mark.parametrize(
61+
"actual",
62+
[
63+
{"this", "is", "a", "set"},
64+
{"contains", "frozen", frozenset({"1", "2"})},
65+
{"contains", "tuple", (1, 2)},
66+
{"contains", "namedtuple", ExampleTuple(a=1, b=2, c=3, d=4)},
67+
],
68+
)
69+
def test_set(snapshot, actual):
70+
assert snapshot == actual
71+
72+
5273
@pytest.mark.parametrize(
5374
"actual",
5475
[
5576
{"b": True, "c": "Some text.", "d": ["1", 2], "a": {"e": False}},
5677
{"b": True, "c": "Some ttext.", "d": ["1", 2], "a": {"e": False}},
78+
{
79+
1: True,
80+
"a": "Some ttext.",
81+
frozenset({"1", "2"}): ["1", 2],
82+
ExampleTuple(a=1, b=2, c=3, d=4): {"e": False},
83+
},
5784
],
5885
)
5986
def test_dict(snapshot, actual):
6087
assert actual == snapshot
6188

6289

63-
def test_set(snapshot):
64-
assert snapshot == {"this", "is", "a", "set"}
65-
assert snapshot == {"contains", "frozen", frozenset({"1", "2"})}
66-
67-
68-
ExampleTuple = namedtuple("ExampleTuple", ["a", "b", "c", "d"])
69-
70-
71-
def test_tuple(snapshot):
72-
assert snapshot == ("this", "is", ("a", "tuple"))
73-
assert snapshot == ExampleTuple(a="this", b="is", c="a", d={"named", "tuple"})
74-
75-
7690
def test_numbers(snapshot):
7791
assert snapshot == 3.5
7892
assert snapshot == 7

0 commit comments

Comments
 (0)