Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions changelog.d/1641.change.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
`attrs.astuple()` and `attr.astuple()` now apply the *filter* to attrs instances used as dictionary keys and values, consistently with other recursive conversions.
6 changes: 6 additions & 0 deletions src/attr/_funcs.py
Original file line number Diff line number Diff line change
Expand Up @@ -238,6 +238,8 @@ def astuple(
Return the *attrs* attribute values of *inst* as a tuple.

Optionally recurse into other *attrs*-decorated classes.
When recursing, *filter* also applies to *attrs* instances used as dictionary
keys and values.

Args:
inst: Instance of an *attrs*-decorated class.
Expand Down Expand Up @@ -268,6 +270,8 @@ def astuple(
If *cls* is not an *attrs* class.

.. versionadded:: 16.2.0
.. versionchanged:: 26.2.0
Apply *filter* to attrs instances in dictionary keys and values.
"""
attrs = fields(inst.__class__)
rv = []
Expand Down Expand Up @@ -322,6 +326,7 @@ def astuple(
(
astuple(
kk,
filter=filter,
tuple_factory=tuple_factory,
retain_collection_types=retain,
)
Expand All @@ -331,6 +336,7 @@ def astuple(
(
astuple(
vv,
filter=filter,
tuple_factory=tuple_factory,
retain_collection_types=retain,
)
Expand Down
23 changes: 23 additions & 0 deletions tests/test_funcs.py
Original file line number Diff line number Diff line change
Expand Up @@ -409,6 +409,29 @@ def test_dicts(self, C, tuple_factory):
assert tuple_factory([1, {"a": tuple_factory([4, 5])}]) == res
assert isinstance(res, tuple_factory)

@pytest.mark.parametrize("retain", [False, True])
@pytest.mark.parametrize("container", MAPPING_TYPES)
def test_dicts_filter(self, C, retain, container):
"""
Filters apply to attrs instances used as dictionary keys and values.
"""

@attr.s(frozen=True)
class Key:
x = attr.ib()
y = attr.ib()

inst = C(container({Key(1, 2): C(3, 4)}), 5)

result = astuple(
inst,
filter=lambda a, v: a.name != "y",
retain_collection_types=retain,
)

assert ({(1,): (3,)},) == result
assert (container if retain else dict) is type(result[0])

@given(container=st.sampled_from(SEQUENCE_TYPES))
def test_lists_tuples_retain_type(self, container, C):
"""
Expand Down
Loading