telegrinder 0.1.dev165__py3-none-any.whl → 0.1.dev166__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.

Files changed (88) hide show
  1. telegrinder/__init__.py +22 -0
  2. telegrinder/api/abc.py +1 -1
  3. telegrinder/api/api.py +8 -6
  4. telegrinder/api/error.py +2 -3
  5. telegrinder/bot/__init__.py +14 -0
  6. telegrinder/bot/bot.py +5 -3
  7. telegrinder/bot/cute_types/__init__.py +4 -0
  8. telegrinder/bot/cute_types/base.py +22 -13
  9. telegrinder/bot/cute_types/chat_join_request.py +63 -0
  10. telegrinder/bot/cute_types/chat_member_updated.py +244 -0
  11. telegrinder/bot/cute_types/message.py +34 -7
  12. telegrinder/bot/cute_types/update.py +5 -4
  13. telegrinder/bot/cute_types/utils.py +39 -17
  14. telegrinder/bot/dispatch/__init__.py +9 -1
  15. telegrinder/bot/dispatch/composition.py +10 -8
  16. telegrinder/bot/dispatch/context.py +9 -10
  17. telegrinder/bot/dispatch/dispatch.py +29 -19
  18. telegrinder/bot/dispatch/handler/abc.py +1 -0
  19. telegrinder/bot/dispatch/handler/func.py +29 -6
  20. telegrinder/bot/dispatch/handler/message_reply.py +2 -3
  21. telegrinder/bot/dispatch/middleware/abc.py +2 -4
  22. telegrinder/bot/dispatch/process.py +4 -3
  23. telegrinder/bot/dispatch/return_manager/__init__.py +1 -1
  24. telegrinder/bot/dispatch/return_manager/abc.py +33 -21
  25. telegrinder/bot/dispatch/return_manager/callback_query.py +4 -2
  26. telegrinder/bot/dispatch/return_manager/inline_query.py +4 -2
  27. telegrinder/bot/dispatch/return_manager/message.py +12 -6
  28. telegrinder/bot/dispatch/view/__init__.py +8 -2
  29. telegrinder/bot/dispatch/view/abc.py +26 -20
  30. telegrinder/bot/dispatch/view/box.py +72 -1
  31. telegrinder/bot/dispatch/view/callback_query.py +1 -3
  32. telegrinder/bot/dispatch/view/chat_join_request.py +17 -0
  33. telegrinder/bot/dispatch/view/chat_member.py +26 -0
  34. telegrinder/bot/dispatch/view/message.py +23 -1
  35. telegrinder/bot/dispatch/view/raw.py +116 -0
  36. telegrinder/bot/dispatch/waiter_machine/__init__.py +2 -1
  37. telegrinder/bot/dispatch/waiter_machine/machine.py +73 -19
  38. telegrinder/bot/dispatch/waiter_machine/middleware.py +3 -3
  39. telegrinder/bot/dispatch/waiter_machine/short_state.py +6 -3
  40. telegrinder/bot/polling/polling.py +4 -2
  41. telegrinder/bot/rules/__init__.py +20 -12
  42. telegrinder/bot/rules/abc.py +0 -9
  43. telegrinder/bot/rules/adapter/event.py +31 -22
  44. telegrinder/bot/rules/callback_data.py +15 -20
  45. telegrinder/bot/rules/chat_join.py +47 -0
  46. telegrinder/bot/rules/enum_text.py +7 -2
  47. telegrinder/bot/rules/inline.py +3 -3
  48. telegrinder/bot/rules/is_from.py +36 -50
  49. telegrinder/bot/rules/message.py +17 -0
  50. telegrinder/bot/rules/message_entities.py +1 -1
  51. telegrinder/bot/rules/start.py +6 -4
  52. telegrinder/bot/rules/text.py +2 -1
  53. telegrinder/bot/rules/update.py +16 -0
  54. telegrinder/bot/scenario/checkbox.py +9 -7
  55. telegrinder/client/aiohttp.py +4 -4
  56. telegrinder/model.py +33 -19
  57. telegrinder/modules.py +16 -32
  58. telegrinder/msgspec_utils.py +37 -36
  59. telegrinder/node/__init__.py +12 -12
  60. telegrinder/node/attachment.py +15 -5
  61. telegrinder/node/base.py +24 -16
  62. telegrinder/node/composer.py +8 -6
  63. telegrinder/node/container.py +1 -1
  64. telegrinder/node/rule.py +4 -4
  65. telegrinder/node/source.py +4 -2
  66. telegrinder/node/tools/generator.py +1 -1
  67. telegrinder/tools/__init__.py +1 -1
  68. telegrinder/tools/error_handler/abc.py +4 -3
  69. telegrinder/tools/error_handler/error_handler.py +22 -16
  70. telegrinder/tools/formatting/html.py +15 -7
  71. telegrinder/tools/formatting/spec_html_formats.py +1 -1
  72. telegrinder/tools/global_context/abc.py +5 -1
  73. telegrinder/tools/global_context/telegrinder_ctx.py +1 -1
  74. telegrinder/tools/i18n/base.py +4 -3
  75. telegrinder/tools/i18n/simple.py +1 -3
  76. telegrinder/tools/keyboard.py +1 -1
  77. telegrinder/tools/loop_wrapper/loop_wrapper.py +24 -16
  78. telegrinder/tools/magic.py +1 -1
  79. telegrinder/types/__init__.py +206 -0
  80. telegrinder/types/enums.py +34 -0
  81. telegrinder/types/methods.py +52 -47
  82. telegrinder/types/objects.py +531 -88
  83. telegrinder/verification_utils.py +3 -1
  84. {telegrinder-0.1.dev165.dist-info → telegrinder-0.1.dev166.dist-info}/METADATA +1 -1
  85. telegrinder-0.1.dev166.dist-info/RECORD +136 -0
  86. telegrinder-0.1.dev165.dist-info/RECORD +0 -128
  87. {telegrinder-0.1.dev165.dist-info → telegrinder-0.1.dev166.dist-info}/LICENSE +0 -0
  88. {telegrinder-0.1.dev165.dist-info → telegrinder-0.1.dev166.dist-info}/WHEEL +0 -0
