Skip to content

Unpack the single type arg for Counter keys when unstructuring - #768

Merged
Tinche merged 2 commits into
python-attrs:mainfrom
uttam12331:fix/counter-unstructure-key-arg
Aug 4, 2026
Merged

Unpack the single type arg for Counter keys when unstructuring#768
Tinche merged 2 commits into
python-attrs:mainfrom
uttam12331:fix/counter-unstructure-key-arg

Conversation

@uttam12331

Copy link
Copy Markdown
Contributor

Summary

mapping_unstructure_factory's single-type-arg branch (comment: "Probably a Counter") assigns the whole args tuple to the singular key_arg:

if len(args) == 2:
    key_arg, val_arg = args
else:
    # Probably a Counter
    key_arg, val_arg = args, Any          # <-- args, not args[0]
# ...
kh = key_handler or converter.get_unstructure_hook(key_arg, cache_result=False)

get_unstructure_hook expects a single type, but receives the 1-tuple (KeyType,), which resolves to the identity hook. So a Counter[KeyType] whose keys need real unstructuring has its keys left unchanged.

The parallel structure factory handles the identical case correctly by unpacking:

else:
    # Probably a Counter
    (key_type,) = args

Reproduction

from collections import Counter
from attrs import define
from cattrs import Converter

@define(frozen=True)
class Key:
    v: int

c = Converter()
c.register_unstructure_hook(Key, lambda k: k.v)
c.unstructure(Counter({Key(1): 5, Key(2): 3}), unstructure_as=Counter[Key])
# before: {Key(v=1): 5, Key(v=2): 3}   (keys not unstructured)
# after:  {1: 5, 2: 3}

Fix

-        key_arg, val_arg = args, Any
+        key_arg, val_arg = args[0], Any

Tests

Added test_counter_unstructure_applies_key_hook, which fails on the previous behavior (keys left un-unstructured) and passes with the fix. The existing Counter[int] tests are unaffected (integer keys use the identity hook either way).

`mapping_unstructure_factory`'s single-type-arg branch ("Probably a Counter")
assigned the whole `args` tuple to `key_arg`, then passed it to
`get_unstructure_hook`, which expects a single type. As a result the key hook
was resolved for `(KeyType,)` (falling back to identity) instead of
`KeyType`, so Counter keys that need real unstructuring were left unchanged.

Unpack `args[0]`, matching the structure factory's `(key_type,) = args`, and
add a regression test.
@codspeed-hq

codspeed-hq Bot commented Aug 4, 2026

Copy link
Copy Markdown

Merging this PR will not alter performance

✅ 64 untouched benchmarks


Comparing uttam12331:fix/counter-unstructure-key-arg (cca9472) with main (a6c0900)

Open in CodSpeed

@Tinche

Tinche commented Aug 4, 2026

Copy link
Copy Markdown
Member

Ooph, good catch!

@Tinche
Tinche merged commit 82b02c0 into python-attrs:main Aug 4, 2026
14 of 15 checks passed
@Tinche

Tinche commented Aug 4, 2026

Copy link
Copy Markdown
Member

Thanks!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants