Skip to content

Commit

Permalink
More msgspec work
Browse files Browse the repository at this point in the history
  • Loading branch information
Tinche committed Jan 11, 2024
1 parent 2333e72 commit b8578e2
Show file tree
Hide file tree
Showing 3 changed files with 30 additions and 0 deletions.
17 changes: 17 additions & 0 deletions docs/preconf.md
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,23 @@ Compatibility notes:
- _attrs_ classes, dataclasses and sequences are handled directly by _msgspec_ if possible, otherwise by the normal _cattrs_ machinery.
This means it's possible the validation errors produced may be _msgspec_ validation errors instead of _cattrs_ validation errors.

This converter supports {meth}`get_loads_hook() <cattrs.preconf.msgspec.MsgspecJsonConverter.get_loads_hook>` and {meth}`get_dumps_hook() <cattrs.preconf.msgspec.MsgspecJsonConverter.get_loads_hook>`.
These are factories for dumping and loading functions (as opposed to unstructuring and structuring); the hooks returned by this may be further optimized to offload as much work as possible to _msgspec_.

```python
>>> from cattrs.preconf.msgspec import make_converter

>>> @define
... class Test:
... a: int

>>> converter = make_converter()
>>> dumps = converter.get_dumps_hook(A)

>>> dumps(Test(1)) # Will use msgspec directly.
b'{"a":1}'
```

_msgspec_ doesn't support PyPy.

```{versionadded} 24.1.0
Expand Down
7 changes: 7 additions & 0 deletions src/cattrs/preconf/msgspec.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

from base64 import b64decode
from datetime import date, datetime
from functools import partial
from typing import Any, Callable, TypeVar, Union

from attrs import has as attrs_has
Expand All @@ -24,6 +25,8 @@


class MsgspecJsonConverter(Converter):
"""A converter specialized for the _msgspec_ library."""

#: The msgspec encoder for dumping.
encoder: Encoder = Encoder()

Expand All @@ -46,6 +49,10 @@ 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.
Expand Down
6 changes: 6 additions & 0 deletions tests/preconf/test_msgspec_cpython.py
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,12 @@ def test_dump_hook_attrs(converter: Conv):
assert converter.get_dumps_hook(A) == converter.encoder.encode


def test_get_loads_hook(converter: Conv):
"""`Converter.get_loads_hook` works."""
hook = converter.get_loads_hook(A)
assert hook(b'{"a": 1}') == A(1)


def test_basic_structs(converter: Conv):
"""Handling msgspec structs works."""

Expand Down

0 comments on commit b8578e2

Please sign in to comment.