|
3 | 3 | from __future__ import annotations |
4 | 4 |
|
5 | 5 | from base64 import b64decode |
| 6 | +from dataclasses import is_dataclass |
6 | 7 | from datetime import date, datetime |
7 | 8 | from enum import Enum |
8 | 9 | from functools import partial |
|
13 | 14 | from msgspec import Struct, convert, to_builtins |
14 | 15 | from msgspec.json import Encoder, decode |
15 | 16 |
|
16 | | -from .._compat import ( |
17 | | - fields, |
18 | | - get_args, |
19 | | - get_origin, |
20 | | - has, |
21 | | - is_bare, |
22 | | - is_mapping, |
23 | | - is_sequence, |
24 | | -) |
| 17 | +from .._compat import fields, get_args, get_origin, is_bare, is_mapping, is_sequence |
25 | 18 | from ..cols import is_namedtuple |
26 | 19 | from ..converters import BaseConverter, Converter |
27 | 20 | from ..dispatch import UnstructureHook |
@@ -104,11 +97,20 @@ def configure_passthroughs(converter: Converter) -> None: |
104 | 97 | """Configure optimizing passthroughs. |
105 | 98 |
|
106 | 99 | A passthrough is when we let msgspec handle something automatically. |
| 100 | +
|
| 101 | + .. versionchanged:: 25.1.0 |
| 102 | + Dataclasses with private attributes are now passed through. |
107 | 103 | """ |
108 | 104 | converter.register_unstructure_hook(bytes, to_builtins) |
109 | 105 | converter.register_unstructure_hook_factory(is_mapping, mapping_unstructure_factory) |
110 | 106 | converter.register_unstructure_hook_factory(is_sequence, seq_unstructure_factory) |
111 | | - converter.register_unstructure_hook_factory(has, msgspec_attrs_unstructure_factory) |
| 107 | + converter.register_unstructure_hook_factory( |
| 108 | + attrs_has, msgspec_attrs_unstructure_factory |
| 109 | + ) |
| 110 | + converter.register_unstructure_hook_factory( |
| 111 | + is_dataclass, |
| 112 | + partial(msgspec_attrs_unstructure_factory, msgspec_skips_private=False), |
| 113 | + ) |
112 | 114 | converter.register_unstructure_hook_factory( |
113 | 115 | is_namedtuple, namedtuple_unstructure_factory |
114 | 116 | ) |
@@ -154,16 +156,21 @@ def mapping_unstructure_factory(type, converter: BaseConverter) -> UnstructureHo |
154 | 156 |
|
155 | 157 |
|
156 | 158 | def msgspec_attrs_unstructure_factory( |
157 | | - type: Any, converter: Converter |
| 159 | + type: Any, converter: Converter, msgspec_skips_private: bool = True |
158 | 160 | ) -> UnstructureHook: |
159 | | - """Choose whether to use msgspec handling or our own.""" |
| 161 | + """Choose whether to use msgspec handling or our own. |
| 162 | +
|
| 163 | + Args: |
| 164 | + msgspec_skips_private: Whether the msgspec library skips unstructuring |
| 165 | + private attributes, making us do the work. |
| 166 | + """ |
160 | 167 | origin = get_origin(type) |
161 | 168 | attribs = fields(origin or type) |
162 | 169 | if attrs_has(type) and any(isinstance(a.type, str) for a in attribs): |
163 | 170 | resolve_types(type) |
164 | 171 | attribs = fields(origin or type) |
165 | 172 |
|
166 | | - if any( |
| 173 | + if msgspec_skips_private and any( |
167 | 174 | attr.name.startswith("_") |
168 | 175 | or ( |
169 | 176 | converter.get_unstructure_hook(attr.type, cache_result=False) |
|
0 commit comments