automated terminal push

This commit is contained in:
lenape
2025-06-27 16:06:02 +00:00
parent 511dd3b36b
commit 6a954eb013
4221 changed files with 2916190 additions and 1 deletions

View File

@@ -0,0 +1,27 @@
import sys
from datetime import datetime
from typing import Any, Callable, TypeVar
if sys.version_info[:2] < (3, 10):
from typing_extensions import ParamSpec
else:
from typing import ParamSpec
def validate_datetime(v, _):
if not isinstance(v, datetime):
raise Exception(f"Expected datetime, got {v}")
return v
T = TypeVar("T")
P = ParamSpec("P")
def wrap(_: Callable[P, Any]) -> Callable[[Callable[..., T]], Callable[P, T]]:
"""Wrap a `Converter` `__init__` in a type-safe way."""
def impl(x: Callable[..., T]) -> Callable[P, T]:
return x
return impl

View File

@@ -0,0 +1,106 @@
"""Preconfigured converters for bson."""
from base64 import b85decode, b85encode
from datetime import date, datetime
from typing import Any, Type, TypeVar, Union
from bson import DEFAULT_CODEC_OPTIONS, CodecOptions, Int64, ObjectId, decode, encode
from cattrs._compat import AbstractSet, is_mapping
from cattrs.gen import make_mapping_structure_fn
from ..converters import BaseConverter, Converter
from ..dispatch import StructureHook
from ..strategies import configure_union_passthrough
from . import validate_datetime, wrap
T = TypeVar("T")
class Base85Bytes(bytes):
"""A subclass to help with binary key encoding/decoding."""
class BsonConverter(Converter):
def dumps(
self,
obj: Any,
unstructure_as: Any = None,
check_keys: bool = False,
codec_options: CodecOptions = DEFAULT_CODEC_OPTIONS,
) -> bytes:
return encode(
self.unstructure(obj, unstructure_as=unstructure_as),
check_keys=check_keys,
codec_options=codec_options,
)
def loads(
self,
data: bytes,
cl: Type[T],
codec_options: CodecOptions = DEFAULT_CODEC_OPTIONS,
) -> T:
return self.structure(decode(data, codec_options=codec_options), cl)
def configure_converter(converter: BaseConverter):
"""
Configure the converter for use with the bson library.
* sets are serialized as lists
* byte mapping keys are base85-encoded into strings when unstructuring, and reverse
* non-string, non-byte mapping keys are coerced into strings when unstructuring
* a deserialization hook is registered for bson.ObjectId by default
"""
def gen_unstructure_mapping(cl: Any, unstructure_to=None):
key_handler = str
args = getattr(cl, "__args__", None)
if args:
if issubclass(args[0], str):
key_handler = None
elif issubclass(args[0], bytes):
def key_handler(k):
return b85encode(k).decode("utf8")
return converter.gen_unstructure_mapping(
cl, unstructure_to=unstructure_to, key_handler=key_handler
)
def gen_structure_mapping(cl: Any) -> StructureHook:
args = getattr(cl, "__args__", None)
if args and issubclass(args[0], bytes):
h = make_mapping_structure_fn(cl, converter, key_type=Base85Bytes)
else:
h = make_mapping_structure_fn(cl, converter)
return h
converter.register_structure_hook(Base85Bytes, lambda v, _: b85decode(v))
converter.register_unstructure_hook_factory(is_mapping, gen_unstructure_mapping)
converter.register_structure_hook_factory(is_mapping, gen_structure_mapping)
converter.register_structure_hook(ObjectId, lambda v, _: ObjectId(v))
configure_union_passthrough(
Union[str, bool, int, float, None, bytes, datetime, ObjectId, Int64], converter
)
# datetime inherits from date, so identity unstructure hook used
# here to prevent the date unstructure hook running.
converter.register_unstructure_hook(datetime, lambda v: v)
converter.register_structure_hook(datetime, validate_datetime)
converter.register_unstructure_hook(date, lambda v: v.isoformat())
converter.register_structure_hook(date, lambda v, _: date.fromisoformat(v))
@wrap(BsonConverter)
def make_converter(*args: Any, **kwargs: Any) -> BsonConverter:
kwargs["unstruct_collection_overrides"] = {
AbstractSet: list,
**kwargs.get("unstruct_collection_overrides", {}),
}
res = BsonConverter(*args, **kwargs)
configure_converter(res)
return res

