From a8774a71e2bf238509faa35b485fb556fca14571 Mon Sep 17 00:00:00 2001 From: Hynek Schlawack Date: Mon, 19 Oct 2020 10:04:10 +0200 Subject: [PATCH 1/2] Treat frozensets like sets in asdict/astuple Fixes #657 --- src/attr/_funcs.py | 10 +++++++--- tests/test_funcs.py | 27 +++++++++++++++++++++++++++ 2 files changed, 34 insertions(+), 3 deletions(-) diff --git a/src/attr/_funcs.py b/src/attr/_funcs.py index 56e3fcf63..e6c930cbd 100644 --- a/src/attr/_funcs.py +++ b/src/attr/_funcs.py @@ -53,8 +53,10 @@ def asdict( v = getattr(inst, a.name) if filter is not None and not filter(a, v): continue + if value_serializer is not None: v = value_serializer(inst, a, v) + if recurse is True: if has(v.__class__): rv[a.name] = asdict( @@ -65,7 +67,7 @@ def asdict( retain_collection_types, value_serializer, ) - elif isinstance(v, (tuple, list, set)): + elif isinstance(v, (tuple, list, set, frozenset)): cf = v.__class__ if retain_collection_types is True else list rv[a.name] = cf( [ @@ -127,7 +129,7 @@ def _asdict_anything( retain_collection_types, value_serializer, ) - elif isinstance(val, (tuple, list, set)): + elif isinstance(val, (tuple, list, set, frozenset)): cf = val.__class__ if retain_collection_types is True else list rv = cf( [ @@ -158,6 +160,7 @@ def _asdict_anything( rv = val if value_serializer is not None: rv = value_serializer(None, None, rv) + return rv @@ -212,7 +215,7 @@ def astuple( retain_collection_types=retain, ) ) - elif isinstance(v, (tuple, list, set)): + elif isinstance(v, (tuple, list, set, frozenset)): cf = v.__class__ if retain is True else list rv.append( cf( @@ -257,6 +260,7 @@ def astuple( rv.append(v) else: rv.append(v) + return rv if tuple_factory is list else tuple_factory(rv) diff --git a/tests/test_funcs.py b/tests/test_funcs.py index 4d99e06f5..20e2747e6 100644 --- a/tests/test_funcs.py +++ b/tests/test_funcs.py @@ -149,12 +149,26 @@ def test_lists_tuples_retain_type(self, container, C): retain_collection_types=True, ) + @given(set_type=st.sampled_from((set, frozenset))) + def test_sets_no_retain(self, C, set_type): + """ + Set types are converted to lists if retain_collection_types=False. + """ + d = asdict( + C(1, set_type((1, 2, 3))), + retain_collection_types=False, + recurse=True, + ) + + assert {"x": 1, "y": [1, 2, 3]} == d + @given(st.sampled_from(MAPPING_TYPES)) def test_dicts(self, C, dict_factory): """ If recurse is True, also recurse into dicts. """ res = asdict(C(1, {"a": C(4, 5)}), dict_factory=dict_factory) + assert {"x": 1, "y": {"a": {"x": 4, "y": 5}}} == res assert isinstance(res, dict_factory) @@ -330,6 +344,19 @@ def test_roundtrip(self, cls, tuple_class): assert instance == roundtrip_instance + @given(set_type=st.sampled_from((set, frozenset))) + def test_sets_no_retain(self, C, set_type): + """ + Set types are converted to lists if retain_collection_types=False. + """ + d = astuple( + C(1, set_type((1, 2, 3))), + retain_collection_types=False, + recurse=True, + ) + + assert (1, [1, 2, 3]) == d + class TestHas(object): """ From f278e5b6a1f8ab111b18c9f1e9712f6f9dfa6913 Mon Sep 17 00:00:00 2001 From: Hynek Schlawack Date: Mon, 19 Oct 2020 10:07:39 +0200 Subject: [PATCH 2/2] Add newsfragment --- changelog.d/704.change.rst | 1 + 1 file changed, 1 insertion(+) create mode 100644 changelog.d/704.change.rst diff --git a/changelog.d/704.change.rst b/changelog.d/704.change.rst new file mode 100644 index 000000000..26b08e223 --- /dev/null +++ b/changelog.d/704.change.rst @@ -0,0 +1 @@ +``attr.asdict()`` and ``attr.astuple()`` now treat ``frozenset``\ s like ``set``\ s with regards to the *retain_collection_types* argument.