Skip to content

Commit

Permalink
fix: allow hashable types to be used as dictionary keys (#103)
Browse files Browse the repository at this point in the history
* test: add failing test case

* fix: always sort serialized

* test: add dict keys test case

* chore: update changelog

* refactor: use serialize as sort key
  • Loading branch information
iamogbz authored Jan 13, 2020
1 parent 6e99629 commit 57f1099
Show file tree
Hide file tree
Showing 4 changed files with 75 additions and 26 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ From v1.0.0 onwards, this project adheres to [Semantic Versioning](https://semve

## Master (Unreleased)

- Fix issue with using hashables as dict keys or in sets (#103)
- Add support for custom objects repr (#101)
- Add support for nested test classes (#99)
- Remove `_snapshot_subdirectory_name` from `SnapshotFossilizer` (#99)
Expand Down
11 changes: 1 addition & 10 deletions src/syrupy/extensions/amber.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import hashlib
import os
from types import GeneratorType
from typing import (
Expand Down Expand Up @@ -79,18 +78,10 @@ def read_file(cls, filepath: str) -> "SnapshotFossil":

@classmethod
def sort(cls, iterable: Iterable[Any]) -> Iterable[Any]:
def _sort_key(value: Any) -> Any:
if isinstance(value, frozenset):
h = hashlib.sha256()
for element in cls.sort(value):
h.update(str(element).encode("utf-8"))
return h.hexdigest()
return value

try:
return sorted(iterable)
except TypeError:
return sorted(iterable, key=_sort_key)
return sorted(iterable, key=cls.serialize)

@classmethod
def with_indent(cls, string: str, depth: int) -> str:
Expand Down
49 changes: 46 additions & 3 deletions tests/__snapshots__/test_extension_amber.ambr
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,27 @@
],
}
---
# name: test_dict[actual2]
<class 'dict'> {
'a': 'Some ttext.',
1: True,
<class 'ExampleTuple'> (
1,
2,
3,
4,
): <class 'dict'> {
'e': False,
},
<class 'frozenset'> {
'1',
'2',
}: <class 'list'> [
'1',
2,
],
}
---
# name: test_empty_snapshot
None
---
Expand Down Expand Up @@ -139,22 +160,44 @@
# name: test_reflection
SnapshotAssertion(name='snapshot', num_executions=0)
---
# name: test_set
# name: test_set[actual0]
<class 'set'> {
'a',
'is',
'set',
'this',
}
---
# name: test_set.1
# name: test_set[actual1]
<class 'set'> {
'contains',
'frozen',
<class 'frozenset'> {
'1',
'2',
},
}
---
# name: test_set[actual2]
<class 'set'> {
'contains',
'frozen',
'tuple',
<class 'tuple'> (
1,
2,
),
}
---
# name: test_set[actual3]
<class 'set'> {
'contains',
'namedtuple',
<class 'ExampleTuple'> (
1,
2,
3,
4,
),
}
---
# name: test_string[0]
Expand Down
40 changes: 27 additions & 13 deletions tests/test_extension_amber.py
Original file line number Diff line number Diff line change
Expand Up @@ -49,30 +49,44 @@ def test_multiple_snapshots(snapshot):
assert snapshot == "Third."


ExampleTuple = namedtuple("ExampleTuple", ["a", "b", "c", "d"])


def test_tuple(snapshot):
assert snapshot == ("this", "is", ("a", "tuple"))
assert snapshot == ExampleTuple(a="this", b="is", c="a", d={"named", "tuple"})


@pytest.mark.parametrize(
"actual",
[
{"this", "is", "a", "set"},
{"contains", "frozen", frozenset({"1", "2"})},
{"contains", "tuple", (1, 2)},
{"contains", "namedtuple", ExampleTuple(a=1, b=2, c=3, d=4)},
],
)
def test_set(snapshot, actual):
assert snapshot == actual


@pytest.mark.parametrize(
"actual",
[
{"b": True, "c": "Some text.", "d": ["1", 2], "a": {"e": False}},
{"b": True, "c": "Some ttext.", "d": ["1", 2], "a": {"e": False}},
{
1: True,
"a": "Some ttext.",
frozenset({"1", "2"}): ["1", 2],
ExampleTuple(a=1, b=2, c=3, d=4): {"e": False},
},
],
)
def test_dict(snapshot, actual):
assert actual == snapshot


def test_set(snapshot):
assert snapshot == {"this", "is", "a", "set"}
assert snapshot == {"contains", "frozen", frozenset({"1", "2"})}


ExampleTuple = namedtuple("ExampleTuple", ["a", "b", "c", "d"])


def test_tuple(snapshot):
assert snapshot == ("this", "is", ("a", "tuple"))
assert snapshot == ExampleTuple(a="this", b="is", c="a", d={"named", "tuple"})


def test_numbers(snapshot):
assert snapshot == 3.5
assert snapshot == 7
Expand Down

0 comments on commit 57f1099

Please sign in to comment.