Skip to content

Commit be25733

Browse files
committed
Fix preconf mapping optimizations
1 parent 8fe5373 commit be25733

File tree

11 files changed

+81
-54
lines changed

11 files changed

+81
-54
lines changed

HISTORY.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,8 @@ Our backwards-compatibility policy can be found [here](https://github.com/python
2727
([#598](https://github.com/python-attrs/cattrs/pull/598))
2828
- Literals containing enums are now unstructured properly, and their unstructuring is greatly optimized in the _bson_, stdlib JSON, _cbor2_, _msgpack_, _msgspec_, _orjson_ and _ujson_ preconf converters.
2929
([#598](https://github.com/python-attrs/cattrs/pull/598))
30+
- Preconf converters now handle dictionaries with literal keys properly.
31+
([#599](https://github.com/python-attrs/cattrs/pull/599))
3032
- Replace `cattrs.gen.MappingStructureFn` with `cattrs.SimpleStructureHook[In, T]`.
3133
- Python 3.13 is now supported.
3234
([#543](https://github.com/python-attrs/cattrs/pull/543) [#547](https://github.com/python-attrs/cattrs/issues/547))

src/cattrs/_compat.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -252,7 +252,6 @@ def is_literal(_) -> bool:
252252

253253

254254
Set = AbcSet
255-
AbstractSet = AbcSet
256255
MutableSet = AbcMutableSet
257256
Sequence = AbcSequence
258257
MutableSequence = AbcMutableSequence

src/cattrs/preconf/bson.py

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,14 @@
11
"""Preconfigured converters for bson."""
22

33
from base64 import b85decode, b85encode
4+
from collections.abc import Set
45
from datetime import date, datetime
56
from typing import Any, TypeVar, Union
67

78
from bson import DEFAULT_CODEC_OPTIONS, CodecOptions, Int64, ObjectId, decode, encode
89

9-
from cattrs._compat import AbstractSet, is_mapping
10-
from cattrs.gen import make_mapping_structure_fn
11-
10+
from .._compat import is_mapping, is_subclass
11+
from ..cols import mapping_structure_factory
1212
from ..converters import BaseConverter, Converter
1313
from ..dispatch import StructureHook
1414
from ..fns import identity
@@ -69,9 +69,9 @@ def gen_unstructure_mapping(cl: Any, unstructure_to=None):
6969
key_handler = str
7070
args = getattr(cl, "__args__", None)
7171
if args:
72-
if issubclass(args[0], str):
72+
if is_subclass(args[0], str):
7373
key_handler = None
74-
elif issubclass(args[0], bytes):
74+
elif is_subclass(args[0], bytes):
7575

7676
def key_handler(k):
7777
return b85encode(k).decode("utf8")
@@ -82,10 +82,10 @@ def key_handler(k):
8282

8383
def gen_structure_mapping(cl: Any) -> StructureHook:
8484
args = getattr(cl, "__args__", None)
85-
if args and issubclass(args[0], bytes):
86-
h = make_mapping_structure_fn(cl, converter, key_type=Base85Bytes)
85+
if args and is_subclass(args[0], bytes):
86+
h = mapping_structure_factory(cl, converter, key_type=Base85Bytes)
8787
else:
88-
h = make_mapping_structure_fn(cl, converter)
88+
h = mapping_structure_factory(cl, converter)
8989
return h
9090

9191
converter.register_structure_hook(Base85Bytes, lambda v, _: b85decode(v))
@@ -112,7 +112,7 @@ def gen_structure_mapping(cl: Any) -> StructureHook:
112112
@wrap(BsonConverter)
113113
def make_converter(*args: Any, **kwargs: Any) -> BsonConverter:
114114
kwargs["unstruct_collection_overrides"] = {
115-
AbstractSet: list,
115+
Set: list,
116116
**kwargs.get("unstruct_collection_overrides", {}),
117117
}
118118
res = BsonConverter(*args, **kwargs)

src/cattrs/preconf/cbor2.py

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,11 @@
11
"""Preconfigured converters for cbor2."""
22

3+
from collections.abc import Set
34
from datetime import date, datetime, timezone
45
from typing import Any, TypeVar, Union
56

67
from cbor2 import dumps, loads
78

8-
from cattrs._compat import AbstractSet
9-
109
from ..converters import BaseConverter, Converter
1110
from ..fns import identity
1211
from ..literals import is_literal_containing_enums
@@ -48,7 +47,7 @@ def configure_converter(converter: BaseConverter):
4847
@wrap(Cbor2Converter)
4948
def make_converter(*args: Any, **kwargs: Any) -> Cbor2Converter:
5049
kwargs["unstruct_collection_overrides"] = {
51-
AbstractSet: list,
50+
Set: list,
5251
**kwargs.get("unstruct_collection_overrides", {}),
5352
}
5453
res = Cbor2Converter(*args, **kwargs)

src/cattrs/preconf/json.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,12 @@
11
"""Preconfigured converters for the stdlib json."""
22

33
from base64 import b85decode, b85encode
4+
from collections.abc import Set
45
from datetime import date, datetime
56
from json import dumps, loads
67
from typing import Any, TypeVar, Union
78

8-
from .._compat import AbstractSet, Counter
9+
from .._compat import Counter
910
from ..converters import BaseConverter, Converter
1011
from ..fns import identity
1112
from ..literals import is_literal_containing_enums
@@ -56,7 +57,7 @@ def configure_converter(converter: BaseConverter):
5657
@wrap(JsonConverter)
5758
def make_converter(*args: Any, **kwargs: Any) -> JsonConverter:
5859
kwargs["unstruct_collection_overrides"] = {
59-
AbstractSet: list,
60+
Set: list,
6061
Counter: dict,
6162
**kwargs.get("unstruct_collection_overrides", {}),
6263
}

src/cattrs/preconf/msgpack.py

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,11 @@
11
"""Preconfigured converters for msgpack."""
22

3+
from collections.abc import Set
34
from datetime import date, datetime, time, timezone
45
from typing import Any, TypeVar, Union
56

67
from msgpack import dumps, loads
78

8-
from cattrs._compat import AbstractSet
9-
109
from ..converters import BaseConverter, Converter
1110
from ..fns import identity
1211
from ..literals import is_literal_containing_enums
@@ -55,7 +54,7 @@ def configure_converter(converter: BaseConverter):
5554
@wrap(MsgpackConverter)
5655
def make_converter(*args: Any, **kwargs: Any) -> MsgpackConverter:
5756
kwargs["unstruct_collection_overrides"] = {
58-
AbstractSet: list,
57+
Set: list,
5958
**kwargs.get("unstruct_collection_overrides", {}),
6059
}
6160
res = MsgpackConverter(*args, **kwargs)

src/cattrs/preconf/orjson.py

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,16 @@
11
"""Preconfigured converters for orjson."""
22

33
from base64 import b85decode, b85encode
4+
from collections.abc import Set
45
from datetime import date, datetime
56
from enum import Enum
67
from functools import partial
78
from typing import Any, TypeVar, Union
89

910
from orjson import dumps, loads
1011

11-
from .._compat import AbstractSet, is_mapping
12-
from ..cols import is_namedtuple, namedtuple_unstructure_factory
12+
from .._compat import is_subclass
13+
from ..cols import is_mapping, is_namedtuple, namedtuple_unstructure_factory
1314
from ..converters import BaseConverter, Converter
1415
from ..fns import identity
1516
from ..literals import is_literal_containing_enums
@@ -56,7 +57,7 @@ def gen_unstructure_mapping(cl: Any, unstructure_to=None):
5657
key_handler = str
5758
args = getattr(cl, "__args__", None)
5859
if args:
59-
if issubclass(args[0], str) and issubclass(args[0], Enum):
60+
if is_subclass(args[0], str) and is_subclass(args[0], Enum):
6061

6162
def key_handler(v):
6263
return v.value
@@ -96,7 +97,7 @@ def key_handler(v):
9697
@wrap(OrjsonConverter)
9798
def make_converter(*args: Any, **kwargs: Any) -> OrjsonConverter:
9899
kwargs["unstruct_collection_overrides"] = {
99-
AbstractSet: list,
100+
Set: list,
100101
**kwargs.get("unstruct_collection_overrides", {}),
101102
}
102103
res = OrjsonConverter(*args, **kwargs)

src/cattrs/preconf/tomlkit.py

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
"""Preconfigured converters for tomlkit."""
22

33
from base64 import b85decode, b85encode
4+
from collections.abc import Set
45
from datetime import date, datetime
56
from enum import Enum
67
from operator import attrgetter
@@ -9,8 +10,7 @@
910
from tomlkit import dumps, loads
1011
from tomlkit.items import Float, Integer, String
1112

12-
from cattrs._compat import AbstractSet, is_mapping
13-
13+
from .._compat import is_mapping, is_subclass
1414
from ..converters import BaseConverter, Converter
1515
from ..strategies import configure_union_passthrough
1616
from . import validate_datetime, wrap
@@ -48,9 +48,9 @@ def gen_unstructure_mapping(cl: Any, unstructure_to=None):
4848
# Currently, tomlkit has inconsistent behavior on 3.11
4949
# so we paper over it here.
5050
# https://github.com/sdispater/tomlkit/issues/237
51-
if issubclass(args[0], str):
52-
key_handler = _enum_value_getter if issubclass(args[0], Enum) else None
53-
elif issubclass(args[0], bytes):
51+
if is_subclass(args[0], str):
52+
key_handler = _enum_value_getter if is_subclass(args[0], Enum) else None
53+
elif is_subclass(args[0], bytes):
5454

5555
def key_handler(k: bytes):
5656
return b85encode(k).decode("utf8")
@@ -77,7 +77,7 @@ def key_handler(k: bytes):
7777
@wrap(TomlkitConverter)
7878
def make_converter(*args: Any, **kwargs: Any) -> TomlkitConverter:
7979
kwargs["unstruct_collection_overrides"] = {
80-
AbstractSet: list,
80+
Set: list,
8181
tuple: list,
8282
**kwargs.get("unstruct_collection_overrides", {}),
8383
}

src/cattrs/preconf/ujson.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
11
"""Preconfigured converters for ujson."""
22

33
from base64 import b85decode, b85encode
4+
from collections.abc import Set
45
from datetime import date, datetime
56
from typing import Any, AnyStr, TypeVar, Union
67

78
from ujson import dumps, loads
89

9-
from .._compat import AbstractSet
1010
from ..converters import BaseConverter, Converter
1111
from ..fns import identity
1212
from ..literals import is_literal_containing_enums
@@ -55,7 +55,7 @@ def configure_converter(converter: BaseConverter):
5555
@wrap(UjsonConverter)
5656
def make_converter(*args: Any, **kwargs: Any) -> UjsonConverter:
5757
kwargs["unstruct_collection_overrides"] = {
58-
AbstractSet: list,
58+
Set: list,
5959
**kwargs.get("unstruct_collection_overrides", {}),
6060
}
6161
res = UjsonConverter(*args, **kwargs)

tests/test_cols.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,12 @@
11
"""Tests for the `cattrs.cols` module."""
22

3+
from collections.abc import Set
34
from typing import Dict
45

56
from immutables import Map
67

78
from cattrs import BaseConverter, Converter
8-
from cattrs._compat import AbstractSet, FrozenSet
9+
from cattrs._compat import FrozenSet
910
from cattrs.cols import (
1011
is_any_set,
1112
iterable_unstructure_factory,
@@ -23,7 +24,7 @@ def test_set_overriding(converter: BaseConverter):
2324
lambda t, c: iterable_unstructure_factory(t, c, unstructure_to=sorted),
2425
)
2526

26-
assert converter.unstructure({"c", "b", "a"}, AbstractSet[str]) == ["a", "b", "c"]
27+
assert converter.unstructure({"c", "b", "a"}, Set[str]) == ["a", "b", "c"]
2728
assert converter.unstructure(frozenset(["c", "b", "a"]), FrozenSet[str]) == [
2829
"a",
2930
"b",

0 commit comments

Comments
 (0)