@@ -15,15 +15,14 @@ else:
15
15
 
16
16
  datetime = type("datetime", (dt,), {})
17
17
 
18
-
19
18
  class OptionMeta(type):
20
19
  def __instancecheck__(cls, __instance: typing.Any) -> bool:
21
20
  return isinstance(__instance, fntypes.option.Some | fntypes.option.Nothing)
22
21
 
23
-
24
22
  class Option(typing.Generic[Value], metaclass=OptionMeta):
25
23
  pass
26
24
 
25
+
27
26
  T = typing.TypeVar("T")
28
27
  Ts = typing.TypeVarTuple("Ts")
29
28
 
@@ -51,13 +50,13 @@ def msgspec_convert(obj: typing.Any, t: type[T]) -> Result[T, msgspec.Validation
51
50
  def option_dec_hook(tp: type[Option[typing.Any]], obj: typing.Any) -> Option[typing.Any]:
52
51
  if obj is None:
53
52
  return Nothing
54
- value_type, = typing.get_args(tp) or (typing.Any,)
53
+ (value_type,) = typing.get_args(tp) or (typing.Any,)
55
54
  return msgspec_convert({"value": obj}, fntypes.option.Some[value_type]).unwrap()
56
55
 
57
56
 
58
57
  def variative_dec_hook(tp: type[Variative], obj: typing.Any) -> Variative:
59
58
  union_types = typing.get_args(tp)
60
-
59
+
61
60
  if isinstance(obj, dict):
62
61
  struct_fields_match_sums: dict[type[msgspec.Struct], int] = {
63
62
  m: sum(1 for k in obj if k in m.__struct_fields__)
@@ -67,15 +66,23 @@ def variative_dec_hook(tp: type[Variative], obj: typing.Any) -> Variative:
67
66
  union_types = tuple(t for t in union_types if t not in struct_fields_match_sums)
68
67
  reverse = False
69
68
 
70
- if len(set(struct_fields_match_sums.values())) != len(struct_fields_match_sums.values()):
71
- struct_fields_match_sums = {m: len(m.__struct_fields__) for m in struct_fields_match_sums}
69
+ if len(set(struct_fields_match_sums.values())) != len(
70
+ struct_fields_match_sums.values()
71
+ ):
72
+ struct_fields_match_sums = {
73
+ m: len(m.__struct_fields__) for m in struct_fields_match_sums
74
+ }
72
75
  reverse = True
73
76
 
74
77
  union_types = (
75
- *sorted(struct_fields_match_sums, key=lambda k: struct_fields_match_sums[k], reverse=reverse),
78
+ *sorted(
79
+ struct_fields_match_sums,
80
+ key=lambda k: struct_fields_match_sums[k],
81
+ reverse=reverse,
82
+ ),
76
83
  *union_types,
77
84
  )
78
-
85
+
79
86
  for t in union_types:
80
87
  match msgspec_convert(obj, t):
81
88
  case Ok(value):
@@ -92,7 +99,7 @@ def variative_dec_hook(tp: type[Variative], obj: typing.Any) -> Variative:
92
99
  class Decoder:
93
100
  """Class `Decoder` for `msgspec` module with decode hook
94
101
  for objects with the specified type.
95
-
102
+
96
103
  ```
97
104
  import enum
98
105
 
@@ -122,7 +129,7 @@ class Decoder:
122
129
  Variative: variative_dec_hook,
123
130
  datetime: lambda t, obj: t.fromtimestamp(obj),
124
131
  }
125
-
132
+
126
133
  def __repr__(self) -> str:
127
134
  return "<{}: dec_hooks={!r}>".format(
128
135
  self.__class__.__name__,
@@ -132,9 +139,9 @@ class Decoder:
132
139
  def add_dec_hook(self, t: T): # type: ignore
133
140
  def decorator(func: DecHook[T]) -> DecHook[T]:
134
141
  return self.dec_hooks.setdefault(get_origin(t), func) # type: ignore
135
-
142
+
136
143
  return decorator
137
-
144
+
138
145
  def dec_hook(self, tp: type[typing.Any], obj: object) -> object:
139
146
  origin_type = t if isinstance((t := get_origin(tp)), type) else type(t)
140
147
  if origin_type not in self.dec_hooks:
@@ -143,7 +150,7 @@ class Decoder:
143
150
  "You can implement decode hook for this type."
144
151
  )
145
152
  return self.dec_hooks[origin_type](tp, obj)
146
-
153
+
147
154
  def convert(
148
155
  self,
149
156
  obj: object,
@@ -163,14 +170,12 @@ class Decoder:
163
170
  builtin_types=builtin_types,
164
171
  str_keys=str_keys,
165
172
  )
166
-
173
+
167
174
  @typing.overload
168
- def decode(self, buf: str | bytes) -> typing.Any:
169
- ...
170
-
175
+ def decode(self, buf: str | bytes) -> typing.Any: ...
176
+
171
177
  @typing.overload
172
- def decode(self, buf: str | bytes, *, strict: bool = True) -> typing.Any:
173
- ...
178
+ def decode(self, buf: str | bytes, *, strict: bool = True) -> typing.Any: ...
174
179
 
175
180
  @typing.overload
176
181
  def decode(
@@ -179,8 +184,7 @@ class Decoder:
179
184
  *,
180
185
  type: type[T],
181
186
  strict: bool = True,
182
- ) -> T:
183
- ...
187
+ ) -> T: ...
184
188
 
185
189
  def decode(
186
190
  self,
@@ -199,7 +203,7 @@ class Decoder:
199
203
 
200
204
  class Encoder:
201
205
  """Class `Encoder` for `msgspec` module with encode hooks for objects.
202
-
206
+
203
207
  ```
204
208
  from datetime import datetime as dt
205
209
 
@@ -231,9 +235,9 @@ class Encoder:
231
235
  def decorator(func: EncHook[T]) -> EncHook[T]:
232
236
  encode_hook = self.enc_hooks.setdefault(get_origin(t), func)
233
237
  return func if encode_hook is not func else encode_hook
234
-
238
+
235
239
  return decorator
236
-
240
+
237
241
  def enc_hook(self, obj: object) -> object:
238
242
  origin_type = get_origin(obj.__class__)
239
243
  if origin_type not in self.enc_hooks:
@@ -244,16 +248,13 @@ class Encoder:
244
248
  return self.enc_hooks[origin_type](obj)
245
249
 
246
250
  @typing.overload
247
- def encode(self, obj: typing.Any) -> str:
248
- ...
249
-
251
+ def encode(self, obj: typing.Any) -> str: ...
252
+
250
253
  @typing.overload
251
- def encode(self, obj: typing.Any, *, as_str: typing.Literal[True]) -> str:
252
- ...
254
+ def encode(self, obj: typing.Any, *, as_str: typing.Literal[True]) -> str: ...
253
255
 
254
256
  @typing.overload
255
- def encode(self, obj: typing.Any, *, as_str: typing.Literal[False]) -> bytes:
256
- ...
257
+ def encode(self, obj: typing.Any, *, as_str: typing.Literal[False]) -> bytes: ...
257
258
 
258
259
  def encode(self, obj: typing.Any, *, as_str: bool = True) -> str | bytes:
259
260
  buf = msgspec.json.encode(obj, enc_hook=self.enc_hook)
@@ -267,14 +268,14 @@ encoder: typing.Final[Encoder] = Encoder()
267
268
  __all__ = (
268
269
  "Decoder",
269
270
  "Encoder",
270
- "Option",
271
271
  "Nothing",
272
+ "Option",
273
+ "datetime",
274
+ "decoder",
275
+ "encoder",
272
276
  "get_origin",
273
- "repr_type",
274
277
  "msgspec_convert",
275
278
  "option_dec_hook",
279
+ "repr_type",
276
280
  "variative_dec_hook",
277
- "datetime",
278
- "decoder",
279
- "encoder",
280
281
  )
@@ -10,22 +10,22 @@ from .tools import generate
10
10
  from .update import UpdateNode
11
11
 
12
12
  __all__ = (
13
- "Node",
14
- "DataNode",
15
- "ScalarNode",
16
13
  "Attachment",
17
- "Photo",
18
- "Video",
19
- "Text",
20
14
  "Audio",
21
- "UpdateNode",
22
- "compose_node",
23
15
  "ComposeError",
16
+ "ContainerNode",
17
+ "DataNode",
24
18
  "MessageNode",
25
- "Source",
26
- "NodeSession",
19
+ "Node",
27
20
  "NodeCollection",
28
- "ContainerNode",
29
- "generate",
21
+ "NodeSession",
22
+ "Photo",
30
23
  "RuleContext",
24
+ "ScalarNode",
25
+ "Source",
26
+ "Text",
27
+ "UpdateNode",
28
+ "Video",
29
+ "compose_node",
30
+ "generate",
31
31
  )
@@ -14,11 +14,21 @@ from .message import MessageNode
14
14
  class Attachment(DataNode):
15
15
  attachment_type: typing.Literal["audio", "document", "photo", "poll", "video"]
16
16
  _: dataclasses.KW_ONLY
17
- audio: Option[telegrinder.types.Audio] = dataclasses.field(default_factory=lambda: Nothing())
18
- document: Option[telegrinder.types.Document] = dataclasses.field(default_factory=lambda: Nothing())
19
- photo: Option[list[telegrinder.types.PhotoSize]] = dataclasses.field(default_factory=lambda: Nothing())
20
- poll: Option[telegrinder.types.Poll] = dataclasses.field(default_factory=lambda: Nothing())
21
- video: Option[telegrinder.types.Video] = dataclasses.field(default_factory=lambda: Nothing())
17
+ audio: Option[telegrinder.types.Audio] = dataclasses.field(
18
+ default_factory=lambda: Nothing()
19
+ )
20
+ document: Option[telegrinder.types.Document] = dataclasses.field(
21
+ default_factory=lambda: Nothing()
22
+ )
23
+ photo: Option[list[telegrinder.types.PhotoSize]] = dataclasses.field(
24
+ default_factory=lambda: Nothing()
25
+ )
26
+ poll: Option[telegrinder.types.Poll] = dataclasses.field(
27
+ default_factory=lambda: Nothing()
28
+ )
29
+ video: Option[telegrinder.types.Video] = dataclasses.field(
30
+ default_factory=lambda: Nothing()
31
+ )
22
32
 
23
33
  @classmethod
24
34
  async def compose(cls, message: MessageNode) -> "Attachment":
telegrinder/node/base.py CHANGED
@@ -2,11 +2,13 @@ import abc
2
2
  import inspect
3
3
  import typing
4
4
 
5
- ComposeResult: typing.TypeAlias = typing.Coroutine[typing.Any, typing.Any, typing.Any] | typing.AsyncGenerator[typing.Any, None]
5
+ ComposeResult: typing.TypeAlias = (
6
+ typing.Coroutine[typing.Any, typing.Any, typing.Any]
7
+ | typing.AsyncGenerator[typing.Any, None]
8
+ )
6
9
 
7
10
 
8
- class ComposeError(BaseException):
9
- ...
11
+ class ComposeError(BaseException): ...
10
12
 
11
13
 
12
14
  class Node(abc.ABC):
@@ -14,7 +16,9 @@ class Node(abc.ABC):
14
16
 
15
17
  @classmethod
16
18
  @abc.abstractmethod
17
- def compose(cls, *args: tuple[typing.Any, ...], **kwargs: typing.Any) -> ComposeResult:
19
+ def compose(
20
+ cls, *args: tuple[typing.Any, ...], **kwargs: typing.Any
21
+ ) -> ComposeResult:
18
22
  pass
19
23
 
20
24
  @classmethod
@@ -24,7 +28,7 @@ class Node(abc.ABC):
24
28
  @classmethod
25
29
  def get_sub_nodes(cls) -> dict[str, type[typing.Self]]:
26
30
  parameters = inspect.signature(cls.compose).parameters
27
-
31
+
28
32
  sub_nodes = {}
29
33
  for name, param in parameters.items():
30
34
  if param.annotation is inspect._empty:
@@ -32,11 +36,11 @@ class Node(abc.ABC):
32
36
  node = param.annotation
33
37
  sub_nodes[name] = node
34
38
  return sub_nodes
35
-
39
+
36
40
  @classmethod
37
41
  def as_node(cls) -> type[typing.Self]:
38
42
  return cls
39
-
43
+
40
44
  @classmethod
41
45
  def is_generator(cls) -> bool:
42
46
  return inspect.isasyncgenfunction(cls.compose)
@@ -48,14 +52,18 @@ class DataNode(Node, abc.ABC):
48
52
  @typing.dataclass_transform()
49
53
  @classmethod
50
54
  @abc.abstractmethod
51
- async def compose(cls, *args: tuple[typing.Any, ...], **kwargs: typing.Any) -> ComposeResult:
55
+ async def compose(
56
+ cls, *args: tuple[typing.Any, ...], **kwargs: typing.Any
57
+ ) -> ComposeResult:
52
58
  pass
53
59
 
54
60
 
55
61
  class ScalarNodeProto(Node, abc.ABC):
56
62
  @classmethod
57
63
  @abc.abstractmethod
58
- async def compose(cls, *args: tuple[typing.Any, ...], **kwargs: typing.Any) -> ComposeResult:
64
+ async def compose(
65
+ cls, *args: tuple[typing.Any, ...], **kwargs: typing.Any
66
+ ) -> ComposeResult:
59
67
  pass
60
68
 
61
69
 
@@ -64,9 +72,9 @@ SCALAR_NODE = type("ScalarNode", (), {"node": "scalar"})
64
72
 
65
73
  if typing.TYPE_CHECKING:
66
74
 
67
- class ScalarNode(ScalarNodeProto, abc.ABC):
75
+ class ScalarNode(ScalarNodeProto, abc.ABC):
68
76
  pass
69
-
77
+
70
78
  else:
71
79
 
72
80
  def create_node(cls, bases, dct):
@@ -75,8 +83,8 @@ else:
75
83
 
76
84
  def create_class(name, bases, dct):
77
85
  return type(
78
- "Scalar",
79
- (SCALAR_NODE,),
86
+ "Scalar",
87
+ (SCALAR_NODE,),
80
88
  {"as_node": classmethod(lambda cls: create_node(cls, bases, dct))},
81
89
  )
82
90
 
@@ -85,9 +93,9 @@ else:
85
93
 
86
94
 
87
95
  __all__ = (
88
- "ScalarNode",
89
- "SCALAR_NODE",
96
+ "ComposeError",
90
97
  "DataNode",
91
98
  "Node",
92
- "ComposeError",
99
+ "SCALAR_NODE",
100
+ "ScalarNode",
93
101
  )
@@ -6,7 +6,7 @@ from telegrinder.node import Node
6
6
 
7
7
  class NodeSession:
8
8
  def __init__(
9
- self,
9
+ self,
10
10
  value: typing.Any,
11
11
  subnodes: dict[str, typing.Self],
12
12
  generator: typing.AsyncGenerator[typing.Any, None] | None = None,
@@ -14,11 +14,11 @@ class NodeSession:
14
14
  self.value = value
15
15
  self.subnodes = subnodes
16
16
  self.generator = generator
17
-
17
+
18
18
  async def close(self, with_value: typing.Any | None = None) -> None:
19
19
  for subnode in self.subnodes.values():
20
20
  await subnode.close()
21
-
21
+
22
22
  if self.generator is None:
23
23
  return
24
24
  try:
@@ -27,7 +27,9 @@ class NodeSession:
27
27
  self.generator = None
28
28
 
29
29
  def __repr__(self) -> str:
30
- return f"<{self.__class__.__name__}: {self.value}" + ("ACTIVE>" if self.generator else ">")
30
+ return f"<{self.__class__.__name__}: {self.value}" + (
31
+ "ACTIVE>" if self.generator else ">"
32
+ )
31
33
 
32
34
 
33
35
  class NodeCollection:
@@ -36,7 +38,7 @@ class NodeCollection:
36
38
 
37
39
  def values(self) -> dict[str, typing.Any]:
38
40
  return {name: session.value for name, session in self.sessions.items()}
39
-
41
+
40
42
  async def close_all(self, with_value: typing.Any | None = None) -> None:
41
43
  for session in self.sessions.values():
42
44
  await session.close(with_value)
@@ -64,7 +66,7 @@ async def compose_node(
64
66
  else:
65
67
  generator = None
66
68
  value = await _node.compose(**context.values()) # type: ignore
67
-
69
+
68
70
  return NodeSession(value, context.sessions, generator)
69
71
 
70
72
 
@@ -13,7 +13,7 @@ class ContainerNode(Node):
13
13
  @classmethod
14
14
  def get_sub_nodes(cls) -> dict[str, type["Node"]]:
15
15
  return {f"node_{i}": node_t for i, node_t in enumerate(cls.linked_nodes)}
16
-
16
+
17
17
  @classmethod
18
18
  def link_nodes(cls, linked_nodes: list[type[Node]]) -> type["ContainerNode"]:
19
19
  return type("_ContainerNode", (cls,), {"linked_nodes": linked_nodes})
telegrinder/node/rule.py CHANGED
@@ -26,7 +26,7 @@ class RuleContext(dict):
26
26
  @classmethod
27
27
  def as_node(cls) -> type[typing.Self]:
28
28
  return cls
29
-
29
+
30
30
  @classmethod
31
31
  def get_sub_nodes(cls) -> dict:
32
32
  return {"update": UpdateNode}
@@ -37,16 +37,16 @@ class RuleContext(dict):
37
37
 
38
38
  def __new__(cls, *rules: ABCRule) -> type[Node]:
39
39
  return type("_RuleNode", (cls,), {"dataclass": dict, "rules": rules}) # type: ignore
40
-
40
+
41
41
  def __class_getitem__(cls, item: tuple[ABCRule, ...]) -> typing.Self:
42
42
  if not isinstance(item, tuple):
43
43
  item = (item,)
44
44
  return cls(*item)
45
-
45
+
46
46
  @staticmethod
47
47
  def generate_dataclass(cls_: type["RuleContext"]): # noqa: ANN205
48
48
  return dataclasses.dataclass(type(cls_.__name__, (object,), dict(cls_.__dict__)))
49
-
49
+
50
50
  def __init_subclass__(cls) -> None:
51
51
  if cls.__name__ == "_RuleNode":
52
52
  return
@@ -21,9 +21,11 @@ class Source(DataNode):
21
21
  chat=message.chat,
22
22
  thread_id=message.message_thread_id.unwrap_or_none(),
23
23
  )
24
-
24
+
25
25
  async def send(self, text: str) -> Message:
26
- result = await self.api.send_message(self.chat.id, message_thread_id=self.thread_id, text=text)
26
+ result = await self.api.send_message(
27
+ self.chat.id, message_thread_id=self.thread_id, text=text
28
+ )
27
29
  return result.unwrap()
28
30
 
29
31
 
@@ -20,7 +20,7 @@ def error_on_none(value: T | None) -> T:
20
20
 
21
21
 
22
22
  def generate(
23
- subnodes: tuple[type[Node], ...],
23
+ subnodes: tuple[type[Node], ...],
24
24
  func: typing.Callable[..., typing.Any],
25
25
  casts: tuple[typing.Callable, ...] = (cast_false_to_none, error_on_none),
26
26
  ) -> type[ContainerNode]:
@@ -98,8 +98,8 @@ __all__ = (
98
98
  "Keyboard",
99
99
  "KeyboardSetBase",
100
100
  "KeyboardSetYAML",
101
- "Link",
102
101
  "Lifespan",
102
+ "Link",
103
103
  "LoopWrapper",
104
104
  "Mention",
105
105
  "ParseMode",
@@ -4,10 +4,9 @@ from abc import ABC, abstractmethod
4
4
  from fntypes.result import Result
5
5
 
6
6
  from telegrinder.api import ABCAPI
7
- from telegrinder.bot.cute_types import BaseCute
8
7
  from telegrinder.bot.dispatch.context import Context
9
8
 
10
- EventT = typing.TypeVar("EventT", bound=BaseCute)
9
+ EventT = typing.TypeVar("EventT")
11
10
  Handler = typing.Callable[typing.Concatenate[EventT, ...], typing.Awaitable[typing.Any]]
12
11
 
13
12
 
@@ -17,7 +16,9 @@ class ABCErrorHandler(ABC, typing.Generic[EventT]):
17
16
  self,
18
17
  *args: typing.Any,
19
18
  **kwargs: typing.Any,
20
- ) -> typing.Callable[[typing.Callable[..., typing.Any]], typing.Callable[..., typing.Any]]:
19
+ ) -> typing.Callable[
20
+ [typing.Callable[..., typing.Any]], typing.Callable[..., typing.Any]
21
+ ]:
21
22
  """Decorator for registering callback as an error handler."""
22
23
 
23
24
  @abstractmethod
@@ -13,7 +13,9 @@ from .error import CatcherError
13
13
 
14
14
  F = typing.TypeVar("F", bound="FuncCatcher")
15
15
  ExceptionT = typing.TypeVar("ExceptionT", bound=BaseException, contravariant=True)
16
- FuncCatcher = typing.Callable[typing.Concatenate[ExceptionT, ...], typing.Awaitable[typing.Any]]
16
+ FuncCatcher = typing.Callable[
17
+ typing.Concatenate[ExceptionT, ...], typing.Awaitable[typing.Any]
18
+ ]
17
19
 
18
20
 
19
21
  @dataclasses.dataclass(frozen=True, repr=False)
@@ -71,7 +73,7 @@ class Catcher(typing.Generic[EventT]):
71
73
  )
72
74
  logger.debug("Failed to match exception {!r}!", exception.__class__.__name__)
73
75
  return Error(exception)
74
-
76
+
75
77
  def match_exception(self, exception: BaseException) -> bool:
76
78
  for exc in self.exceptions:
77
79
  if isinstance(exc, type) and type(exception) is exc:
@@ -84,10 +86,11 @@ class Catcher(typing.Generic[EventT]):
84
86
  class ErrorHandler(ABCErrorHandler[EventT]):
85
87
  def __init__(self, catcher: Catcher[EventT] | None = None, /) -> None:
86
88
  self.catcher = catcher
87
-
89
+
88
90
  def __repr__(self) -> str:
89
91
  return (
90
- "<ErrorHandler: exceptions_handled=[{}], catcher={!r}>".format(
92
+ "<{}: exceptions_handled=[{}], catcher={!r}>".format(
93
+ self.__class__.__name__,
91
94
  ", ".join(
92
95
  e.__name__ if isinstance(e, type) else repr(e)
93
96
  for e in self.catcher.exceptions
@@ -95,7 +98,7 @@ class ErrorHandler(ABCErrorHandler[EventT]):
95
98
  self.catcher,
96
99
  )
97
100
  if self.catcher is not None
98
- else "<ErrorHandler: No catcher>"
101
+ else "<{}: No catcher>".format(self.__class__.__name__)
99
102
  )
100
103
 
101
104
  def __call__(
@@ -121,8 +124,9 @@ class ErrorHandler(ABCErrorHandler[EventT]):
121
124
  ignore_errors=ignore_errors,
122
125
  )
123
126
  return func
127
+
124
128
  return decorator
125
-
129
+
126
130
  async def process(
127
131
  self,
128
132
  handler: Handler[EventT],
@@ -135,23 +139,25 @@ class ErrorHandler(ABCErrorHandler[EventT]):
135
139
  try:
136
140
  return await self.catcher(handler, event, api, ctx)
137
141
  except BaseException as exc:
138
- return Error(CatcherError(
139
- exc,
140
- "Exception {!r} was occurred during the running catcher {!r}.".format(
141
- repr(exc), self.catcher.func.__name__
142
+ return Error(
143
+ CatcherError(
144
+ exc,
145
+ "Exception {!r} was occurred during the running catcher {!r}.".format(
146
+ repr(exc), self.catcher.func.__name__
147
+ ),
142
148
  )
143
- ))
144
-
149
+ )
150
+
145
151
  def process_catcher_error(self, error: CatcherError) -> Result[None, str]:
146
152
  assert self.catcher is not None
147
-
153
+
148
154
  if self.catcher.raise_exception:
149
155
  raise error.exc from None
150
156
  if not self.catcher.ignore_errors:
151
157
  return Error(error.error)
152
158
  if self.catcher.logging:
153
159
  logger.error(error.error)
154
-
160
+
155
161
  return Ok(None)
156
162
 
157
163
  async def run(
@@ -163,13 +169,13 @@ class ErrorHandler(ABCErrorHandler[EventT]):
163
169
  ) -> Result[typing.Any, typing.Any]:
164
170
  if not self.catcher:
165
171
  return Ok(await handler(event, **magic_bundle(handler, ctx))) # type: ignore
166
-
172
+
167
173
  match await self.process(handler, event, api, ctx):
168
174
  case Ok(_) as ok:
169
175
  return ok
170
176
  case Error(exc) as err:
171
177
  if isinstance(exc, CatcherError):
172
- return self.process_catcher_error(exc)
178
+ return self.process_catcher_error(exc)
173
179
  if self.catcher.ignore_errors:
174
180
  return Ok(None)
175
181
  return err
@@ -49,7 +49,9 @@ class StringFormatter(string.Formatter):
49
49
  )
50
50
  return fmt
51
51
 
52
- def get_spec_formatter(self, value: SpecialFormat) -> typing.Callable[..., "TagFormat"]:
52
+ def get_spec_formatter(
53
+ self, value: SpecialFormat
54
+ ) -> typing.Callable[..., "TagFormat"]:
53
55
  return globals()[value.__formatter_name__]
54
56
 
55
57
  def check_formats(self, value: typing.Any, fmts: list[str]) -> "TagFormat":
@@ -78,11 +80,15 @@ class StringFormatter(string.Formatter):
78
80
  with suppress(ValueError):
79
81
  return HTMLFormatter(
80
82
  format(
81
- value.formatting()
82
- if isinstance(value, TagFormat)
83
- else self.get_spec_formatter(value)(**value.__dict__).formatting()
84
- if is_spec_format(value)
85
- else value,
83
+ (
84
+ value.formatting()
85
+ if isinstance(value, TagFormat)
86
+ else (
87
+ self.get_spec_formatter(value)(**value.__dict__).formatting()
88
+ if is_spec_format(value)
89
+ else value
90
+ )
91
+ ),
86
92
  fmt,
87
93
  )
88
94
  )
@@ -235,7 +241,9 @@ def start_bot_link(bot_id: str | int, data: str, string: str | None = None) -> T
235
241
  return link(get_start_bot_link(bot_id, data), string)
236
242
 
237
243
 
238
- def start_group_link(bot_id: str | int, data: str, string: str | None = None) -> TagFormat:
244
+ def start_group_link(
245
+ bot_id: str | int, data: str, string: str | None = None
246
+ ) -> TagFormat:
239
247
  return link(get_start_group_link(bot_id, data), string)
240
248
 
241
249
 
@@ -76,7 +76,7 @@ class PreCode(BaseSpecFormat):
76
76
  @dataclasses.dataclass(repr=False)
77
77
  class TgEmoji(BaseSpecFormat):
78
78
  __formatter_name__ = "tg_emoji"
79
-
79
+
80
80
  string: str
81
81
  emoji_id: int
82
82
 
@@ -35,7 +35,11 @@ class GlobalCtxVar(typing.Generic[T]):
35
35
 
36
36
  @classmethod
37
37
  def collect(cls, name: str, ctx_value: T | CtxVariable[T]) -> typing.Self:
38
- ctx_value = CtxVar(ctx_value) if not isinstance(ctx_value, CtxVar | GlobalCtxVar) else ctx_value
38
+ ctx_value = (
39
+ CtxVar(ctx_value)
40
+ if not isinstance(ctx_value, CtxVar | GlobalCtxVar)
41
+ else ctx_value
42
+ )
39
43
  params = ctx_value.__dict__
40
44
  params["name"] = name
41
45
  return cls(**params)