View File

@@ -0,0 +1,50 @@
"""Preconfigured converters for cbor2."""
from datetime import date, datetime, timezone
from typing import Any, Type, TypeVar, Union
from cbor2 import dumps, loads
from cattrs._compat import AbstractSet
from ..converters import BaseConverter, Converter
from ..strategies import configure_union_passthrough
from . import wrap
T = TypeVar("T")
class Cbor2Converter(Converter):
def dumps(self, obj: Any, unstructure_as: Any = None, **kwargs: Any) -> bytes:
return dumps(self.unstructure(obj, unstructure_as=unstructure_as), **kwargs)
def loads(self, data: bytes, cl: Type[T], **kwargs: Any) -> T:
return self.structure(loads(data, **kwargs), cl)
def configure_converter(converter: BaseConverter):
"""
Configure the converter for use with the cbor2 library.
* datetimes are serialized as timestamp floats
* sets are serialized as lists
"""
converter.register_unstructure_hook(datetime, lambda v: v.timestamp())
converter.register_structure_hook(
datetime, lambda v, _: datetime.fromtimestamp(v, timezone.utc)
)
converter.register_unstructure_hook(date, lambda v: v.isoformat())
converter.register_structure_hook(date, lambda v, _: date.fromisoformat(v))
configure_union_passthrough(Union[str, bool, int, float, None, bytes], converter)
@wrap(Cbor2Converter)
def make_converter(*args: Any, **kwargs: Any) -> Cbor2Converter:
kwargs["unstruct_collection_overrides"] = {
AbstractSet: list,
**kwargs.get("unstruct_collection_overrides", {}),
}
res = Cbor2Converter(*args, **kwargs)
configure_converter(res)
return res

View File

@@ -0,0 +1,56 @@
"""Preconfigured converters for the stdlib json."""
from base64 import b85decode, b85encode
from datetime import date, datetime
from json import dumps, loads
from typing import Any, Type, TypeVar, Union
from .._compat import AbstractSet, Counter
from ..converters import BaseConverter, Converter
from ..strategies import configure_union_passthrough
from . import wrap
T = TypeVar("T")
class JsonConverter(Converter):
def dumps(self, obj: Any, unstructure_as: Any = None, **kwargs: Any) -> str:
return dumps(self.unstructure(obj, unstructure_as=unstructure_as), **kwargs)
def loads(self, data: Union[bytes, str], cl: Type[T], **kwargs: Any) -> T:
return self.structure(loads(data, **kwargs), cl)
def configure_converter(converter: BaseConverter):
"""
Configure the converter for use with the stdlib json module.
* bytes are serialized as base85 strings
* datetimes are serialized as ISO 8601
* counters are serialized as dicts
* sets are serialized as lists
* union passthrough is configured for unions of strings, bools, ints,
floats and None
"""
converter.register_unstructure_hook(
bytes, lambda v: (b85encode(v) if v else b"").decode("utf8")
)
converter.register_structure_hook(bytes, lambda v, _: b85decode(v))
converter.register_unstructure_hook(datetime, lambda v: v.isoformat())
converter.register_structure_hook(datetime, lambda v, _: datetime.fromisoformat(v))
converter.register_unstructure_hook(date, lambda v: v.isoformat())
converter.register_structure_hook(date, lambda v, _: date.fromisoformat(v))
configure_union_passthrough(Union[str, bool, int, float, None], converter)
@wrap(JsonConverter)
def make_converter(*args: Any, **kwargs: Any) -> JsonConverter:
kwargs["unstruct_collection_overrides"] = {
AbstractSet: list,
Counter: dict,
**kwargs.get("unstruct_collection_overrides", {}),
}
res = JsonConverter(*args, **kwargs)
configure_converter(res)
return res

View File

