telegrinder 0.3.4__py3-none-any.whl → 0.4.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of telegrinder might be problematic. Click here for more details.
- telegrinder/__init__.py +148 -149
- telegrinder/api/__init__.py +9 -8
- telegrinder/api/api.py +101 -93
- telegrinder/api/error.py +20 -16
- telegrinder/api/response.py +20 -20
- telegrinder/api/token.py +36 -36
- telegrinder/bot/__init__.py +72 -66
- telegrinder/bot/bot.py +83 -76
- telegrinder/bot/cute_types/__init__.py +19 -17
- telegrinder/bot/cute_types/base.py +184 -258
- telegrinder/bot/cute_types/callback_query.py +400 -385
- telegrinder/bot/cute_types/chat_join_request.py +62 -61
- telegrinder/bot/cute_types/chat_member_updated.py +157 -160
- telegrinder/bot/cute_types/inline_query.py +44 -43
- telegrinder/bot/cute_types/message.py +2590 -2637
- telegrinder/bot/cute_types/pre_checkout_query.py +42 -0
- telegrinder/bot/cute_types/update.py +112 -104
- telegrinder/bot/cute_types/utils.py +62 -95
- telegrinder/bot/dispatch/__init__.py +59 -55
- telegrinder/bot/dispatch/abc.py +76 -77
- telegrinder/bot/dispatch/context.py +96 -98
- telegrinder/bot/dispatch/dispatch.py +254 -202
- telegrinder/bot/dispatch/handler/__init__.py +13 -13
- telegrinder/bot/dispatch/handler/abc.py +23 -24
- telegrinder/bot/dispatch/handler/audio_reply.py +44 -44
- telegrinder/bot/dispatch/handler/base.py +57 -57
- telegrinder/bot/dispatch/handler/document_reply.py +44 -44
- telegrinder/bot/dispatch/handler/func.py +129 -135
- telegrinder/bot/dispatch/handler/media_group_reply.py +44 -43
- telegrinder/bot/dispatch/handler/message_reply.py +36 -36
- telegrinder/bot/dispatch/handler/photo_reply.py +44 -44
- telegrinder/bot/dispatch/handler/sticker_reply.py +37 -37
- telegrinder/bot/dispatch/handler/video_reply.py +44 -44
- telegrinder/bot/dispatch/middleware/__init__.py +3 -3
- telegrinder/bot/dispatch/middleware/abc.py +97 -22
- telegrinder/bot/dispatch/middleware/global_middleware.py +70 -0
- telegrinder/bot/dispatch/process.py +151 -157
- telegrinder/bot/dispatch/return_manager/__init__.py +15 -13
- telegrinder/bot/dispatch/return_manager/abc.py +104 -108
- telegrinder/bot/dispatch/return_manager/callback_query.py +20 -20
- telegrinder/bot/dispatch/return_manager/inline_query.py +15 -15
- telegrinder/bot/dispatch/return_manager/message.py +36 -36
- telegrinder/bot/dispatch/return_manager/pre_checkout_query.py +20 -0
- telegrinder/bot/dispatch/view/__init__.py +15 -13
- telegrinder/bot/dispatch/view/abc.py +45 -41
- telegrinder/bot/dispatch/view/base.py +231 -200
- telegrinder/bot/dispatch/view/box.py +140 -129
- telegrinder/bot/dispatch/view/callback_query.py +16 -17
- telegrinder/bot/dispatch/view/chat_join_request.py +11 -16
- telegrinder/bot/dispatch/view/chat_member.py +37 -39
- telegrinder/bot/dispatch/view/inline_query.py +16 -17
- telegrinder/bot/dispatch/view/message.py +43 -44
- telegrinder/bot/dispatch/view/pre_checkout_query.py +16 -0
- telegrinder/bot/dispatch/view/raw.py +116 -114
- telegrinder/bot/dispatch/waiter_machine/__init__.py +17 -17
- telegrinder/bot/dispatch/waiter_machine/actions.py +14 -13
- telegrinder/bot/dispatch/waiter_machine/hasher/__init__.py +8 -8
- telegrinder/bot/dispatch/waiter_machine/hasher/callback.py +55 -55
- telegrinder/bot/dispatch/waiter_machine/hasher/hasher.py +59 -57
- telegrinder/bot/dispatch/waiter_machine/hasher/message.py +51 -51
- telegrinder/bot/dispatch/waiter_machine/hasher/state.py +20 -19
- telegrinder/bot/dispatch/waiter_machine/machine.py +251 -172
- telegrinder/bot/dispatch/waiter_machine/middleware.py +94 -89
- telegrinder/bot/dispatch/waiter_machine/short_state.py +57 -68
- telegrinder/bot/polling/__init__.py +4 -4
- telegrinder/bot/polling/abc.py +25 -25
- telegrinder/bot/polling/polling.py +139 -131
- telegrinder/bot/rules/__init__.py +85 -62
- telegrinder/bot/rules/abc.py +213 -206
- telegrinder/bot/rules/callback_data.py +122 -163
- telegrinder/bot/rules/chat_join.py +45 -43
- telegrinder/bot/rules/command.py +126 -126
- telegrinder/bot/rules/enum_text.py +33 -36
- telegrinder/bot/rules/func.py +28 -26
- telegrinder/bot/rules/fuzzy.py +24 -24
- telegrinder/bot/rules/id.py +24 -0
- telegrinder/bot/rules/inline.py +58 -56
- telegrinder/bot/rules/integer.py +21 -20
- telegrinder/bot/rules/is_from.py +127 -127
- telegrinder/bot/rules/logic.py +18 -0
- telegrinder/bot/rules/markup.py +42 -43
- telegrinder/bot/rules/mention.py +14 -14
- telegrinder/bot/rules/message.py +15 -17
- telegrinder/bot/rules/message_entities.py +33 -35
- telegrinder/bot/rules/node.py +33 -27
- telegrinder/bot/rules/payload.py +81 -0
- telegrinder/bot/rules/payment_invoice.py +29 -0
- telegrinder/bot/rules/regex.py +36 -37
- telegrinder/bot/rules/rule_enum.py +72 -72
- telegrinder/bot/rules/start.py +42 -42
- telegrinder/bot/rules/state.py +35 -37
- telegrinder/bot/rules/text.py +38 -33
- telegrinder/bot/rules/update.py +15 -15
- telegrinder/bot/scenario/__init__.py +5 -5
- telegrinder/bot/scenario/abc.py +17 -19
- telegrinder/bot/scenario/checkbox.py +174 -176
- telegrinder/bot/scenario/choice.py +48 -51
- telegrinder/client/__init__.py +12 -4
- telegrinder/client/abc.py +100 -75
- telegrinder/client/aiohttp.py +134 -130
- telegrinder/client/form_data.py +31 -0
- telegrinder/client/sonic.py +212 -0
- telegrinder/model.py +208 -315
- telegrinder/modules.py +239 -237
- telegrinder/msgspec_json.py +14 -14
- telegrinder/msgspec_utils.py +478 -410
- telegrinder/node/__init__.py +86 -25
- telegrinder/node/attachment.py +163 -87
- telegrinder/node/base.py +288 -160
- telegrinder/node/callback_query.py +54 -53
- telegrinder/node/command.py +34 -33
- telegrinder/node/composer.py +163 -198
- telegrinder/node/container.py +33 -27
- telegrinder/node/either.py +82 -0
- telegrinder/node/event.py +54 -65
- telegrinder/node/file.py +51 -0
- telegrinder/node/me.py +15 -16
- telegrinder/node/payload.py +78 -0
- telegrinder/node/polymorphic.py +67 -48
- telegrinder/node/rule.py +72 -76
- telegrinder/node/scope.py +36 -38
- telegrinder/node/source.py +87 -71
- telegrinder/node/text.py +53 -41
- telegrinder/node/tools/__init__.py +3 -3
- telegrinder/node/tools/generator.py +36 -40
- telegrinder/py.typed +0 -0
- telegrinder/rules.py +1 -62
- telegrinder/tools/__init__.py +152 -93
- telegrinder/tools/adapter/__init__.py +19 -0
- telegrinder/tools/adapter/abc.py +49 -0
- telegrinder/tools/adapter/dataclass.py +56 -0
- telegrinder/{bot/rules → tools}/adapter/errors.py +5 -5
- telegrinder/{bot/rules → tools}/adapter/event.py +63 -65
- telegrinder/{bot/rules → tools}/adapter/node.py +46 -48
- telegrinder/{bot/rules → tools}/adapter/raw_event.py +27 -27
- telegrinder/{bot/rules → tools}/adapter/raw_update.py +30 -30
- telegrinder/tools/buttons.py +106 -80
- telegrinder/tools/callback_data_serilization/__init__.py +5 -0
- telegrinder/tools/callback_data_serilization/abc.py +51 -0
- telegrinder/tools/callback_data_serilization/json_ser.py +60 -0
- telegrinder/tools/callback_data_serilization/msgpack_ser.py +172 -0
- telegrinder/tools/error_handler/__init__.py +7 -7
- telegrinder/tools/error_handler/abc.py +30 -33
- telegrinder/tools/error_handler/error.py +9 -9
- telegrinder/tools/error_handler/error_handler.py +179 -193
- telegrinder/tools/formatting/__init__.py +83 -63
- telegrinder/tools/formatting/deep_links.py +541 -0
- telegrinder/tools/formatting/{html.py → html_formatter.py} +266 -294
- telegrinder/tools/formatting/spec_html_formats.py +71 -117
- telegrinder/tools/functional.py +8 -12
- telegrinder/tools/global_context/__init__.py +7 -7
- telegrinder/tools/global_context/abc.py +63 -63
- telegrinder/tools/global_context/global_context.py +387 -412
- telegrinder/tools/global_context/telegrinder_ctx.py +27 -27
- telegrinder/tools/i18n/__init__.py +7 -7
- telegrinder/tools/i18n/abc.py +30 -30
- telegrinder/tools/i18n/middleware/__init__.py +3 -3
- telegrinder/tools/i18n/middleware/abc.py +22 -25
- telegrinder/tools/i18n/simple.py +43 -43
- telegrinder/tools/input_file_directory.py +30 -0
- telegrinder/tools/keyboard.py +128 -128
- telegrinder/tools/lifespan.py +105 -0
- telegrinder/tools/limited_dict.py +32 -37
- telegrinder/tools/loop_wrapper/__init__.py +4 -4
- telegrinder/tools/loop_wrapper/abc.py +20 -15
- telegrinder/tools/loop_wrapper/loop_wrapper.py +169 -224
- telegrinder/tools/magic.py +307 -157
- telegrinder/tools/parse_mode.py +6 -6
- telegrinder/tools/state_storage/__init__.py +4 -4
- telegrinder/tools/state_storage/abc.py +31 -35
- telegrinder/tools/state_storage/memory.py +25 -25
- telegrinder/tools/strings.py +13 -0
- telegrinder/types/__init__.py +268 -260
- telegrinder/types/enums.py +711 -701
- telegrinder/types/input_file.py +51 -0
- telegrinder/types/methods.py +5055 -4633
- telegrinder/types/objects.py +7058 -6950
- telegrinder/verification_utils.py +30 -32
- {telegrinder-0.3.4.dist-info → telegrinder-0.4.0.dist-info}/LICENSE +22 -22
- telegrinder-0.4.0.dist-info/METADATA +144 -0
- telegrinder-0.4.0.dist-info/RECORD +182 -0
- {telegrinder-0.3.4.dist-info → telegrinder-0.4.0.dist-info}/WHEEL +1 -1
- telegrinder/bot/rules/adapter/__init__.py +0 -17
- telegrinder/bot/rules/adapter/abc.py +0 -31
- telegrinder/node/message.py +0 -14
- telegrinder/node/update.py +0 -15
- telegrinder/tools/formatting/links.py +0 -38
- telegrinder/tools/kb_set/__init__.py +0 -4
- telegrinder/tools/kb_set/base.py +0 -15
- telegrinder/tools/kb_set/yaml.py +0 -63
- telegrinder-0.3.4.dist-info/METADATA +0 -110
- telegrinder-0.3.4.dist-info/RECORD +0 -165
telegrinder/model.py
CHANGED
|
@@ -1,320 +1,213 @@
|
|
|
1
|
-
import
|
|
2
|
-
import
|
|
3
|
-
|
|
4
|
-
import
|
|
5
|
-
import
|
|
6
|
-
|
|
7
|
-
import
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
) ->
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
def
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
return
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
self
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
exclude_fields:
|
|
169
|
-
|
|
170
|
-
"""
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
get_origin(value.__annotations__["data"]): value
|
|
208
|
-
for key, value in vars(self.__class__).items()
|
|
209
|
-
if key.startswith("convert_") and callable(value)
|
|
210
|
-
}
|
|
211
|
-
)
|
|
212
|
-
|
|
213
|
-
def __call__(self, data: typing.Any, *, serialize: bool = True) -> typing.Any:
|
|
214
|
-
converter = self.get_converter(get_origin(type(data)))
|
|
215
|
-
if converter is not None:
|
|
216
|
-
if isinstance(converter, staticmethod):
|
|
217
|
-
return converter(data, serialize)
|
|
218
|
-
return converter(self, data, serialize)
|
|
219
|
-
return data
|
|
220
|
-
|
|
221
|
-
@property
|
|
222
|
-
def converters(self) -> dict[type[typing.Any], typing.Callable[..., typing.Any]]:
|
|
223
|
-
return self._converters.copy()
|
|
224
|
-
|
|
225
|
-
@property
|
|
226
|
-
def files(self) -> dict[str, tuple[str, bytes]]:
|
|
227
|
-
return self._files.copy()
|
|
228
|
-
|
|
229
|
-
@staticmethod
|
|
230
|
-
def convert_enum(data: enum.Enum, _: bool = False) -> typing.Any:
|
|
231
|
-
return data.value
|
|
232
|
-
|
|
233
|
-
@staticmethod
|
|
234
|
-
def convert_datetime(data: datetime, _: bool = False) -> int:
|
|
235
|
-
return int(data.timestamp())
|
|
236
|
-
|
|
237
|
-
def get_converter(self, t: type[typing.Any]):
|
|
238
|
-
for type_, converter in self._converters.items():
|
|
239
|
-
if issubclass(t, type_):
|
|
240
|
-
return converter
|
|
241
|
-
return None
|
|
242
|
-
|
|
243
|
-
def convert_model(
|
|
244
|
-
self,
|
|
245
|
-
data: Model,
|
|
246
|
-
serialize: bool = True,
|
|
247
|
-
) -> str | dict[str, typing.Any]:
|
|
248
|
-
converted_dct = self(data.to_dict(), serialize=False)
|
|
249
|
-
return encoder.encode(converted_dct) if serialize is True else converted_dct
|
|
250
|
-
|
|
251
|
-
def convert_dct(
|
|
252
|
-
self,
|
|
253
|
-
data: dict[str, typing.Any],
|
|
254
|
-
serialize: bool = True,
|
|
255
|
-
) -> dict[str, typing.Any]:
|
|
256
|
-
return {
|
|
257
|
-
k: self(v, serialize=serialize) for k, v in data.items() if type(v) not in (NoneType, Nothing)
|
|
258
|
-
}
|
|
259
|
-
|
|
260
|
-
def convert_lst(
|
|
261
|
-
self,
|
|
262
|
-
data: list[typing.Any],
|
|
263
|
-
serialize: bool = True,
|
|
264
|
-
) -> str | list[typing.Any]:
|
|
265
|
-
converted_lst = [self(x, serialize=False) for x in data]
|
|
266
|
-
return encoder.encode(converted_lst) if serialize is True else converted_lst
|
|
267
|
-
|
|
268
|
-
def convert_tpl(self, data: tuple[typing.Any, ...], _: bool = False) -> str | tuple[typing.Any, ...]:
|
|
269
|
-
match data:
|
|
270
|
-
case (str(filename), bytes(content)):
|
|
271
|
-
attach_name = secrets.token_urlsafe(16)
|
|
272
|
-
self._files[attach_name] = (filename, content)
|
|
273
|
-
return "attach://{}".format(attach_name)
|
|
274
|
-
|
|
275
|
-
return data
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
class Proxy:
|
|
279
|
-
def __init__(self, cfg: "_ProxiedDict", key: str) -> None:
|
|
280
|
-
self.key = key
|
|
281
|
-
self.cfg = cfg
|
|
282
|
-
|
|
283
|
-
def get(self) -> typing.Any | None:
|
|
284
|
-
return self.cfg._defaults.get(self.key)
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
class _ProxiedDict(typing.Generic[T]):
|
|
288
|
-
def __init__(self, tp: type[T]) -> None:
|
|
289
|
-
self.type = tp
|
|
290
|
-
self._defaults = {}
|
|
291
|
-
|
|
292
|
-
def __setattribute__(self, name: str, value: typing.Any, /) -> None:
|
|
293
|
-
self._defaults[name] = value
|
|
294
|
-
|
|
295
|
-
def __getitem__(self, key: str, /) -> None:
|
|
296
|
-
return Proxy(self, key) # type: ignore
|
|
297
|
-
|
|
298
|
-
def __setitem__(self, key: str, value: typing.Any, /) -> None:
|
|
299
|
-
self._defaults[key] = value
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
if typing.TYPE_CHECKING:
|
|
303
|
-
|
|
304
|
-
def ProxiedDict(typed_dct: type[T]) -> T | _ProxiedDict[T]: # noqa: N802
|
|
305
|
-
...
|
|
306
|
-
|
|
307
|
-
else:
|
|
308
|
-
ProxiedDict = _ProxiedDict
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
__all__ = (
|
|
312
|
-
"DataConverter",
|
|
1
|
+
import keyword
|
|
2
|
+
import typing
|
|
3
|
+
|
|
4
|
+
import msgspec
|
|
5
|
+
from fntypes.co import Nothing, Result, Some
|
|
6
|
+
|
|
7
|
+
from telegrinder.msgspec_utils import decoder, encoder, struct_as_dict
|
|
8
|
+
|
|
9
|
+
if typing.TYPE_CHECKING:
|
|
10
|
+
from telegrinder.api.error import APIError
|
|
11
|
+
|
|
12
|
+
MODEL_CONFIG: typing.Final[dict[str, typing.Any]] = {
|
|
13
|
+
"dict": True,
|
|
14
|
+
"rename": {kw + "_": kw for kw in keyword.kwlist},
|
|
15
|
+
}
|
|
16
|
+
UNSET = typing.cast(typing.Any, msgspec.UNSET)
|
|
17
|
+
"""Docs: https://jcristharif.com/msgspec/api.html#unset
|
|
18
|
+
|
|
19
|
+
During decoding, if a field isn't explicitly set in the model,
|
|
20
|
+
the default value of `UNSET` will be set instead. This lets downstream
|
|
21
|
+
consumers determine whether a field was left unset, or explicitly set a value."""
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
def full_result[T](
|
|
25
|
+
result: Result[msgspec.Raw, "APIError"],
|
|
26
|
+
full_t: type[T],
|
|
27
|
+
) -> Result[T, "APIError"]:
|
|
28
|
+
return result.map(lambda v: decoder.decode(v, type=full_t))
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def is_none(value: typing.Any, /) -> typing.TypeGuard[None | Nothing]:
|
|
32
|
+
return value is None or isinstance(value, Nothing)
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
def get_params(params: dict[str, typing.Any]) -> dict[str, typing.Any]:
|
|
36
|
+
validated_params = {}
|
|
37
|
+
for k, v in (
|
|
38
|
+
*params.pop("other", {}).items(),
|
|
39
|
+
*params.items(),
|
|
40
|
+
):
|
|
41
|
+
if isinstance(v, Proxy):
|
|
42
|
+
v = v.get()
|
|
43
|
+
if k == "self" or is_none(v):
|
|
44
|
+
continue
|
|
45
|
+
validated_params[k] = v.unwrap() if isinstance(v, Some) else v
|
|
46
|
+
return validated_params
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
if typing.TYPE_CHECKING:
|
|
50
|
+
|
|
51
|
+
@typing.overload
|
|
52
|
+
def field(*, name: str | None = ...) -> typing.Any: ...
|
|
53
|
+
|
|
54
|
+
@typing.overload
|
|
55
|
+
def field(*, default: typing.Any, name: str | None = ...) -> typing.Any: ...
|
|
56
|
+
|
|
57
|
+
@typing.overload
|
|
58
|
+
def field(
|
|
59
|
+
*,
|
|
60
|
+
default_factory: typing.Callable[[], typing.Any],
|
|
61
|
+
name: str | None = None,
|
|
62
|
+
) -> typing.Any: ...
|
|
63
|
+
|
|
64
|
+
@typing.overload
|
|
65
|
+
def field(
|
|
66
|
+
*,
|
|
67
|
+
converter: typing.Callable[[typing.Any], typing.Any],
|
|
68
|
+
name: str | None = ...,
|
|
69
|
+
) -> typing.Any: ...
|
|
70
|
+
|
|
71
|
+
@typing.overload
|
|
72
|
+
def field(
|
|
73
|
+
*,
|
|
74
|
+
default: typing.Any,
|
|
75
|
+
converter: typing.Callable[[typing.Any], typing.Any],
|
|
76
|
+
name: str | None = ...,
|
|
77
|
+
) -> typing.Any: ...
|
|
78
|
+
|
|
79
|
+
@typing.overload
|
|
80
|
+
def field(
|
|
81
|
+
*,
|
|
82
|
+
default_factory: typing.Callable[[], typing.Any],
|
|
83
|
+
converter: typing.Callable[[typing.Any], typing.Any],
|
|
84
|
+
name: str | None = None,
|
|
85
|
+
) -> typing.Any: ...
|
|
86
|
+
|
|
87
|
+
def field(
|
|
88
|
+
*,
|
|
89
|
+
default=...,
|
|
90
|
+
default_factory=...,
|
|
91
|
+
name=...,
|
|
92
|
+
converter=...,
|
|
93
|
+
) -> typing.Any: ...
|
|
94
|
+
|
|
95
|
+
class From[T]:
|
|
96
|
+
def __new__(cls, _: T, /) -> typing.Any: ...
|
|
97
|
+
else:
|
|
98
|
+
from msgspec import field as _field
|
|
99
|
+
|
|
100
|
+
type From[T] = T
|
|
101
|
+
|
|
102
|
+
def field(**kwargs):
|
|
103
|
+
kwargs.pop("converter", None)
|
|
104
|
+
return _field(**kwargs)
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
@typing.dataclass_transform(field_specifiers=(field,))
|
|
108
|
+
class Model(msgspec.Struct, **MODEL_CONFIG):
|
|
109
|
+
if not typing.TYPE_CHECKING:
|
|
110
|
+
|
|
111
|
+
def __post_init__(self):
|
|
112
|
+
for field in self.__struct_fields__:
|
|
113
|
+
if is_none(getattr(self, field)):
|
|
114
|
+
setattr(self, field, UNSET)
|
|
115
|
+
|
|
116
|
+
def __getattribute__(self, name, /):
|
|
117
|
+
val = super().__getattribute__(name)
|
|
118
|
+
return Nothing() if val is UNSET else val
|
|
119
|
+
|
|
120
|
+
@classmethod
|
|
121
|
+
def from_data[**P, T](cls: typing.Callable[P, T], *args: P.args, **kwargs: P.kwargs) -> T:
|
|
122
|
+
return decoder.convert(msgspec.structs.asdict(cls(*args, **kwargs)), type=cls) # type: ignore
|
|
123
|
+
|
|
124
|
+
@classmethod
|
|
125
|
+
def from_dict(cls, obj: dict[str, typing.Any], /) -> typing.Self:
|
|
126
|
+
return decoder.convert(obj, type=cls)
|
|
127
|
+
|
|
128
|
+
@classmethod
|
|
129
|
+
def from_raw(cls, raw: str | bytes, /) -> typing.Self:
|
|
130
|
+
return decoder.decode(raw, type=cls)
|
|
131
|
+
|
|
132
|
+
def _to_dict(
|
|
133
|
+
self,
|
|
134
|
+
dct_name: str,
|
|
135
|
+
exclude_fields: set[str],
|
|
136
|
+
full: bool,
|
|
137
|
+
) -> dict[str, typing.Any]:
|
|
138
|
+
if dct_name not in self.__dict__:
|
|
139
|
+
self.__dict__[dct_name] = (
|
|
140
|
+
struct_as_dict(self)
|
|
141
|
+
if not full
|
|
142
|
+
else encoder.to_builtins(self.to_dict(exclude_fields=exclude_fields), order="deterministic")
|
|
143
|
+
)
|
|
144
|
+
|
|
145
|
+
if not exclude_fields:
|
|
146
|
+
return self.__dict__[dct_name]
|
|
147
|
+
|
|
148
|
+
return {key: value for key, value in self.__dict__[dct_name].items() if key not in exclude_fields}
|
|
149
|
+
|
|
150
|
+
def to_raw(self) -> str:
|
|
151
|
+
return encoder.encode(self)
|
|
152
|
+
|
|
153
|
+
def to_dict(
|
|
154
|
+
self,
|
|
155
|
+
*,
|
|
156
|
+
exclude_fields: set[str] | None = None,
|
|
157
|
+
) -> dict[str, typing.Any]:
|
|
158
|
+
""":param exclude_fields: Model field names to exclude from the dictionary representation of this model.
|
|
159
|
+
:return: A dictionary representation of this model.
|
|
160
|
+
"""
|
|
161
|
+
return self._to_dict("model_as_dict", exclude_fields or set(), full=False)
|
|
162
|
+
|
|
163
|
+
def to_full_dict(
|
|
164
|
+
self,
|
|
165
|
+
*,
|
|
166
|
+
exclude_fields: set[str] | None = None,
|
|
167
|
+
) -> dict[str, typing.Any]:
|
|
168
|
+
""":param exclude_fields: Model field names to exclude from the dictionary representation of this model.
|
|
169
|
+
:return: A dictionary representation of this model including all models, structs, custom types.
|
|
170
|
+
"""
|
|
171
|
+
return self._to_dict("model_as_full_dict", exclude_fields or set(), full=True)
|
|
172
|
+
|
|
173
|
+
|
|
174
|
+
class Proxy[T]:
|
|
175
|
+
def __init__(self, cfg: "_ProxiedDict[T]", key: str) -> None:
|
|
176
|
+
self.key = key
|
|
177
|
+
self.cfg = cfg
|
|
178
|
+
|
|
179
|
+
def get(self) -> typing.Any | None:
|
|
180
|
+
return self.cfg._defaults.get(self.key)
|
|
181
|
+
|
|
182
|
+
|
|
183
|
+
class _ProxiedDict[T]:
|
|
184
|
+
def __init__(self, tp: type[T]) -> None:
|
|
185
|
+
self.type = tp
|
|
186
|
+
self._defaults = {}
|
|
187
|
+
|
|
188
|
+
def __setattribute__(self, name: str, value: typing.Any, /) -> None:
|
|
189
|
+
self._defaults[name] = value
|
|
190
|
+
|
|
191
|
+
def __getitem__(self, key: str, /) -> None:
|
|
192
|
+
return Proxy(self, key) # type: ignore
|
|
193
|
+
|
|
194
|
+
def __setitem__(self, key: str, value: typing.Any, /) -> None:
|
|
195
|
+
self._defaults[key] = value
|
|
196
|
+
|
|
197
|
+
|
|
198
|
+
if typing.TYPE_CHECKING:
|
|
199
|
+
|
|
200
|
+
def ProxiedDict[T](typed_dct: type[T]) -> T | _ProxiedDict[T]: # noqa: N802
|
|
201
|
+
...
|
|
202
|
+
else:
|
|
203
|
+
ProxiedDict = _ProxiedDict
|
|
204
|
+
|
|
205
|
+
|
|
206
|
+
__all__ = (
|
|
313
207
|
"MODEL_CONFIG",
|
|
314
208
|
"Model",
|
|
315
209
|
"ProxiedDict",
|
|
316
210
|
"Proxy",
|
|
317
211
|
"full_result",
|
|
318
|
-
"
|
|
319
|
-
|
|
320
|
-
)
|
|
212
|
+
"get_params",
|
|
213
|
+
)
|