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/0.change.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Improve `attrs.astuple()` performance for dictionary keys and values of exact atomic types by avoiding redundant attrs-class detection.
6 changes: 4 additions & 2 deletions src/attr/_funcs.py
Original file line number Diff line number Diff line change
Expand Up @@ -325,7 +325,8 @@ def astuple(
tuple_factory=tuple_factory,
retain_collection_types=retain,
)
if has(kk.__class__)
if type(kk) not in _ATOMIC_TYPES
and has(kk.__class__)
else kk
),
(
Expand All @@ -334,7 +335,8 @@ def astuple(
tuple_factory=tuple_factory,
retain_collection_types=retain,
)
if has(vv.__class__)
if type(vv) not in _ATOMIC_TYPES
and has(vv.__class__)
else vv
),
)
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_dict_atomic_and_subclass(self, C, retain, container):
"""Exact atomic keys/values and int subclasses retain behavior."""

class IntSub(int):
pass

mapping = container({"one": 1, "two": IntSub(2), "three": C(3, 4)})
result = astuple(C(mapping, 9), retain_collection_types=retain)
assert result == ({"one": 1, "two": 2, "three": (3, 4)}, 9)
assert type(result[0]) is (container if retain else dict)
assert type(result[0]["two"]) is IntSub

@pytest.mark.parametrize("retain", [False, True])
@pytest.mark.parametrize("container", MAPPING_TYPES)
def test_dict_atomic_keys(self, C, retain, container):
"""Dictionary atomic keys are preserved during conversion."""
mapping = container({1: "one", 2: "two"})
result = astuple(C(mapping, 9), retain_collection_types=retain)
assert result == (mapping, 9)
assert type(result[0]) is (container if retain else dict)

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