@@ -0,0 +1,54 @@
"""Preconfigured converters for msgpack."""
from datetime import date, datetime, time, timezone
from typing import Any, Type, TypeVar, Union
from msgpack import dumps, loads
from cattrs._compat import AbstractSet
from ..converters import BaseConverter, Converter
from ..strategies import configure_union_passthrough
from . import wrap
T = TypeVar("T")
class MsgpackConverter(Converter):
def dumps(self, obj: Any, unstructure_as: Any = None, **kwargs: Any) -> bytes:
return dumps(self.unstructure(obj, unstructure_as=unstructure_as), **kwargs)
def loads(self, data: bytes, cl: Type[T], **kwargs: Any) -> T:
return self.structure(loads(data, **kwargs), cl)
def configure_converter(converter: BaseConverter):
"""
Configure the converter for use with the msgpack library.
* datetimes are serialized as timestamp floats
* sets are serialized as lists
"""
converter.register_unstructure_hook(datetime, lambda v: v.timestamp())
converter.register_structure_hook(
datetime, lambda v, _: datetime.fromtimestamp(v, timezone.utc)
)
converter.register_unstructure_hook(
date, lambda v: datetime.combine(v, time(tzinfo=timezone.utc)).timestamp()
)
converter.register_structure_hook(
date, lambda v, _: datetime.fromtimestamp(v, timezone.utc).date()
)
configure_union_passthrough(Union[str, bool, int, float, None, bytes], converter)
@wrap(MsgpackConverter)
def make_converter(*args: Any, **kwargs: Any) -> MsgpackConverter:
kwargs["unstruct_collection_overrides"] = {
AbstractSet: list,
**kwargs.get("unstruct_collection_overrides", {}),
}
res = MsgpackConverter(*args, **kwargs)
configure_converter(res)
return res

View File

@@ -0,0 +1,185 @@
"""Preconfigured converters for msgspec."""
from __future__ import annotations
from base64 import b64decode
from datetime import date, datetime
from enum import Enum
from functools import partial
from typing import Any, Callable, TypeVar, Union, get_type_hints
from attrs import has as attrs_has
from attrs import resolve_types
from msgspec import Struct, convert, to_builtins
from msgspec.json import Encoder, decode
from .._compat import (
fields,
get_args,
get_origin,
has,
is_bare,
is_mapping,
is_sequence,
)
from ..cols import is_namedtuple
from ..converters import BaseConverter, Converter
from ..dispatch import UnstructureHook
from ..fns import identity
from ..gen import make_hetero_tuple_unstructure_fn
from ..strategies import configure_union_passthrough
from . import wrap
T = TypeVar("T")
__all__ = ["MsgspecJsonConverter", "configure_converter", "make_converter"]
class MsgspecJsonConverter(Converter):
"""A converter specialized for the _msgspec_ library."""
#: The msgspec encoder for dumping.
encoder: Encoder = Encoder()
def dumps(self, obj: Any, unstructure_as: Any = None, **kwargs: Any) -> bytes:
"""Unstructure and encode `obj` into JSON bytes."""
return self.encoder.encode(
self.unstructure(obj, unstructure_as=unstructure_as), **kwargs
)
def get_dumps_hook(
self, unstructure_as: Any, **kwargs: Any
) -> Callable[[Any], bytes]:
"""Produce a `dumps` hook for the given type."""
unstruct_hook = self.get_unstructure_hook(unstructure_as)
if unstruct_hook in (identity, to_builtins):
return self.encoder.encode
return self.dumps
def loads(self, data: bytes, cl: type[T], **kwargs: Any) -> T:
"""Decode and structure `cl` from the provided JSON bytes."""
return self.structure(decode(data, **kwargs), cl)
def get_loads_hook(self, cl: type[T]) -> Callable[[bytes], T]:
"""Produce a `loads` hook for the given type."""
return partial(self.loads, cl=cl)
def configure_converter(converter: Converter) -> None:
"""Configure the converter for the msgspec library.
* bytes are serialized as base64 strings, directly by msgspec
* datetimes and dates are passed through to be serialized as RFC 3339 directly
* enums are passed through to msgspec directly
* union passthrough configured for str, bool, int, float and None
"""
configure_passthroughs(converter)
converter.register_unstructure_hook(Struct, to_builtins)
converter.register_unstructure_hook(Enum, to_builtins)
converter.register_structure_hook(Struct, convert)
converter.register_structure_hook(bytes, lambda v, _: b64decode(v))
converter.register_structure_hook(datetime, lambda v, _: convert(v, datetime))
converter.register_structure_hook(date, lambda v, _: date.fromisoformat(v))
configure_union_passthrough(Union[str, bool, int, float, None], converter)
@wrap(MsgspecJsonConverter)
def make_converter(*args: Any, **kwargs: Any) -> MsgspecJsonConverter:
res = MsgspecJsonConverter(*args, **kwargs)
configure_converter(res)
return res
def configure_passthroughs(converter: Converter) -> None:
"""Configure optimizing passthroughs.
A passthrough is when we let msgspec handle something automatically.
"""
converter.register_unstructure_hook(bytes, to_builtins)
converter.register_unstructure_hook_factory(is_mapping, mapping_unstructure_factory)
converter.register_unstructure_hook_factory(is_sequence, seq_unstructure_factory)
converter.register_unstructure_hook_factory(has, attrs_unstructure_factory)
converter.register_unstructure_hook_factory(
is_namedtuple, namedtuple_unstructure_factory
)
def seq_unstructure_factory(type, converter: Converter) -> UnstructureHook:
"""The msgspec unstructure hook factory for sequences."""
if is_bare(type):
type_arg = Any
else:
args = get_args(type)
type_arg = args[0]
handler = converter.get_unstructure_hook(type_arg, cache_result=False)
if handler in (identity, to_builtins):
return handler
return converter.gen_unstructure_iterable(type)
def mapping_unstructure_factory(type, converter: BaseConverter) -> UnstructureHook:
"""The msgspec unstructure hook factory for mappings."""
if is_bare(type):
key_arg = Any
val_arg = Any
key_handler = converter.get_unstructure_hook(key_arg, cache_result=False)
value_handler = converter.get_unstructure_hook(val_arg, cache_result=False)
else:
args = get_args(type)
if len(args) == 2:
key_arg, val_arg = args
else:
# Probably a Counter
key_arg, val_arg = args, Any
key_handler = converter.get_unstructure_hook(key_arg, cache_result=False)
value_handler = converter.get_unstructure_hook(val_arg, cache_result=False)
if key_handler in (identity, to_builtins) and value_handler in (
identity,
to_builtins,
):
return to_builtins
return converter.gen_unstructure_mapping(type)
def attrs_unstructure_factory(type: Any, converter: Converter) -> UnstructureHook:
"""Choose whether to use msgspec handling or our own."""
origin = get_origin(type)
attribs = fields(origin or type)
if attrs_has(type) and any(isinstance(a.type, str) for a in attribs):
resolve_types(type)
attribs = fields(origin or type)
if any(
attr.name.startswith("_")
or (
converter.get_unstructure_hook(attr.type, cache_result=False)
not in (identity, to_builtins)
)
for attr in attribs
):
return converter.gen_unstructure_attrs_fromdict(type)
return to_builtins
def namedtuple_unstructure_factory(
type: type[tuple], converter: BaseConverter
) -> UnstructureHook:
"""A hook factory for unstructuring namedtuples, modified for msgspec."""
if all(
converter.get_unstructure_hook(t) in (identity, to_builtins)
for t in get_type_hints(type).values()
):
return identity
return make_hetero_tuple_unstructure_fn(
type,
converter,
unstructure_to=tuple,
type_args=tuple(get_type_hints(type).values()),
)

