Skip to content

Commit 1315e42

Browse files
eendebakptpre-commit-ci[bot]hynek
authored
Improve performance of attr.astuple (#1469)
* Improve performance of attr.astuple * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Add news fragment --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: Hynek Schlawack <[email protected]>
1 parent 6ef8d73 commit 1315e42

File tree

3 files changed

+25
-4
lines changed

3 files changed

+25
-4
lines changed

changelog.d/1469.change.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
The performance of `attrs.astuple()` has been improved by 49–270%.

src/attr/_funcs.py

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -278,8 +278,11 @@ def astuple(
278278
v = getattr(inst, a.name)
279279
if filter is not None and not filter(a, v):
280280
continue
281+
value_type = type(v)
281282
if recurse is True:
282-
if has(v.__class__):
283+
if value_type in _ATOMIC_TYPES:
284+
rv.append(v)
285+
elif has(value_type):
283286
rv.append(
284287
astuple(
285288
v,
@@ -289,7 +292,7 @@ def astuple(
289292
retain_collection_types=retain,
290293
)
291294
)
292-
elif isinstance(v, (tuple, list, set, frozenset)):
295+
elif issubclass(value_type, (tuple, list, set, frozenset)):
293296
cf = v.__class__ if retain is True else list
294297
items = [
295298
(
@@ -313,8 +316,8 @@ def astuple(
313316
# Workaround for TypeError: cf.__new__() missing 1 required
314317
# positional argument (which appears, for a namedturle)
315318
rv.append(cf(*items))
316-
elif isinstance(v, dict):
317-
df = v.__class__ if retain is True else dict
319+
elif issubclass(value_type, dict):
320+
df = value_type if retain is True else dict
318321
rv.append(
319322
df(
320323
(

tests/test_funcs.py

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -499,6 +499,23 @@ class A:
499499
with pytest.raises(TypeError, match=re.escape(message)):
500500
attr.astuple(instance, retain_collection_types=True)
501501

502+
def test_non_atomic_types(self, C):
503+
"""
504+
Non-atomic types that don't have special treatment for are serialized
505+
and the types are retained.
506+
"""
507+
508+
class Int(int):
509+
pass
510+
511+
c = C(Int(10), [Int(1), 2])
512+
expected = (Int(10), [Int(1), 2])
513+
514+
result = astuple(c)
515+
assert expected == result
516+
assert type(result[0]) is Int
517+
assert type(result[1][0]) is Int
518+
502519

503520
class TestHas:
504521
"""

0 commit comments

Comments
 (0)