2
2
3
3
__all__ = ["DataclassInstance" , "replace" , "fields" , "asdict" , "astuple" ]
4
4
5
- from collections .abc import Callable
6
- from dataclasses import Field as _dataclass_Field
7
- from dataclasses import asdict as _dataclass_asdict
8
- from dataclasses import astuple as _dataclass_astuple
9
- from dataclasses import fields as _dataclass_fields
10
- from dataclasses import replace as _dataclass_replace
5
+ from collections .abc import Callable , Hashable , Mapping
6
+ from dataclasses import (
7
+ Field ,
8
+ asdict as _dataclass_asdict ,
9
+ astuple as _dataclass_astuple ,
10
+ field ,
11
+ fields as _dataclass_fields ,
12
+ replace as _dataclass_replace ,
13
+ )
11
14
from typing import Any , ClassVar , Protocol , runtime_checkable
12
15
13
16
from plum import dispatch
@@ -27,34 +30,215 @@ def __subclasshook__(cls: type, c: type) -> bool:
27
30
return hasattr (c , "__dataclass_fields__" )
28
31
29
32
30
- @dispatch # type: ignore[misc]
33
+ # ===================================================================
34
+ # Replace
35
+
36
+
37
+ @dispatch
31
38
def replace (obj : DataclassInstance , / , ** kwargs : Any ) -> DataclassInstance :
32
- """Replace the fields of a dataclass instance."""
39
+ """Replace the fields of a dataclass instance.
40
+
41
+ Examples
42
+ --------
43
+ >>> from dataclasses import dataclass
44
+ >>> from dataclasstools import replace
45
+
46
+ >>> @dataclass
47
+ ... class Point:
48
+ ... x: float
49
+ ... y: float
50
+
51
+ >>> p = Point(1.0, 2.0)
52
+ >>> p
53
+ Point(x=1.0, y=2.0)
54
+
55
+ >>> replace(p, x=3.0)
56
+ Point(x=3.0, y=2.0)
57
+
58
+ """
33
59
return _dataclass_replace (obj , ** kwargs )
34
60
35
61
36
- @dispatch # type: ignore[misc]
37
- def fields (obj : DataclassInstance ) -> tuple [_dataclass_Field , ...]: # type: ignore[type-arg] # TODO: raise issue in beartype
38
- """Return the fields of a dataclass instance."""
62
+ @dispatch # type: ignore[no-redef]
63
+ def replace (obj : Mapping [Hashable , Any ], / , ** kwargs : Any ) -> Mapping [Hashable , Any ]:
64
+ """Replace the fields of a mapping.
65
+
66
+ This operates similarly to `dict.update`, except that
67
+ the kwargs are checked against the keys of the mapping.
68
+
69
+ Examples
70
+ --------
71
+ >>> from dataclasses import dataclass
72
+ >>> from dataclasstools import replace
73
+
74
+ >>> p = {"a": 1, "b": 2, "c": 3}
75
+ >>> p
76
+ {'a': 1, 'b': 2, 'c': 3}
77
+
78
+ >>> replace(p, c=4.0)
79
+ {'a': 1, 'b': 2, 'c': 4.0}
80
+
81
+ """
82
+ extra_keys = set (kwargs ) - set (obj )
83
+ if extra_keys :
84
+ msg = f"invalid keys { extra_keys } ."
85
+ raise ValueError (msg )
86
+
87
+ return type (obj )(** {** obj , ** kwargs })
88
+
89
+
90
+ # ===================================================================
91
+ # Fields
92
+
93
+
94
+ @dispatch
95
+ def fields (obj : DataclassInstance , / ) -> tuple [Field , ...]: # type: ignore[type-arg] # TODO: raise issue in beartype
96
+ """Return the fields of a dataclass instance.
97
+
98
+ Examples
99
+ --------
100
+ >>> from dataclasses import dataclass
101
+ >>> from dataclasstools import fields
102
+
103
+ >>> @dataclass
104
+ ... class Point:
105
+ ... x: float
106
+ ... y: float
107
+
108
+ >>> p = Point(1.0, 2.0)
109
+ >>> fields(p)
110
+ (Field(name='x',type=<class 'float'>,...),
111
+ Field(name='y',type=<class 'float'>,...))
112
+
113
+ """
39
114
return _dataclass_fields (obj )
40
115
41
116
42
- @dispatch # type: ignore[misc]
117
+ @dispatch # type: ignore[no-redef]
118
+ def fields (obj : Mapping [str , Any ], / ) -> tuple [Field , ...]: # type: ignore[type-arg] # TODO: raise issue in beartype
119
+ """Return the mapping as a tuple of `dataclass.Field` objects.
120
+
121
+ Examples
122
+ --------
123
+ >>> from dataclasstools import fields
124
+
125
+ >>> p = {"a": 1, "b": 2.0, "c": "3"}
126
+ >>> fields(p)
127
+ (Field(name='a',type=<class 'int'>,...),
128
+ Field(name='b',type=<class 'float'>,...),
129
+ Field(name='c',type=<class 'str'>,...))
130
+
131
+ """
132
+ fs = tuple (field (kw_only = True ) for _ in obj ) # pylint: disable=invalid-field-call
133
+ for f , (k , v ) in zip (fs , obj .items (), strict = True ):
134
+ f .name = k
135
+ f .type = type (v )
136
+ return fs
137
+
138
+
139
+ # ===================================================================
140
+ # Asdict
141
+
142
+
143
+ @dispatch
43
144
def asdict (
44
145
obj : DataclassInstance ,
45
146
/ ,
46
147
* ,
47
148
dict_factory : Callable [[list [tuple [str , Any ]]], dict [str , Any ]] = dict ,
48
149
) -> dict [str , Any ]:
49
- """Return the fields of a dataclass instance as a dictionary."""
150
+ """Return the fields of a dataclass instance as a dictionary.
151
+
152
+ Examples
153
+ --------
154
+ >>> from dataclasses import dataclass
155
+ >>> from dataclasstools import asdict
156
+
157
+ >>> @dataclass
158
+ ... class Point:
159
+ ... x: float
160
+ ... y: float
161
+
162
+ >>> p = Point(1.0, 2.0)
163
+ >>> asdict(p)
164
+ {'x': 1.0, 'y': 2.0}
165
+
166
+ """
50
167
return _dataclass_asdict (obj , dict_factory = dict_factory )
51
168
52
169
53
- @dispatch # type: ignore[misc]
170
+ @dispatch # type: ignore[no-redef]
171
+ def asdict (
172
+ obj : Mapping [str , Any ],
173
+ / ,
174
+ * ,
175
+ dict_factory : Callable [[list [tuple [str , Any ]]], dict [str , Any ]] = dict ,
176
+ ) -> dict [str , Any ]:
177
+ """Return the fields of a mapping as a dictionary.
178
+
179
+ Following the `asdict` API, the dictionary may be copied if ``dict_factory``
180
+ performs a copy when constructed from a :class:`~collections.abc.Mapping`.
181
+
182
+ Examples
183
+ --------
184
+ >>> from dataclasstools import asdict
185
+
186
+ >>> p = {"a": 1, "b": 2.0, "c": "3"}
187
+ >>> asdict(p)
188
+ {'a': 1, 'b': 2.0, 'c': '3'}
189
+
190
+ >>> asdict(p) is p
191
+ False
192
+
193
+ """
194
+ return dict_factory (obj )
195
+
196
+
197
+ # ===================================================================
198
+ # Astuple
199
+
200
+
201
+ @dispatch
54
202
def astuple (
55
203
obj : DataclassInstance ,
56
204
/ ,
57
205
tuple_factory : Callable [[Any ], tuple [Any , ...]] = tuple ,
58
206
) -> tuple [Any , ...]:
59
- """Return the fields of a dataclass instance as a tuple."""
207
+ """Return the fields of a dataclass instance as a tuple.
208
+
209
+ Examples
210
+ --------
211
+ >>> from dataclasses import dataclass
212
+ >>> from dataclasstools import astuple
213
+
214
+ >>> @dataclass
215
+ ... class Point:
216
+ ... x: float
217
+ ... y: float
218
+
219
+ >>> p = Point(1.0, 2.0)
220
+ >>> astuple(p)
221
+ (1.0, 2.0)
222
+
223
+ """
60
224
return _dataclass_astuple (obj , tuple_factory = tuple_factory )
225
+
226
+
227
+ @dispatch # type: ignore[no-redef]
228
+ def astuple (
229
+ obj : Mapping [str , Any ],
230
+ / ,
231
+ tuple_factory : Callable [[Any ], tuple [Any , ...]] = tuple ,
232
+ ) -> tuple [Any , ...]:
233
+ """Return the fields of a mapping as a tuple.
234
+
235
+ Examples
236
+ --------
237
+ >>> from dataclasstools import astuple
238
+
239
+ >>> p = {"a": 1, "b": 2.0, "c": "3"}
240
+ >>> astuple(p)
241
+ (1, 2.0, '3')
242
+
243
+ """
244
+ return tuple_factory (obj .values ())
0 commit comments