View File

@@ -0,0 +1,95 @@
"""Preconfigured converters for orjson."""
from base64 import b85decode, b85encode
from datetime import date, datetime
from enum import Enum
from functools import partial
from typing import Any, Type, TypeVar, Union
from orjson import dumps, loads
from .._compat import AbstractSet, is_mapping
from ..cols import is_namedtuple, namedtuple_unstructure_factory
from ..converters import BaseConverter, Converter
from ..fns import identity
from ..strategies import configure_union_passthrough
from . import wrap
T = TypeVar("T")
class OrjsonConverter(Converter):
def dumps(self, obj: Any, unstructure_as: Any = None, **kwargs: Any) -> bytes:
return dumps(self.unstructure(obj, unstructure_as=unstructure_as), **kwargs)
def loads(self, data: Union[bytes, bytearray, memoryview, str], cl: Type[T]) -> T:
return self.structure(loads(data), cl)
def configure_converter(converter: BaseConverter):
"""
Configure the converter for use with the orjson library.
* bytes are serialized as base85 strings
* datetimes and dates are passed through to be serialized as RFC 3339 by orjson
* typed namedtuples are serialized as lists
* sets are serialized as lists
* string enum mapping keys have special handling
* mapping keys are coerced into strings when unstructuring
.. versionchanged: 24.1.0
Add support for typed namedtuples.
"""
converter.register_unstructure_hook(
bytes, lambda v: (b85encode(v) if v else b"").decode("utf8")
)
converter.register_structure_hook(bytes, lambda v, _: b85decode(v))
converter.register_structure_hook(datetime, lambda v, _: datetime.fromisoformat(v))
converter.register_structure_hook(date, lambda v, _: date.fromisoformat(v))
def gen_unstructure_mapping(cl: Any, unstructure_to=None):
key_handler = str
args = getattr(cl, "__args__", None)
if args:
if issubclass(args[0], str) and issubclass(args[0], Enum):
def key_handler(v):
return v.value
else:
# It's possible the handler for the key type has been overridden.
# (For example base85 encoding for bytes.)
# In that case, we want to use the override.
kh = converter.get_unstructure_hook(args[0])
if kh != identity:
key_handler = kh
return converter.gen_unstructure_mapping(
cl, unstructure_to=unstructure_to, key_handler=key_handler
)
converter._unstructure_func.register_func_list(
[
(is_mapping, gen_unstructure_mapping, True),
(
is_namedtuple,
partial(namedtuple_unstructure_factory, unstructure_to=tuple),
"extended",
),
]
)
configure_union_passthrough(Union[str, bool, int, float, None], converter)
@wrap(OrjsonConverter)
def make_converter(*args: Any, **kwargs: Any) -> OrjsonConverter:
kwargs["unstruct_collection_overrides"] = {
AbstractSet: list,
**kwargs.get("unstruct_collection_overrides", {}),
}
res = OrjsonConverter(*args, **kwargs)
configure_converter(res)
return res

