telegrinder 1.0.0rc1__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.
- telegrinder/__init__.py +258 -0
- telegrinder/__meta__.py +1 -0
- telegrinder/api/__init__.py +15 -0
- telegrinder/api/api.py +175 -0
- telegrinder/api/error.py +50 -0
- telegrinder/api/response.py +23 -0
- telegrinder/api/token.py +30 -0
- telegrinder/api/validators.py +30 -0
- telegrinder/bot/__init__.py +144 -0
- telegrinder/bot/bot.py +70 -0
- telegrinder/bot/cute_types/__init__.py +41 -0
- telegrinder/bot/cute_types/base.py +228 -0
- telegrinder/bot/cute_types/base.pyi +49 -0
- telegrinder/bot/cute_types/business_connection.py +9 -0
- telegrinder/bot/cute_types/business_messages_deleted.py +9 -0
- telegrinder/bot/cute_types/callback_query.py +248 -0
- telegrinder/bot/cute_types/chat_boost_removed.py +9 -0
- telegrinder/bot/cute_types/chat_boost_updated.py +9 -0
- telegrinder/bot/cute_types/chat_join_request.py +59 -0
- telegrinder/bot/cute_types/chat_member_updated.py +158 -0
- telegrinder/bot/cute_types/chosen_inline_result.py +11 -0
- telegrinder/bot/cute_types/inline_query.py +41 -0
- telegrinder/bot/cute_types/message.py +2809 -0
- telegrinder/bot/cute_types/message_reaction_count_updated.py +9 -0
- telegrinder/bot/cute_types/message_reaction_updated.py +9 -0
- telegrinder/bot/cute_types/paid_media_purchased.py +11 -0
- telegrinder/bot/cute_types/poll.py +9 -0
- telegrinder/bot/cute_types/poll_answer.py +9 -0
- telegrinder/bot/cute_types/pre_checkout_query.py +36 -0
- telegrinder/bot/cute_types/shipping_query.py +11 -0
- telegrinder/bot/cute_types/update.py +209 -0
- telegrinder/bot/cute_types/utils.py +141 -0
- telegrinder/bot/dispatch/__init__.py +99 -0
- telegrinder/bot/dispatch/abc.py +74 -0
- telegrinder/bot/dispatch/action.py +99 -0
- telegrinder/bot/dispatch/context.py +162 -0
- telegrinder/bot/dispatch/dispatch.py +362 -0
- telegrinder/bot/dispatch/handler/__init__.py +23 -0
- telegrinder/bot/dispatch/handler/abc.py +25 -0
- telegrinder/bot/dispatch/handler/audio_reply.py +43 -0
- telegrinder/bot/dispatch/handler/base.py +34 -0
- telegrinder/bot/dispatch/handler/document_reply.py +43 -0
- telegrinder/bot/dispatch/handler/func.py +73 -0
- telegrinder/bot/dispatch/handler/media_group_reply.py +43 -0
- telegrinder/bot/dispatch/handler/message_reply.py +35 -0
- telegrinder/bot/dispatch/handler/photo_reply.py +43 -0
- telegrinder/bot/dispatch/handler/sticker_reply.py +36 -0
- telegrinder/bot/dispatch/handler/video_reply.py +43 -0
- telegrinder/bot/dispatch/middleware/__init__.py +13 -0
- telegrinder/bot/dispatch/middleware/abc.py +112 -0
- telegrinder/bot/dispatch/middleware/box.py +32 -0
- telegrinder/bot/dispatch/middleware/filter.py +88 -0
- telegrinder/bot/dispatch/middleware/media_group.py +69 -0
- telegrinder/bot/dispatch/process.py +93 -0
- telegrinder/bot/dispatch/return_manager/__init__.py +21 -0
- telegrinder/bot/dispatch/return_manager/abc.py +107 -0
- telegrinder/bot/dispatch/return_manager/callback_query.py +19 -0
- telegrinder/bot/dispatch/return_manager/inline_query.py +14 -0
- telegrinder/bot/dispatch/return_manager/message.py +34 -0
- telegrinder/bot/dispatch/return_manager/pre_checkout_query.py +19 -0
- telegrinder/bot/dispatch/return_manager/utils.py +20 -0
- telegrinder/bot/dispatch/router/__init__.py +4 -0
- telegrinder/bot/dispatch/router/abc.py +15 -0
- telegrinder/bot/dispatch/router/base.py +154 -0
- telegrinder/bot/dispatch/view/__init__.py +15 -0
- telegrinder/bot/dispatch/view/abc.py +15 -0
- telegrinder/bot/dispatch/view/base.py +226 -0
- telegrinder/bot/dispatch/view/box.py +207 -0
- telegrinder/bot/dispatch/view/media_group.py +25 -0
- telegrinder/bot/dispatch/waiter_machine/__init__.py +25 -0
- telegrinder/bot/dispatch/waiter_machine/actions.py +16 -0
- telegrinder/bot/dispatch/waiter_machine/hasher/__init__.py +13 -0
- telegrinder/bot/dispatch/waiter_machine/hasher/callback.py +53 -0
- telegrinder/bot/dispatch/waiter_machine/hasher/hasher.py +61 -0
- telegrinder/bot/dispatch/waiter_machine/hasher/message.py +49 -0
- telegrinder/bot/dispatch/waiter_machine/machine.py +264 -0
- telegrinder/bot/dispatch/waiter_machine/middleware.py +77 -0
- telegrinder/bot/dispatch/waiter_machine/short_state.py +105 -0
- telegrinder/bot/polling/__init__.py +4 -0
- telegrinder/bot/polling/abc.py +25 -0
- telegrinder/bot/polling/error_handler.py +93 -0
- telegrinder/bot/polling/polling.py +167 -0
- telegrinder/bot/polling/utils.py +12 -0
- telegrinder/bot/rules/__init__.py +166 -0
- telegrinder/bot/rules/abc.py +150 -0
- telegrinder/bot/rules/button.py +20 -0
- telegrinder/bot/rules/callback_data.py +109 -0
- telegrinder/bot/rules/chat_join.py +28 -0
- telegrinder/bot/rules/chat_member_updated.py +145 -0
- telegrinder/bot/rules/command.py +137 -0
- telegrinder/bot/rules/enum_text.py +29 -0
- telegrinder/bot/rules/func.py +21 -0
- telegrinder/bot/rules/fuzzy.py +21 -0
- telegrinder/bot/rules/inline.py +45 -0
- telegrinder/bot/rules/integer.py +19 -0
- telegrinder/bot/rules/is_from.py +213 -0
- telegrinder/bot/rules/logic.py +22 -0
- telegrinder/bot/rules/magic.py +60 -0
- telegrinder/bot/rules/markup.py +51 -0
- telegrinder/bot/rules/media.py +13 -0
- telegrinder/bot/rules/mention.py +15 -0
- telegrinder/bot/rules/message_entities.py +37 -0
- telegrinder/bot/rules/node.py +43 -0
- telegrinder/bot/rules/payload.py +89 -0
- telegrinder/bot/rules/payment_invoice.py +14 -0
- telegrinder/bot/rules/regex.py +34 -0
- telegrinder/bot/rules/rule_enum.py +71 -0
- telegrinder/bot/rules/start.py +73 -0
- telegrinder/bot/rules/state.py +35 -0
- telegrinder/bot/rules/text.py +27 -0
- telegrinder/bot/rules/update.py +14 -0
- telegrinder/bot/scenario/__init__.py +5 -0
- telegrinder/bot/scenario/abc.py +16 -0
- telegrinder/bot/scenario/checkbox.py +183 -0
- telegrinder/bot/scenario/choice.py +44 -0
- telegrinder/client/__init__.py +11 -0
- telegrinder/client/abc.py +136 -0
- telegrinder/client/form_data.py +34 -0
- telegrinder/client/rnet.py +198 -0
- telegrinder/model.py +133 -0
- telegrinder/model.pyi +57 -0
- telegrinder/modules.py +1081 -0
- telegrinder/msgspec_utils/__init__.py +42 -0
- telegrinder/msgspec_utils/abc.py +16 -0
- telegrinder/msgspec_utils/custom_types/__init__.py +6 -0
- telegrinder/msgspec_utils/custom_types/datetime.py +24 -0
- telegrinder/msgspec_utils/custom_types/enum_meta.py +61 -0
- telegrinder/msgspec_utils/custom_types/literal.py +25 -0
- telegrinder/msgspec_utils/custom_types/option.py +17 -0
- telegrinder/msgspec_utils/decoder.py +388 -0
- telegrinder/msgspec_utils/encoder.py +204 -0
- telegrinder/msgspec_utils/json.py +15 -0
- telegrinder/msgspec_utils/tools.py +80 -0
- telegrinder/node/__init__.py +80 -0
- telegrinder/node/compose.py +193 -0
- telegrinder/node/nodes/__init__.py +96 -0
- telegrinder/node/nodes/attachment.py +169 -0
- telegrinder/node/nodes/callback_query.py +25 -0
- telegrinder/node/nodes/channel.py +97 -0
- telegrinder/node/nodes/command.py +33 -0
- telegrinder/node/nodes/error.py +43 -0
- telegrinder/node/nodes/event.py +70 -0
- telegrinder/node/nodes/file.py +39 -0
- telegrinder/node/nodes/global_node.py +66 -0
- telegrinder/node/nodes/i18n.py +110 -0
- telegrinder/node/nodes/me.py +26 -0
- telegrinder/node/nodes/message_entities.py +15 -0
- telegrinder/node/nodes/payload.py +84 -0
- telegrinder/node/nodes/reply_message.py +14 -0
- telegrinder/node/nodes/source.py +172 -0
- telegrinder/node/nodes/state_mutator.py +71 -0
- telegrinder/node/nodes/text.py +62 -0
- telegrinder/node/scope.py +88 -0
- telegrinder/node/utils.py +38 -0
- telegrinder/py.typed +0 -0
- telegrinder/rules.py +1 -0
- telegrinder/tools/__init__.py +183 -0
- telegrinder/tools/aio.py +147 -0
- telegrinder/tools/final.py +21 -0
- telegrinder/tools/formatting/__init__.py +85 -0
- telegrinder/tools/formatting/deep_links/__init__.py +39 -0
- telegrinder/tools/formatting/deep_links/links.py +468 -0
- telegrinder/tools/formatting/deep_links/parsing.py +88 -0
- telegrinder/tools/formatting/deep_links/validators.py +8 -0
- telegrinder/tools/formatting/html.py +241 -0
- telegrinder/tools/fullname.py +82 -0
- telegrinder/tools/global_context/__init__.py +13 -0
- telegrinder/tools/global_context/abc.py +63 -0
- telegrinder/tools/global_context/builtin_context.py +45 -0
- telegrinder/tools/global_context/global_context.py +614 -0
- telegrinder/tools/input_file_directory.py +30 -0
- telegrinder/tools/keyboard/__init__.py +6 -0
- telegrinder/tools/keyboard/abc.py +84 -0
- telegrinder/tools/keyboard/base.py +108 -0
- telegrinder/tools/keyboard/button.py +181 -0
- telegrinder/tools/keyboard/data.py +31 -0
- telegrinder/tools/keyboard/keyboard.py +160 -0
- telegrinder/tools/keyboard/utils.py +95 -0
- telegrinder/tools/lifespan.py +188 -0
- telegrinder/tools/limited_dict.py +35 -0
- telegrinder/tools/loop_wrapper.py +271 -0
- telegrinder/tools/magic/__init__.py +29 -0
- telegrinder/tools/magic/annotations.py +172 -0
- telegrinder/tools/magic/descriptors.py +57 -0
- telegrinder/tools/magic/function.py +254 -0
- telegrinder/tools/magic/inspect.py +16 -0
- telegrinder/tools/magic/shortcut.py +107 -0
- telegrinder/tools/member_descriptor_proxy.py +95 -0
- telegrinder/tools/parse_mode.py +12 -0
- telegrinder/tools/serialization/__init__.py +5 -0
- telegrinder/tools/serialization/abc.py +34 -0
- telegrinder/tools/serialization/json_ser.py +60 -0
- telegrinder/tools/serialization/msgpack_ser.py +197 -0
- telegrinder/tools/serialization/utils.py +18 -0
- telegrinder/tools/singleton/__init__.py +4 -0
- telegrinder/tools/singleton/abc.py +14 -0
- telegrinder/tools/singleton/singleton.py +18 -0
- telegrinder/tools/state_mutator/__init__.py +4 -0
- telegrinder/tools/state_mutator/mutation.py +85 -0
- telegrinder/tools/state_storage/__init__.py +4 -0
- telegrinder/tools/state_storage/abc.py +38 -0
- telegrinder/tools/state_storage/memory.py +27 -0
- telegrinder/tools/strings.py +22 -0
- telegrinder/types/__init__.py +323 -0
- telegrinder/types/enums.py +754 -0
- telegrinder/types/input_file.py +51 -0
- telegrinder/types/methods.py +6143 -0
- telegrinder/types/methods_utils.py +66 -0
- telegrinder/types/objects.py +8184 -0
- telegrinder/types/webapp.py +129 -0
- telegrinder/verification_utils.py +35 -0
- telegrinder-1.0.0rc1.dist-info/METADATA +166 -0
- telegrinder-1.0.0rc1.dist-info/RECORD +215 -0
- telegrinder-1.0.0rc1.dist-info/WHEEL +4 -0
- telegrinder-1.0.0rc1.dist-info/licenses/LICENSE +22 -0
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import typing
|
|
2
|
+
|
|
3
|
+
_NOVALUE: typing.Any = object()
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
def private_func_name(func: typing.Callable[..., typing.Any], /) -> str:
|
|
7
|
+
return "__" + func.__name__
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class additional_property[T, R]: # noqa: N801
|
|
11
|
+
def __init__(self, func: typing.Callable[[T], R], /) -> None:
|
|
12
|
+
self.func = func
|
|
13
|
+
self.func_name = private_func_name(func)
|
|
14
|
+
|
|
15
|
+
def __set__(self, instance: T, value: typing.Any, /) -> None:
|
|
16
|
+
instance.__dict__[self.func_name] = value
|
|
17
|
+
|
|
18
|
+
def __get__(self, instance: T | None, owner: type[T], /) -> typing.Any:
|
|
19
|
+
if instance is None:
|
|
20
|
+
raise AttributeError("Cannot access additional property from class.")
|
|
21
|
+
|
|
22
|
+
value = instance.__dict__.get(self.func_name, _NOVALUE)
|
|
23
|
+
if value is _NOVALUE:
|
|
24
|
+
value = self.func(instance)
|
|
25
|
+
instance.__dict__[self.func_name] = value
|
|
26
|
+
|
|
27
|
+
return value
|
|
28
|
+
|
|
29
|
+
def __delete__(self, instance: T, /) -> None:
|
|
30
|
+
instance.__dict__.pop(self.func_name, None)
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
class class_property[T]: # noqa: N801
|
|
34
|
+
def __init__(self, func: typing.Callable[..., T], /) -> None:
|
|
35
|
+
self.func = func
|
|
36
|
+
self.func_name = private_func_name(func)
|
|
37
|
+
|
|
38
|
+
def __get__(self, instance: typing.Any | None, owner: type[typing.Any], /) -> T:
|
|
39
|
+
return self.func(owner)
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
class class_cached_property[T]: # noqa: N801
|
|
43
|
+
def __init__(self, func: typing.Callable[..., T], /) -> None:
|
|
44
|
+
self.func = func
|
|
45
|
+
self.func_name = private_func_name(func)
|
|
46
|
+
|
|
47
|
+
def __get__(self, instance: typing.Any | None, owner: type[typing.Any], /) -> T:
|
|
48
|
+
value = getattr(owner, self.func_name, _NOVALUE)
|
|
49
|
+
|
|
50
|
+
if value is _NOVALUE:
|
|
51
|
+
value = self.func(owner)
|
|
52
|
+
setattr(owner, self.func_name, value)
|
|
53
|
+
|
|
54
|
+
return value
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
__all__ = ("additional_property", "class_cached_property", "class_property")
|
|
@@ -0,0 +1,254 @@
|
|
|
1
|
+
import dataclasses
|
|
2
|
+
import enum
|
|
3
|
+
import inspect
|
|
4
|
+
import types
|
|
5
|
+
import typing
|
|
6
|
+
from functools import cached_property, lru_cache, partial
|
|
7
|
+
|
|
8
|
+
from telegrinder.tools.magic.annotations import Annotations, MappingAnnotations
|
|
9
|
+
|
|
10
|
+
type AnyFunction = Func[..., typing.Any]
|
|
11
|
+
type Func[**P, R] = typing.Callable[P, R]
|
|
12
|
+
type Function[**P, R] = typing.Callable[
|
|
13
|
+
P,
|
|
14
|
+
typing.Union[
|
|
15
|
+
typing.Coroutine[typing.Any, typing.Any, R],
|
|
16
|
+
typing.Awaitable[R],
|
|
17
|
+
typing.AsyncGenerator[R, typing.Any],
|
|
18
|
+
typing.Generator[typing.Any, typing.Any, R],
|
|
19
|
+
typing.AsyncContextManager[R, typing.Any],
|
|
20
|
+
typing.ContextManager[R, typing.Any],
|
|
21
|
+
R,
|
|
22
|
+
],
|
|
23
|
+
]
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def _to_str(obj: typing.Any, /) -> str:
|
|
27
|
+
if isinstance(obj, enum.Enum):
|
|
28
|
+
return str(obj.value)
|
|
29
|
+
return str(obj) if not isinstance(obj, str) else obj
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
@lru_cache(maxsize=1024)
|
|
33
|
+
def _unwrap_func(obj: typing.Any, /) -> typing.Any:
|
|
34
|
+
while True:
|
|
35
|
+
if isinstance(obj, partial):
|
|
36
|
+
obj = obj.func
|
|
37
|
+
continue
|
|
38
|
+
|
|
39
|
+
if hasattr(obj, "__wrapped__"):
|
|
40
|
+
obj = obj.__wrapped__
|
|
41
|
+
continue
|
|
42
|
+
|
|
43
|
+
break
|
|
44
|
+
|
|
45
|
+
return obj
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
def _resolve_arg_names(
|
|
49
|
+
func: AnyFunction,
|
|
50
|
+
/,
|
|
51
|
+
*,
|
|
52
|
+
start_idx: int,
|
|
53
|
+
stop_idx: int,
|
|
54
|
+
exclude: set[str] | None = None,
|
|
55
|
+
) -> tuple[str, ...]:
|
|
56
|
+
exclude = exclude or set()
|
|
57
|
+
varnames = func.__code__.co_varnames[start_idx:stop_idx]
|
|
58
|
+
return tuple(name for name in varnames if name not in exclude)
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
class FunctionParameters(typing.TypedDict):
|
|
62
|
+
args: list[tuple[str, typing.Any | inspect.Parameter.empty]]
|
|
63
|
+
kwargs: list[tuple[str, typing.Any | inspect.Parameter.empty]]
|
|
64
|
+
var_star_args: typing.NotRequired[str]
|
|
65
|
+
var_star_kwargs: typing.NotRequired[str]
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
@dataclasses.dataclass(frozen=True, repr=False)
|
|
69
|
+
class Bundle[R]:
|
|
70
|
+
function: Func[..., R]
|
|
71
|
+
start_idx: int
|
|
72
|
+
context: types.MappingProxyType[str, typing.Any]
|
|
73
|
+
|
|
74
|
+
@cached_property
|
|
75
|
+
def args(self) -> tuple[typing.Any, ...]:
|
|
76
|
+
return tuple(
|
|
77
|
+
self.context[name]
|
|
78
|
+
for name in resolve_posonly_arg_names(self.function, start_idx=self.start_idx)
|
|
79
|
+
if name in self.context
|
|
80
|
+
)
|
|
81
|
+
|
|
82
|
+
@cached_property
|
|
83
|
+
def kwargs(self) -> dict[str, typing.Any]:
|
|
84
|
+
posonly_arg_names = resolve_posonly_arg_names(self.function, start_idx=self.start_idx)
|
|
85
|
+
return {name: value for name, value in self.context.items() if name not in posonly_arg_names}
|
|
86
|
+
|
|
87
|
+
@classmethod
|
|
88
|
+
def from_context(
|
|
89
|
+
cls,
|
|
90
|
+
function: Func[..., R],
|
|
91
|
+
context: typing.Mapping[str, typing.Any],
|
|
92
|
+
/,
|
|
93
|
+
*,
|
|
94
|
+
start_idx: int = 1,
|
|
95
|
+
) -> typing.Self:
|
|
96
|
+
return cls(function, start_idx, types.MappingProxyType(context))
|
|
97
|
+
|
|
98
|
+
def __repr__(self) -> str:
|
|
99
|
+
return "<{}: function={!r} bundle args={!r} kwargs={!r}>".format(
|
|
100
|
+
type(self).__name__,
|
|
101
|
+
self.function,
|
|
102
|
+
self.args,
|
|
103
|
+
self.kwargs,
|
|
104
|
+
)
|
|
105
|
+
|
|
106
|
+
def __call__(self, *args: typing.Any, **kwargs: typing.Any) -> R:
|
|
107
|
+
return self.function(*args, *self.args, **kwargs, **self.kwargs)
|
|
108
|
+
|
|
109
|
+
def __and__(self, other: object, /) -> typing.Self:
|
|
110
|
+
if not isinstance(other, Bundle):
|
|
111
|
+
return NotImplemented
|
|
112
|
+
|
|
113
|
+
if self.function != other.function:
|
|
114
|
+
raise ValueError(
|
|
115
|
+
f"Cannot merge contexts because bundle {other!r} is intended for a different function.",
|
|
116
|
+
)
|
|
117
|
+
|
|
118
|
+
return dataclasses.replace(
|
|
119
|
+
self,
|
|
120
|
+
context=types.MappingProxyType(mapping=self.context | other.context),
|
|
121
|
+
)
|
|
122
|
+
|
|
123
|
+
def __iand__(self, other: object, /) -> typing.Self:
|
|
124
|
+
return self.__and__(other)
|
|
125
|
+
|
|
126
|
+
|
|
127
|
+
def resolve_arg_names(
|
|
128
|
+
func: AnyFunction,
|
|
129
|
+
/,
|
|
130
|
+
*,
|
|
131
|
+
start_idx: int = 1,
|
|
132
|
+
exclude: set[str] | None = None,
|
|
133
|
+
) -> tuple[str, ...]:
|
|
134
|
+
func = _unwrap_func(func)
|
|
135
|
+
return _resolve_arg_names(
|
|
136
|
+
func,
|
|
137
|
+
start_idx=start_idx,
|
|
138
|
+
stop_idx=func.__code__.co_argcount + func.__code__.co_kwonlyargcount,
|
|
139
|
+
exclude=exclude,
|
|
140
|
+
)
|
|
141
|
+
|
|
142
|
+
|
|
143
|
+
def resolve_kwonly_arg_names(
|
|
144
|
+
func: AnyFunction,
|
|
145
|
+
/,
|
|
146
|
+
*,
|
|
147
|
+
start_idx: int = 1,
|
|
148
|
+
exclude: set[str] | None = None,
|
|
149
|
+
) -> tuple[str, ...]:
|
|
150
|
+
func = _unwrap_func(func)
|
|
151
|
+
return _resolve_arg_names(
|
|
152
|
+
func,
|
|
153
|
+
start_idx=func.__code__.co_argcount + start_idx,
|
|
154
|
+
stop_idx=func.__code__.co_argcount + func.__code__.co_kwonlyargcount,
|
|
155
|
+
exclude=exclude,
|
|
156
|
+
)
|
|
157
|
+
|
|
158
|
+
|
|
159
|
+
def resolve_posonly_arg_names(
|
|
160
|
+
func: AnyFunction,
|
|
161
|
+
/,
|
|
162
|
+
*,
|
|
163
|
+
start_idx: int = 1,
|
|
164
|
+
exclude: set[str] | None = None,
|
|
165
|
+
) -> tuple[str, ...]:
|
|
166
|
+
func = _unwrap_func(func)
|
|
167
|
+
return _resolve_arg_names(
|
|
168
|
+
func,
|
|
169
|
+
start_idx=start_idx,
|
|
170
|
+
stop_idx=func.__code__.co_posonlyargcount,
|
|
171
|
+
exclude=exclude,
|
|
172
|
+
)
|
|
173
|
+
|
|
174
|
+
|
|
175
|
+
@lru_cache(maxsize=1024)
|
|
176
|
+
def get_default_args(func: AnyFunction, /) -> dict[str, typing.Any]:
|
|
177
|
+
parameters = get_func_parameters(func)
|
|
178
|
+
return {
|
|
179
|
+
key: value
|
|
180
|
+
for sublist in (parameters["args"], parameters["kwargs"])
|
|
181
|
+
for key, value in sublist
|
|
182
|
+
if value is not inspect.Parameter.empty
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
|
|
186
|
+
@lru_cache(maxsize=1024)
|
|
187
|
+
def get_func_parameters(func: AnyFunction, /) -> FunctionParameters:
|
|
188
|
+
func_params = FunctionParameters(args=[], kwargs=[])
|
|
189
|
+
|
|
190
|
+
for k, p in inspect.signature(func).parameters.items():
|
|
191
|
+
if k in ("self", "cls"):
|
|
192
|
+
continue
|
|
193
|
+
|
|
194
|
+
match p.kind:
|
|
195
|
+
case p.POSITIONAL_OR_KEYWORD | p.POSITIONAL_ONLY:
|
|
196
|
+
func_params["args"].append((k, p.default))
|
|
197
|
+
case p.KEYWORD_ONLY:
|
|
198
|
+
func_params["kwargs"].append((k, p.default))
|
|
199
|
+
case p.VAR_POSITIONAL:
|
|
200
|
+
func_params["var_star_args"] = k
|
|
201
|
+
case p.VAR_KEYWORD:
|
|
202
|
+
func_params["var_star_kwargs"] = k
|
|
203
|
+
|
|
204
|
+
return func_params
|
|
205
|
+
|
|
206
|
+
|
|
207
|
+
@lru_cache(maxsize=1024)
|
|
208
|
+
def get_func_annotations(func: AnyFunction, /) -> MappingAnnotations:
|
|
209
|
+
return Annotations(obj=func).get(
|
|
210
|
+
ignore_failed_evals=True,
|
|
211
|
+
exclude_forward_refs=True,
|
|
212
|
+
cache=False,
|
|
213
|
+
)
|
|
214
|
+
|
|
215
|
+
|
|
216
|
+
def bundle[R](
|
|
217
|
+
function: Function[..., R],
|
|
218
|
+
context: dict[typing.Any, typing.Any],
|
|
219
|
+
/,
|
|
220
|
+
*,
|
|
221
|
+
start_idx: int = 1,
|
|
222
|
+
bundle_kwargs: bool = False,
|
|
223
|
+
typebundle: bool = False,
|
|
224
|
+
omit_defaults: bool = False,
|
|
225
|
+
) -> Bundle[R]:
|
|
226
|
+
if typebundle:
|
|
227
|
+
return Bundle.from_context(
|
|
228
|
+
function,
|
|
229
|
+
{name: context[ann] for name, ann in get_func_annotations(function).items() if ann in context},
|
|
230
|
+
start_idx=start_idx,
|
|
231
|
+
)
|
|
232
|
+
|
|
233
|
+
kwargs = {_to_str(k): v for k, v in context.items()}
|
|
234
|
+
if not bundle_kwargs or "var_star_kwargs" not in get_func_parameters(function):
|
|
235
|
+
names = resolve_arg_names(function, start_idx=start_idx, exclude={"cls", "self"})
|
|
236
|
+
kwargs = {k: v for k, v in kwargs.items() if k in names}
|
|
237
|
+
|
|
238
|
+
return Bundle.from_context(
|
|
239
|
+
function,
|
|
240
|
+
kwargs if omit_defaults else get_default_args(function) | kwargs,
|
|
241
|
+
start_idx=start_idx,
|
|
242
|
+
)
|
|
243
|
+
|
|
244
|
+
|
|
245
|
+
__all__ = (
|
|
246
|
+
"Bundle",
|
|
247
|
+
"bundle",
|
|
248
|
+
"get_default_args",
|
|
249
|
+
"get_func_annotations",
|
|
250
|
+
"get_func_parameters",
|
|
251
|
+
"resolve_arg_names",
|
|
252
|
+
"resolve_kwonly_arg_names",
|
|
253
|
+
"resolve_posonly_arg_names",
|
|
254
|
+
)
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import sys
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
def get_frame_module_name() -> str:
|
|
5
|
+
frame = sys._getframe()
|
|
6
|
+
|
|
7
|
+
while frame:
|
|
8
|
+
if frame.f_globals.get("__name__", "").startswith("telegrinder"):
|
|
9
|
+
frame = frame.f_back
|
|
10
|
+
else:
|
|
11
|
+
break
|
|
12
|
+
|
|
13
|
+
return "<module>" if frame is None else frame.f_globals.get("__name__", "<module>")
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
__all__ = ("get_frame_module_name",)
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
import dataclasses
|
|
2
|
+
import inspect
|
|
3
|
+
import typing
|
|
4
|
+
from collections import OrderedDict
|
|
5
|
+
from functools import wraps
|
|
6
|
+
|
|
7
|
+
from kungfu.library.monad.result import Result
|
|
8
|
+
|
|
9
|
+
from telegrinder.tools.magic.function import get_func_parameters
|
|
10
|
+
from telegrinder.types.methods_utils import get_params
|
|
11
|
+
|
|
12
|
+
if typing.TYPE_CHECKING:
|
|
13
|
+
from telegrinder.api.error import APIError
|
|
14
|
+
from telegrinder.bot.cute_types.base import BaseCute, BaseShortcuts
|
|
15
|
+
|
|
16
|
+
type Executor[T] = typing.Callable[
|
|
17
|
+
[T, str, dict[str, typing.Any]],
|
|
18
|
+
typing.Awaitable[Result[typing.Any, APIError]],
|
|
19
|
+
]
|
|
20
|
+
type CuteMethod[T, **P, R] = typing.Callable[
|
|
21
|
+
typing.Concatenate[T, P],
|
|
22
|
+
typing.Awaitable[Result[R, APIError]],
|
|
23
|
+
]
|
|
24
|
+
type ShortcutMethod[**P, R] = typing.Callable[
|
|
25
|
+
typing.Concatenate[typing.Any, P],
|
|
26
|
+
typing.Coroutine[typing.Any, typing.Any, Result[R, APIError]],
|
|
27
|
+
]
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
@dataclasses.dataclass(slots=True, frozen=True)
|
|
31
|
+
class Shortcut[T]:
|
|
32
|
+
method_name: str
|
|
33
|
+
executor: Executor[T] | None = dataclasses.field(default=None, kw_only=True)
|
|
34
|
+
custom_params: set[str] = dataclasses.field(default_factory=lambda: set[str](), kw_only=True)
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
@typing.overload
|
|
38
|
+
def shortcut[T: BaseCute, S: BaseShortcuts, **P, R](
|
|
39
|
+
method_name: str,
|
|
40
|
+
/,
|
|
41
|
+
*,
|
|
42
|
+
custom_params: set[str] = ...,
|
|
43
|
+
) -> typing.Callable[[CuteMethod[T, P, R] | CuteMethod[S, P, R]], ShortcutMethod[P, R]]: ...
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
@typing.overload
|
|
47
|
+
def shortcut[T: BaseCute, S: BaseShortcuts, **P, R](
|
|
48
|
+
method_name: str,
|
|
49
|
+
/,
|
|
50
|
+
*,
|
|
51
|
+
executor: Executor[T],
|
|
52
|
+
custom_params: set[str] = ...,
|
|
53
|
+
) -> typing.Callable[[CuteMethod[T, P, R] | CuteMethod[S, P, R]], ShortcutMethod[P, R]]: ...
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
def shortcut[**P, R](
|
|
57
|
+
method_name: str,
|
|
58
|
+
/,
|
|
59
|
+
*,
|
|
60
|
+
executor: Executor[typing.Any] | None = None,
|
|
61
|
+
custom_params: set[str] | None = None,
|
|
62
|
+
) -> typing.Callable[[CuteMethod[typing.Any, P, R]], ShortcutMethod[P, R]]:
|
|
63
|
+
"""Decorate a cute method as a shortcut."""
|
|
64
|
+
|
|
65
|
+
def wrapper(func: CuteMethod[typing.Any, P, R]) -> ShortcutMethod[P, R]:
|
|
66
|
+
@wraps(func)
|
|
67
|
+
async def inner(
|
|
68
|
+
self: typing.Any,
|
|
69
|
+
*args: P.args,
|
|
70
|
+
**kwargs: P.kwargs,
|
|
71
|
+
) -> Result[R, typing.Any]:
|
|
72
|
+
if executor is None:
|
|
73
|
+
return await func(self, *args, **kwargs)
|
|
74
|
+
|
|
75
|
+
params: dict[str, typing.Any] = OrderedDict()
|
|
76
|
+
func_params = get_func_parameters(func)
|
|
77
|
+
|
|
78
|
+
for index, (arg, default) in enumerate(func_params["args"]):
|
|
79
|
+
if len(args) > index:
|
|
80
|
+
params[arg] = args[index]
|
|
81
|
+
elif default is not inspect.Parameter.empty:
|
|
82
|
+
params[arg] = default
|
|
83
|
+
|
|
84
|
+
if var_args := func_params.get("var_star_args"):
|
|
85
|
+
params[var_args] = args[len(func_params["args"]) :]
|
|
86
|
+
|
|
87
|
+
for kwarg, default in func_params["kwargs"]:
|
|
88
|
+
params[kwarg] = (
|
|
89
|
+
kwargs.pop(kwarg, default) if default is not inspect.Parameter.empty else kwargs.pop(kwarg)
|
|
90
|
+
)
|
|
91
|
+
|
|
92
|
+
if var_kwargs := func_params.get("var_star_kwargs"):
|
|
93
|
+
params[var_kwargs] = kwargs.copy()
|
|
94
|
+
|
|
95
|
+
return await executor(self, method_name, get_params(params))
|
|
96
|
+
|
|
97
|
+
inner.__shortcut__ = Shortcut( # type: ignore
|
|
98
|
+
method_name=method_name,
|
|
99
|
+
executor=executor,
|
|
100
|
+
custom_params=custom_params or set(),
|
|
101
|
+
)
|
|
102
|
+
return inner # type: ignore
|
|
103
|
+
|
|
104
|
+
return wrapper
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
__all__ = ("Shortcut", "shortcut")
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
import dataclasses
|
|
2
|
+
import operator
|
|
3
|
+
import types
|
|
4
|
+
import typing
|
|
5
|
+
|
|
6
|
+
type Operation = GetAttribute | GetItem | MethodCall
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
def evaluate_operations(
|
|
10
|
+
obj: typing.Any,
|
|
11
|
+
member_name: str,
|
|
12
|
+
operations: list[Operation],
|
|
13
|
+
/,
|
|
14
|
+
) -> typing.Any:
|
|
15
|
+
obj = getattr(obj, member_name)
|
|
16
|
+
|
|
17
|
+
for operation in operations:
|
|
18
|
+
match operation:
|
|
19
|
+
case GetAttribute(attribute_name=attribute_name):
|
|
20
|
+
obj = getattr(obj, attribute_name)
|
|
21
|
+
case GetItem():
|
|
22
|
+
obj = operation[obj]
|
|
23
|
+
case MethodCall():
|
|
24
|
+
obj = operation(obj)
|
|
25
|
+
|
|
26
|
+
return obj
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
@dataclasses.dataclass(slots=True, frozen=True, kw_only=True)
|
|
30
|
+
class MethodCall:
|
|
31
|
+
caller: operator.methodcaller
|
|
32
|
+
|
|
33
|
+
def __call__(self, obj: typing.Any, /) -> typing.Any:
|
|
34
|
+
return self.caller(obj)
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
@dataclasses.dataclass(slots=True, frozen=True, kw_only=True)
|
|
38
|
+
class GetAttribute:
|
|
39
|
+
attribute_name: str
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
@dataclasses.dataclass(slots=True, frozen=True, kw_only=True)
|
|
43
|
+
class GetItem:
|
|
44
|
+
itemgetter: operator.itemgetter[typing.Any]
|
|
45
|
+
|
|
46
|
+
def __getitem__(self, obj: typing.Any, /) -> typing.Any:
|
|
47
|
+
return self.itemgetter(obj)
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
class MemberDescriptorProxy:
|
|
51
|
+
_operations: list[Operation]
|
|
52
|
+
_objclass: type[typing.Any]
|
|
53
|
+
|
|
54
|
+
__slots__ = ("_descriptor", "_operations", "_member_name", "_objclass")
|
|
55
|
+
|
|
56
|
+
def __init__(self, descriptor: types.MemberDescriptorType) -> None:
|
|
57
|
+
self._descriptor = descriptor
|
|
58
|
+
self._member_name = descriptor.__name__
|
|
59
|
+
self._objclass = descriptor.__objclass__
|
|
60
|
+
self._operations = []
|
|
61
|
+
|
|
62
|
+
def __getattr__(self, __name: str) -> typing.Any:
|
|
63
|
+
if __name in getattr(type(self), "__static_attributes__"):
|
|
64
|
+
return super().__getattribute__(__name)
|
|
65
|
+
|
|
66
|
+
self._operations.append(GetAttribute(attribute_name=__name))
|
|
67
|
+
return self
|
|
68
|
+
|
|
69
|
+
def __getitem__(self, __item: typing.Any) -> typing.Any:
|
|
70
|
+
self._operations.append(GetItem(itemgetter=operator.itemgetter(__item)))
|
|
71
|
+
return self
|
|
72
|
+
|
|
73
|
+
def __call__(self, *args: typing.Any, **kwargs: typing.Any) -> typing.Any:
|
|
74
|
+
if not self._operations or not isinstance(operation := self._operations.pop(-1), GetAttribute):
|
|
75
|
+
raise SyntaxError("No operations to call.")
|
|
76
|
+
|
|
77
|
+
self._operations.append(MethodCall(caller=operator.methodcaller(operation.attribute_name, *args, **kwargs)))
|
|
78
|
+
return self
|
|
79
|
+
|
|
80
|
+
def __get__(self, instance: typing.Any, owner: typing.Type[typing.Any]) -> typing.Any:
|
|
81
|
+
if instance is None:
|
|
82
|
+
return self
|
|
83
|
+
return self._descriptor.__get__(instance, owner)
|
|
84
|
+
|
|
85
|
+
def __set__(self, instance: typing.Any, value: typing.Any) -> None:
|
|
86
|
+
self._descriptor.__set__(instance, value)
|
|
87
|
+
|
|
88
|
+
def __delete__(self, instance: typing.Any) -> None:
|
|
89
|
+
self._descriptor.__delete__(instance)
|
|
90
|
+
|
|
91
|
+
def __repr__(self) -> str:
|
|
92
|
+
return self._descriptor.__repr__()
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
__all__ = ("MemberDescriptorProxy", "evaluate_operations")
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import typing
|
|
2
|
+
|
|
3
|
+
from telegrinder.tools.final import Final
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
@typing.final
|
|
7
|
+
class ParseMode(Final):
|
|
8
|
+
MARKDOWNV2: typing.Final[typing.Literal["MarkdownV2"]] = "MarkdownV2"
|
|
9
|
+
HTML: typing.Final[typing.Literal["HTML"]] = "HTML"
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
__all__ = ("ParseMode",)
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
from telegrinder.tools.serialization.abc import ABCDataSerializer
|
|
2
|
+
from telegrinder.tools.serialization.json_ser import JSONSerializer
|
|
3
|
+
from telegrinder.tools.serialization.msgpack_ser import MsgPackSerializer
|
|
4
|
+
|
|
5
|
+
__all__ = ("ABCDataSerializer", "JSONSerializer", "MsgPackSerializer")
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import abc
|
|
2
|
+
import typing
|
|
3
|
+
from functools import cached_property
|
|
4
|
+
|
|
5
|
+
import msgspec
|
|
6
|
+
from kungfu.library.monad.result import Result
|
|
7
|
+
|
|
8
|
+
if typing.TYPE_CHECKING:
|
|
9
|
+
from _typeshed import DataclassInstance
|
|
10
|
+
|
|
11
|
+
type ModelType = msgspec.Struct | DataclassInstance
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class ABCDataSerializer[Data = typing.Any](abc.ABC):
|
|
15
|
+
ident_key: str | None = None
|
|
16
|
+
|
|
17
|
+
@abc.abstractmethod
|
|
18
|
+
def __init__(self, data_type: type[Data], /) -> None:
|
|
19
|
+
pass
|
|
20
|
+
|
|
21
|
+
@cached_property
|
|
22
|
+
def key(self) -> str:
|
|
23
|
+
return self.ident_key + "_" if self.ident_key else ""
|
|
24
|
+
|
|
25
|
+
@abc.abstractmethod
|
|
26
|
+
def serialize(self, data: Data) -> str:
|
|
27
|
+
pass
|
|
28
|
+
|
|
29
|
+
@abc.abstractmethod
|
|
30
|
+
def deserialize(self, serialized_data: str) -> Result[Data, str]:
|
|
31
|
+
pass
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
__all__ = ("ABCDataSerializer",)
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import typing
|
|
2
|
+
|
|
3
|
+
import msgspec
|
|
4
|
+
from kungfu.library.monad.result import Error, Ok, Result
|
|
5
|
+
|
|
6
|
+
from telegrinder.modules import json
|
|
7
|
+
from telegrinder.msgspec_utils import decoder
|
|
8
|
+
from telegrinder.tools.serialization.abc import ABCDataSerializer, ModelType
|
|
9
|
+
from telegrinder.tools.serialization.utils import get_model_ident_key
|
|
10
|
+
|
|
11
|
+
type Json = dict[str, typing.Any] | ModelType
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class JSONSerializer[JsonT: Json](ABCDataSerializer[JsonT]):
|
|
15
|
+
@typing.overload
|
|
16
|
+
def __init__(self, model_t: type[JsonT]) -> None: ...
|
|
17
|
+
|
|
18
|
+
@typing.overload
|
|
19
|
+
def __init__(self, model_t: type[JsonT], *, ident_key: str | None = ...) -> None: ...
|
|
20
|
+
|
|
21
|
+
def __init__(
|
|
22
|
+
self,
|
|
23
|
+
model_t: type[JsonT] = dict[str, typing.Any],
|
|
24
|
+
*,
|
|
25
|
+
ident_key: str | None = None,
|
|
26
|
+
) -> None:
|
|
27
|
+
self.model_t = model_t
|
|
28
|
+
self.ident_key = ident_key or get_model_ident_key(model_t)
|
|
29
|
+
|
|
30
|
+
@classmethod
|
|
31
|
+
def serialize_from_json(cls, data: JsonT, *, ident_key: str | None = None) -> str:
|
|
32
|
+
return cls(type(data), ident_key=ident_key).serialize(data)
|
|
33
|
+
|
|
34
|
+
@classmethod
|
|
35
|
+
def deserialize_to_json(cls, serialized_data: str, model_t: type[JsonT]) -> Result[JsonT, str]:
|
|
36
|
+
return cls(model_t).deserialize(serialized_data)
|
|
37
|
+
|
|
38
|
+
def serialize(self, data: JsonT) -> str:
|
|
39
|
+
return self.key + json.dumps(data)
|
|
40
|
+
|
|
41
|
+
def deserialize(self, serialized_data: str) -> Result[JsonT, str]:
|
|
42
|
+
if not serialized_data.startswith(self.key):
|
|
43
|
+
return Error("Data is not corresponding to key.")
|
|
44
|
+
|
|
45
|
+
data = serialized_data.removeprefix(self.key)
|
|
46
|
+
try:
|
|
47
|
+
data_obj = json.loads(data)
|
|
48
|
+
except (msgspec.ValidationError, msgspec.DecodeError):
|
|
49
|
+
return Error("Cannot decode json.")
|
|
50
|
+
|
|
51
|
+
if not issubclass(self.model_t, dict):
|
|
52
|
+
try:
|
|
53
|
+
return Ok(decoder.convert(data_obj, type=self.model_t))
|
|
54
|
+
except (msgspec.ValidationError, msgspec.DecodeError):
|
|
55
|
+
return Error("Incorrect data.")
|
|
56
|
+
|
|
57
|
+
return Ok(data_obj)
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
__all__ = ("JSONSerializer",)
|