View File

@@ -0,0 +1,72 @@
"""Preconfigured converters for pyyaml."""
from datetime import date, datetime
from functools import partial
from typing import Any, Type, TypeVar, Union
from yaml import safe_dump, safe_load
from .._compat import FrozenSetSubscriptable
from ..cols import is_namedtuple, namedtuple_unstructure_factory
from ..converters import BaseConverter, Converter
from ..strategies import configure_union_passthrough
from . import validate_datetime, wrap
T = TypeVar("T")
def validate_date(v, _):
if not isinstance(v, date):
raise ValueError(f"Expected date, got {v}")
return v
class PyyamlConverter(Converter):
def dumps(self, obj: Any, unstructure_as: Any = None, **kwargs: Any) -> str:
return safe_dump(self.unstructure(obj, unstructure_as=unstructure_as), **kwargs)
def loads(self, data: str, cl: Type[T]) -> T:
return self.structure(safe_load(data), cl)
def configure_converter(converter: BaseConverter):
"""
Configure the converter for use with the pyyaml library.
* frozensets are serialized as lists
* string enums are converted into strings explicitly
* datetimes and dates are validated
* typed namedtuples are serialized as lists
.. versionchanged: 24.1.0
Add support for typed namedtuples.
"""
converter.register_unstructure_hook(
str, lambda v: v if v.__class__ is str else v.value
)
# datetime inherits from date, so identity unstructure hook used
# here to prevent the date unstructure hook running.
converter.register_unstructure_hook(datetime, lambda v: v)
converter.register_structure_hook(datetime, validate_datetime)
converter.register_structure_hook(date, validate_date)
converter.register_unstructure_hook_factory(is_namedtuple)(
partial(namedtuple_unstructure_factory, unstructure_to=tuple)
)
configure_union_passthrough(
Union[str, bool, int, float, None, bytes, datetime, date], converter
)
@wrap(PyyamlConverter)
def make_converter(*args: Any, **kwargs: Any) -> PyyamlConverter:
kwargs["unstruct_collection_overrides"] = {
FrozenSetSubscriptable: list,
**kwargs.get("unstruct_collection_overrides", {}),
}
res = PyyamlConverter(*args, **kwargs)
configure_converter(res)
return res

View File

@@ -0,0 +1,87 @@
"""Preconfigured converters for tomlkit."""
from base64 import b85decode, b85encode
from datetime import date, datetime
from enum import Enum
from operator import attrgetter
from typing import Any, Type, TypeVar, Union
from tomlkit import dumps, loads
from tomlkit.items import Float, Integer, String
from cattrs._compat import AbstractSet, is_mapping
from ..converters import BaseConverter, Converter
from ..strategies import configure_union_passthrough
from . import validate_datetime, wrap
T = TypeVar("T")
_enum_value_getter = attrgetter("_value_")
class TomlkitConverter(Converter):
def dumps(self, obj: Any, unstructure_as: Any = None, **kwargs: Any) -> str:
return dumps(self.unstructure(obj, unstructure_as=unstructure_as), **kwargs)
def loads(self, data: str, cl: Type[T]) -> T:
return self.structure(loads(data), cl)
def configure_converter(converter: BaseConverter):
"""
Configure the converter for use with the tomlkit library.
* bytes are serialized as base85 strings
* sets are serialized as lists
* tuples are serializas as lists
* mapping keys are coerced into strings when unstructuring
"""
converter.register_structure_hook(bytes, lambda v, _: b85decode(v))
converter.register_unstructure_hook(
bytes, lambda v: (b85encode(v) if v else b"").decode("utf8")
)
def gen_unstructure_mapping(cl: Any, unstructure_to=None):
key_handler = str
args = getattr(cl, "__args__", None)
if args:
# Currently, tomlkit has inconsistent behavior on 3.11
# so we paper over it here.
# https://github.com/sdispater/tomlkit/issues/237
if issubclass(args[0], str):
key_handler = _enum_value_getter if issubclass(args[0], Enum) else None
elif issubclass(args[0], bytes):
def key_handler(k: bytes):
return b85encode(k).decode("utf8")
return converter.gen_unstructure_mapping(
cl, unstructure_to=unstructure_to, key_handler=key_handler
)
converter._unstructure_func.register_func_list(
[(is_mapping, gen_unstructure_mapping, True)]
)
# datetime inherits from date, so identity unstructure hook used
# here to prevent the date unstructure hook running.
converter.register_unstructure_hook(datetime, lambda v: v)
converter.register_structure_hook(datetime, validate_datetime)
converter.register_unstructure_hook(date, lambda v: v.isoformat())
converter.register_structure_hook(date, lambda v, _: date.fromisoformat(v))
configure_union_passthrough(
Union[str, String, bool, int, Integer, float, Float], converter
)
@wrap(TomlkitConverter)
def make_converter(*args: Any, **kwargs: Any) -> TomlkitConverter:
kwargs["unstruct_collection_overrides"] = {
AbstractSet: list,
tuple: list,
**kwargs.get("unstruct_collection_overrides", {}),
}
res = TomlkitConverter(*args, **kwargs)
configure_converter(res)
return res

View File

@@ -0,0 +1,55 @@
"""Preconfigured converters for ujson."""
from base64 import b85decode, b85encode
from datetime import date, datetime
from typing import Any, AnyStr, Type, TypeVar, Union
from ujson import dumps, loads
from cattrs._compat import AbstractSet
from ..converters import BaseConverter, Converter
from ..strategies import configure_union_passthrough
from . import wrap
T = TypeVar("T")
class UjsonConverter(Converter):
def dumps(self, obj: Any, unstructure_as: Any = None, **kwargs: Any) -> str:
return dumps(self.unstructure(obj, unstructure_as=unstructure_as), **kwargs)
def loads(self, data: AnyStr, cl: Type[T], **kwargs: Any) -> T:
return self.structure(loads(data, **kwargs), cl)
def configure_converter(converter: BaseConverter):
"""
Configure the converter for use with the ujson library.
* bytes are serialized as base64 strings
* datetimes are serialized as ISO 8601
* sets are serialized as lists
"""
converter.register_unstructure_hook(
bytes, lambda v: (b85encode(v) if v else b"").decode("utf8")
)
converter.register_structure_hook(bytes, lambda v, _: b85decode(v))
converter.register_unstructure_hook(datetime, lambda v: v.isoformat())
converter.register_structure_hook(datetime, lambda v, _: datetime.fromisoformat(v))
converter.register_unstructure_hook(date, lambda v: v.isoformat())
converter.register_structure_hook(date, lambda v, _: date.fromisoformat(v))
configure_union_passthrough(Union[str, bool, int, float, None], converter)
@wrap(UjsonConverter)
def make_converter(*args: Any, **kwargs: Any) -> UjsonConverter:
kwargs["unstruct_collection_overrides"] = {
AbstractSet: list,
**kwargs.get("unstruct_collection_overrides", {}),
}
res = UjsonConverter(*args, **kwargs)
configure_converter(res)
return res