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.
Files changed (215) hide show
  1. telegrinder/__init__.py +258 -0
  2. telegrinder/__meta__.py +1 -0
  3. telegrinder/api/__init__.py +15 -0
  4. telegrinder/api/api.py +175 -0
  5. telegrinder/api/error.py +50 -0
  6. telegrinder/api/response.py +23 -0
  7. telegrinder/api/token.py +30 -0
  8. telegrinder/api/validators.py +30 -0
  9. telegrinder/bot/__init__.py +144 -0
  10. telegrinder/bot/bot.py +70 -0
  11. telegrinder/bot/cute_types/__init__.py +41 -0
  12. telegrinder/bot/cute_types/base.py +228 -0
  13. telegrinder/bot/cute_types/base.pyi +49 -0
  14. telegrinder/bot/cute_types/business_connection.py +9 -0
  15. telegrinder/bot/cute_types/business_messages_deleted.py +9 -0
  16. telegrinder/bot/cute_types/callback_query.py +248 -0
  17. telegrinder/bot/cute_types/chat_boost_removed.py +9 -0
  18. telegrinder/bot/cute_types/chat_boost_updated.py +9 -0
  19. telegrinder/bot/cute_types/chat_join_request.py +59 -0
  20. telegrinder/bot/cute_types/chat_member_updated.py +158 -0
  21. telegrinder/bot/cute_types/chosen_inline_result.py +11 -0
  22. telegrinder/bot/cute_types/inline_query.py +41 -0
  23. telegrinder/bot/cute_types/message.py +2809 -0
  24. telegrinder/bot/cute_types/message_reaction_count_updated.py +9 -0
  25. telegrinder/bot/cute_types/message_reaction_updated.py +9 -0
  26. telegrinder/bot/cute_types/paid_media_purchased.py +11 -0
  27. telegrinder/bot/cute_types/poll.py +9 -0
  28. telegrinder/bot/cute_types/poll_answer.py +9 -0
  29. telegrinder/bot/cute_types/pre_checkout_query.py +36 -0
  30. telegrinder/bot/cute_types/shipping_query.py +11 -0
  31. telegrinder/bot/cute_types/update.py +209 -0
  32. telegrinder/bot/cute_types/utils.py +141 -0
  33. telegrinder/bot/dispatch/__init__.py +99 -0
  34. telegrinder/bot/dispatch/abc.py +74 -0
  35. telegrinder/bot/dispatch/action.py +99 -0
  36. telegrinder/bot/dispatch/context.py +162 -0
  37. telegrinder/bot/dispatch/dispatch.py +362 -0
  38. telegrinder/bot/dispatch/handler/__init__.py +23 -0
  39. telegrinder/bot/dispatch/handler/abc.py +25 -0
  40. telegrinder/bot/dispatch/handler/audio_reply.py +43 -0
  41. telegrinder/bot/dispatch/handler/base.py +34 -0
  42. telegrinder/bot/dispatch/handler/document_reply.py +43 -0
  43. telegrinder/bot/dispatch/handler/func.py +73 -0
  44. telegrinder/bot/dispatch/handler/media_group_reply.py +43 -0
  45. telegrinder/bot/dispatch/handler/message_reply.py +35 -0
  46. telegrinder/bot/dispatch/handler/photo_reply.py +43 -0
  47. telegrinder/bot/dispatch/handler/sticker_reply.py +36 -0
  48. telegrinder/bot/dispatch/handler/video_reply.py +43 -0
  49. telegrinder/bot/dispatch/middleware/__init__.py +13 -0
  50. telegrinder/bot/dispatch/middleware/abc.py +112 -0
  51. telegrinder/bot/dispatch/middleware/box.py +32 -0
  52. telegrinder/bot/dispatch/middleware/filter.py +88 -0
  53. telegrinder/bot/dispatch/middleware/media_group.py +69 -0
  54. telegrinder/bot/dispatch/process.py +93 -0
  55. telegrinder/bot/dispatch/return_manager/__init__.py +21 -0
  56. telegrinder/bot/dispatch/return_manager/abc.py +107 -0
  57. telegrinder/bot/dispatch/return_manager/callback_query.py +19 -0
  58. telegrinder/bot/dispatch/return_manager/inline_query.py +14 -0
  59. telegrinder/bot/dispatch/return_manager/message.py +34 -0
  60. telegrinder/bot/dispatch/return_manager/pre_checkout_query.py +19 -0
  61. telegrinder/bot/dispatch/return_manager/utils.py +20 -0
  62. telegrinder/bot/dispatch/router/__init__.py +4 -0
  63. telegrinder/bot/dispatch/router/abc.py +15 -0
  64. telegrinder/bot/dispatch/router/base.py +154 -0
  65. telegrinder/bot/dispatch/view/__init__.py +15 -0
  66. telegrinder/bot/dispatch/view/abc.py +15 -0
  67. telegrinder/bot/dispatch/view/base.py +226 -0
  68. telegrinder/bot/dispatch/view/box.py +207 -0
  69. telegrinder/bot/dispatch/view/media_group.py +25 -0
  70. telegrinder/bot/dispatch/waiter_machine/__init__.py +25 -0
  71. telegrinder/bot/dispatch/waiter_machine/actions.py +16 -0
  72. telegrinder/bot/dispatch/waiter_machine/hasher/__init__.py +13 -0
  73. telegrinder/bot/dispatch/waiter_machine/hasher/callback.py +53 -0
  74. telegrinder/bot/dispatch/waiter_machine/hasher/hasher.py +61 -0
  75. telegrinder/bot/dispatch/waiter_machine/hasher/message.py +49 -0
  76. telegrinder/bot/dispatch/waiter_machine/machine.py +264 -0
  77. telegrinder/bot/dispatch/waiter_machine/middleware.py +77 -0
  78. telegrinder/bot/dispatch/waiter_machine/short_state.py +105 -0
  79. telegrinder/bot/polling/__init__.py +4 -0
  80. telegrinder/bot/polling/abc.py +25 -0
  81. telegrinder/bot/polling/error_handler.py +93 -0
  82. telegrinder/bot/polling/polling.py +167 -0
  83. telegrinder/bot/polling/utils.py +12 -0
  84. telegrinder/bot/rules/__init__.py +166 -0
  85. telegrinder/bot/rules/abc.py +150 -0
  86. telegrinder/bot/rules/button.py +20 -0
  87. telegrinder/bot/rules/callback_data.py +109 -0
  88. telegrinder/bot/rules/chat_join.py +28 -0
  89. telegrinder/bot/rules/chat_member_updated.py +145 -0
  90. telegrinder/bot/rules/command.py +137 -0
  91. telegrinder/bot/rules/enum_text.py +29 -0
  92. telegrinder/bot/rules/func.py +21 -0
  93. telegrinder/bot/rules/fuzzy.py +21 -0
  94. telegrinder/bot/rules/inline.py +45 -0
  95. telegrinder/bot/rules/integer.py +19 -0
  96. telegrinder/bot/rules/is_from.py +213 -0
  97. telegrinder/bot/rules/logic.py +22 -0
  98. telegrinder/bot/rules/magic.py +60 -0
  99. telegrinder/bot/rules/markup.py +51 -0
  100. telegrinder/bot/rules/media.py +13 -0
  101. telegrinder/bot/rules/mention.py +15 -0
  102. telegrinder/bot/rules/message_entities.py +37 -0
  103. telegrinder/bot/rules/node.py +43 -0
  104. telegrinder/bot/rules/payload.py +89 -0
  105. telegrinder/bot/rules/payment_invoice.py +14 -0
  106. telegrinder/bot/rules/regex.py +34 -0
  107. telegrinder/bot/rules/rule_enum.py +71 -0
  108. telegrinder/bot/rules/start.py +73 -0
  109. telegrinder/bot/rules/state.py +35 -0
  110. telegrinder/bot/rules/text.py +27 -0
  111. telegrinder/bot/rules/update.py +14 -0
  112. telegrinder/bot/scenario/__init__.py +5 -0
  113. telegrinder/bot/scenario/abc.py +16 -0
  114. telegrinder/bot/scenario/checkbox.py +183 -0
  115. telegrinder/bot/scenario/choice.py +44 -0
  116. telegrinder/client/__init__.py +11 -0
  117. telegrinder/client/abc.py +136 -0
  118. telegrinder/client/form_data.py +34 -0
  119. telegrinder/client/rnet.py +198 -0
  120. telegrinder/model.py +133 -0
  121. telegrinder/model.pyi +57 -0
  122. telegrinder/modules.py +1081 -0
  123. telegrinder/msgspec_utils/__init__.py +42 -0
  124. telegrinder/msgspec_utils/abc.py +16 -0
  125. telegrinder/msgspec_utils/custom_types/__init__.py +6 -0
  126. telegrinder/msgspec_utils/custom_types/datetime.py +24 -0
  127. telegrinder/msgspec_utils/custom_types/enum_meta.py +61 -0
  128. telegrinder/msgspec_utils/custom_types/literal.py +25 -0
  129. telegrinder/msgspec_utils/custom_types/option.py +17 -0
  130. telegrinder/msgspec_utils/decoder.py +388 -0
  131. telegrinder/msgspec_utils/encoder.py +204 -0
  132. telegrinder/msgspec_utils/json.py +15 -0
  133. telegrinder/msgspec_utils/tools.py +80 -0
  134. telegrinder/node/__init__.py +80 -0
  135. telegrinder/node/compose.py +193 -0
  136. telegrinder/node/nodes/__init__.py +96 -0
  137. telegrinder/node/nodes/attachment.py +169 -0
  138. telegrinder/node/nodes/callback_query.py +25 -0
  139. telegrinder/node/nodes/channel.py +97 -0
  140. telegrinder/node/nodes/command.py +33 -0
  141. telegrinder/node/nodes/error.py +43 -0
  142. telegrinder/node/nodes/event.py +70 -0
  143. telegrinder/node/nodes/file.py +39 -0
  144. telegrinder/node/nodes/global_node.py +66 -0
  145. telegrinder/node/nodes/i18n.py +110 -0
  146. telegrinder/node/nodes/me.py +26 -0
  147. telegrinder/node/nodes/message_entities.py +15 -0
  148. telegrinder/node/nodes/payload.py +84 -0
  149. telegrinder/node/nodes/reply_message.py +14 -0
  150. telegrinder/node/nodes/source.py +172 -0
  151. telegrinder/node/nodes/state_mutator.py +71 -0
  152. telegrinder/node/nodes/text.py +62 -0
  153. telegrinder/node/scope.py +88 -0
  154. telegrinder/node/utils.py +38 -0
  155. telegrinder/py.typed +0 -0
  156. telegrinder/rules.py +1 -0
  157. telegrinder/tools/__init__.py +183 -0
  158. telegrinder/tools/aio.py +147 -0
  159. telegrinder/tools/final.py +21 -0
  160. telegrinder/tools/formatting/__init__.py +85 -0
  161. telegrinder/tools/formatting/deep_links/__init__.py +39 -0
  162. telegrinder/tools/formatting/deep_links/links.py +468 -0
  163. telegrinder/tools/formatting/deep_links/parsing.py +88 -0
  164. telegrinder/tools/formatting/deep_links/validators.py +8 -0
  165. telegrinder/tools/formatting/html.py +241 -0
  166. telegrinder/tools/fullname.py +82 -0
  167. telegrinder/tools/global_context/__init__.py +13 -0
  168. telegrinder/tools/global_context/abc.py +63 -0
  169. telegrinder/tools/global_context/builtin_context.py +45 -0
  170. telegrinder/tools/global_context/global_context.py +614 -0
  171. telegrinder/tools/input_file_directory.py +30 -0
  172. telegrinder/tools/keyboard/__init__.py +6 -0
  173. telegrinder/tools/keyboard/abc.py +84 -0
  174. telegrinder/tools/keyboard/base.py +108 -0
  175. telegrinder/tools/keyboard/button.py +181 -0
  176. telegrinder/tools/keyboard/data.py +31 -0
  177. telegrinder/tools/keyboard/keyboard.py +160 -0
  178. telegrinder/tools/keyboard/utils.py +95 -0
  179. telegrinder/tools/lifespan.py +188 -0
  180. telegrinder/tools/limited_dict.py +35 -0
  181. telegrinder/tools/loop_wrapper.py +271 -0
  182. telegrinder/tools/magic/__init__.py +29 -0
  183. telegrinder/tools/magic/annotations.py +172 -0
  184. telegrinder/tools/magic/descriptors.py +57 -0
  185. telegrinder/tools/magic/function.py +254 -0
  186. telegrinder/tools/magic/inspect.py +16 -0
  187. telegrinder/tools/magic/shortcut.py +107 -0
  188. telegrinder/tools/member_descriptor_proxy.py +95 -0
  189. telegrinder/tools/parse_mode.py +12 -0
  190. telegrinder/tools/serialization/__init__.py +5 -0
  191. telegrinder/tools/serialization/abc.py +34 -0
  192. telegrinder/tools/serialization/json_ser.py +60 -0
  193. telegrinder/tools/serialization/msgpack_ser.py +197 -0
  194. telegrinder/tools/serialization/utils.py +18 -0
  195. telegrinder/tools/singleton/__init__.py +4 -0
  196. telegrinder/tools/singleton/abc.py +14 -0
  197. telegrinder/tools/singleton/singleton.py +18 -0
  198. telegrinder/tools/state_mutator/__init__.py +4 -0
  199. telegrinder/tools/state_mutator/mutation.py +85 -0
  200. telegrinder/tools/state_storage/__init__.py +4 -0
  201. telegrinder/tools/state_storage/abc.py +38 -0
  202. telegrinder/tools/state_storage/memory.py +27 -0
  203. telegrinder/tools/strings.py +22 -0
  204. telegrinder/types/__init__.py +323 -0
  205. telegrinder/types/enums.py +754 -0
  206. telegrinder/types/input_file.py +51 -0
  207. telegrinder/types/methods.py +6143 -0
  208. telegrinder/types/methods_utils.py +66 -0
  209. telegrinder/types/objects.py +8184 -0
  210. telegrinder/types/webapp.py +129 -0
  211. telegrinder/verification_utils.py +35 -0
  212. telegrinder-1.0.0rc1.dist-info/METADATA +166 -0
  213. telegrinder-1.0.0rc1.dist-info/RECORD +215 -0
  214. telegrinder-1.0.0rc1.dist-info/WHEEL +4 -0
  215. telegrinder-1.0.0rc1.dist-info/licenses/LICENSE +22 -0
@@ -0,0 +1,241 @@
1
+ import enum
2
+ import html
3
+ import types
4
+ import typing
5
+ from string.templatelib import Template
6
+
7
+ from telegrinder.tools.formatting.deep_links.links import tg_mention_link
8
+ from telegrinder.tools.parse_mode import ParseMode
9
+
10
+ type FormatString = object | Template
11
+ type Formatter = typing.Callable[[FormatString], TagFormat]
12
+
13
+ TAG_FORMAT: typing.Final = "<{tag}{data}>{content}</{tag}>"
14
+ UNION_SPECIFIERS_SEPARATOR: typing.Final = "+"
15
+
16
+
17
+ def blockquote(s: FormatString, /, *, expandable: bool = False) -> TagFormat:
18
+ return TagFormat(s, tag=Tag.BLOCK_QUOTE, **dict(expandable=None) if expandable is True else dict())
19
+
20
+
21
+ def bold(s: FormatString, /) -> TagFormat:
22
+ return TagFormat(s, tag=Tag.BOLD)
23
+
24
+
25
+ def code_inline(s: FormatString, /) -> TagFormat:
26
+ return TagFormat(s, tag=Tag.CODE)
27
+
28
+
29
+ def expandable_blockquote(s: FormatString, /) -> TagFormat:
30
+ return blockquote(s, expandable=True)
31
+
32
+
33
+ monospace = code_inline
34
+
35
+
36
+ def italic(s: FormatString, /) -> TagFormat:
37
+ return TagFormat(s, tag=Tag.ITALIC)
38
+
39
+
40
+ def link(href: FormatString, /, *, text: str | None = None) -> TagFormat:
41
+ return TagFormat(
42
+ text or href,
43
+ tag=Tag.HYPERLINK,
44
+ href=f'"{href}"',
45
+ )
46
+
47
+
48
+ def pre_code(s: FormatString, /, *, lang: str | None = None) -> TagFormat:
49
+ if not lang:
50
+ return TagFormat(s, tag=Tag.PRE)
51
+ return pre_code(TagFormat(s, tag=Tag.CODE, **{"class": f"language-{lang}"}))
52
+
53
+
54
+ def spoiler(s: FormatString, /) -> TagFormat:
55
+ return TagFormat(s, tag=Tag.SPOILER)
56
+
57
+
58
+ def strike(s: FormatString, /) -> TagFormat:
59
+ return TagFormat(s, tag=Tag.STRIKE)
60
+
61
+
62
+ def mention(s: FormatString, /, *, user_id: int) -> TagFormat:
63
+ text = escape(s) if isinstance(s, Template) else str(s)
64
+ return link(tg_mention_link(user_id=user_id), text=text)
65
+
66
+
67
+ def tg_emoji(s: FormatString, /, *, emoji_id: int) -> TagFormat:
68
+ return TagFormat(s, tag=Tag.EMOJI, emoji_id=emoji_id)
69
+
70
+
71
+ def underline(s: FormatString, /) -> TagFormat:
72
+ return TagFormat(s, tag=Tag.UNDERLINE)
73
+
74
+
75
+ def _apply_formats(value: typing.Any, format_spec: str | None = None) -> str:
76
+ if not format_spec:
77
+ return html.escape(str(value))
78
+
79
+ result: FormatString = html.escape(str(value))
80
+
81
+ for fmt in (f.strip() for f in format_spec.split(UNION_SPECIFIERS_SEPARATOR)):
82
+ if fmt in FORMATTERS:
83
+ result = FORMATTERS[fmt](result)
84
+ else:
85
+ raise ValueError(f"Unknown format: `{fmt}`")
86
+
87
+ return result.formatting() if isinstance(result, TagFormat) else result
88
+
89
+
90
+ def escape(s: FormatString, /) -> str:
91
+ if isinstance(s, Template):
92
+ parts: list[str] = []
93
+
94
+ for item in s:
95
+ if isinstance(item, str):
96
+ parts.append(html.escape(item))
97
+ else:
98
+ value = item.value
99
+
100
+ if isinstance(value, TagFormat):
101
+ parts.append(value.formatting())
102
+ elif item.format_spec:
103
+ parts.append(_apply_formats(value, item.format_spec))
104
+ else:
105
+ parts.append(html.escape(str(value)))
106
+
107
+ return "".join(parts)
108
+
109
+ return html.escape(str(s))
110
+
111
+
112
+ class HTMLMeta(type):
113
+ def __lshift__[T](cls: typing.Callable[..., T], other: object, /) -> T:
114
+ if not isinstance(other, str | Template | TagFormat):
115
+ return NotImplemented
116
+ return cls(other)
117
+
118
+
119
+ class Tag(enum.StrEnum):
120
+ HYPERLINK = "a"
121
+ BOLD = "b"
122
+ ITALIC = "i"
123
+ UNDERLINE = "u"
124
+ STRIKE = "s"
125
+ CODE = "code"
126
+ PRE = "pre"
127
+ SPOILER = "tg-spoiler"
128
+ BLOCK_QUOTE = "blockquote"
129
+ EMOJI = "tg-emoji"
130
+
131
+ def __str__(self) -> str:
132
+ return self.value
133
+
134
+
135
+ class TagFormat(str):
136
+ __slots__ = ("data", "tag")
137
+
138
+ tag: Tag
139
+ data: dict[str, typing.Any]
140
+
141
+ def __new__(cls, s: FormatString, /, *, tag: Tag, **data: typing.Any) -> typing.Self:
142
+ s = escape(s) if not isinstance(s, TagFormat) else s.formatting()
143
+ obj = super().__new__(cls, s)
144
+ obj.data = data
145
+ obj.tag = tag
146
+ return obj
147
+
148
+ def __str__(self) -> str:
149
+ return self.formatting()
150
+
151
+ @property
152
+ def content(self) -> str:
153
+ return super().__str__()
154
+
155
+ @property
156
+ def tag_data(self) -> str:
157
+ return "".join(f" {k}={v}" if v is not None else f" {k}" for k, v in self.data.items())
158
+
159
+ def formatting(self) -> str:
160
+ return TAG_FORMAT.format(
161
+ tag=self.tag,
162
+ data=self.tag_data,
163
+ content=self.content,
164
+ )
165
+
166
+
167
+ class HTML(str, metaclass=HTMLMeta):
168
+ """A class for creating and formatting HTML strings.
169
+
170
+ >>> HTML << "Hello, " << bold("World!")
171
+ 'Hello, <b>World!</b>'
172
+
173
+ >>> HTML << t"Hello, {name:underline}! " << "You are <b>{age}</b> years old."
174
+ 'Hello, <u>Arseny</u>! You are <b>20</b> years old.'
175
+ """
176
+
177
+ PARSE_MODE: typing.Final = ParseMode.HTML
178
+
179
+ __slots__ = ()
180
+
181
+ def __new__(cls, s: str | Template | TagFormat, /) -> typing.Self:
182
+ if isinstance(s, Template):
183
+ content = escape(s)
184
+ elif isinstance(s, TagFormat):
185
+ content = s.formatting()
186
+ else:
187
+ content = s
188
+
189
+ return super().__new__(cls, content)
190
+
191
+ def __lshift__(self, other: object, /) -> typing.Self:
192
+ if isinstance(other, TagFormat):
193
+ addition = other.formatting()
194
+ elif isinstance(other, Template):
195
+ addition = escape(other)
196
+ else:
197
+ addition = str(other)
198
+
199
+ return self.__class__.__new__(self.__class__, self + addition)
200
+
201
+
202
+ FORMATTERS: types.MappingProxyType[str, Formatter] = types.MappingProxyType(
203
+ mapping=dict(
204
+ bold=bold,
205
+ b=bold,
206
+ code=code_inline,
207
+ c=code_inline,
208
+ monospace=monospace,
209
+ italic=italic,
210
+ i=italic,
211
+ spoiler=spoiler,
212
+ sp=spoiler,
213
+ strike=strike,
214
+ s=strike,
215
+ underline=underline,
216
+ u=underline,
217
+ blockquote=blockquote,
218
+ expandable_blockquote=expandable_blockquote,
219
+ ),
220
+ )
221
+
222
+
223
+ __all__ = (
224
+ "HTML",
225
+ "Tag",
226
+ "TagFormat",
227
+ "blockquote",
228
+ "bold",
229
+ "code_inline",
230
+ "escape",
231
+ "expandable_blockquote",
232
+ "italic",
233
+ "link",
234
+ "mention",
235
+ "monospace",
236
+ "pre_code",
237
+ "spoiler",
238
+ "strike",
239
+ "tg_emoji",
240
+ "underline",
241
+ )
@@ -0,0 +1,82 @@
1
+ import builtins
2
+ import inspect
3
+ import os.path
4
+ import sys
5
+ import types
6
+ import typing
7
+
8
+ type RoutineMethodType = (
9
+ types.MethodType | types.MethodDescriptorType | types.MethodWrapperType | types.BuiltinMethodType
10
+ )
11
+ type RoutineDescriptorType = types.MethodDescriptorType | types.GetSetDescriptorType
12
+
13
+ _BUILTINS: typing.Final[frozenset[typing.Any]] = frozenset(
14
+ x for name in dir(builtins) if getattr((x := getattr(builtins, name)), "__module__", None) == "builtins"
15
+ )
16
+ _CACHE_KEY: typing.Final = "__fullname_cache__"
17
+
18
+
19
+ def _is_builtin(obj: typing.Any, /) -> bool:
20
+ return inspect.isbuiltin(obj) or obj in _BUILTINS
21
+
22
+
23
+ def _is_routine_method(obj: typing.Any, /) -> typing.TypeIs[RoutineMethodType]:
24
+ return inspect.isbuiltin(obj) or inspect.ismethod(obj) or inspect.ismethodwrapper(obj)
25
+
26
+
27
+ def _is_routine_descriptor(obj: typing.Any, /) -> typing.TypeIs[RoutineDescriptorType]:
28
+ return inspect.ismethoddescriptor(obj) or inspect.isgetsetdescriptor(obj)
29
+
30
+
31
+ def _module_name(module: types.ModuleType, /) -> str:
32
+ if (mod_name := module.__name__) != "__main__":
33
+ return mod_name
34
+
35
+ mod_package = module.__package__ or ""
36
+ mod_file = module.__file__
37
+ if mod_file is None:
38
+ return mod_package
39
+
40
+ mod_fname = os.path.basename(mod_file).removesuffix(".py")
41
+ return mod_package if mod_package in ("__init__", "__main__") else mod_fname
42
+
43
+
44
+ def fullname(obj: object, /) -> str:
45
+ """The full name (`__module__.__name__`) of the object."""
46
+
47
+ if isinstance(obj, staticmethod | classmethod):
48
+ obj = obj.__func__
49
+
50
+ elif (
51
+ not inspect.isroutine(obj)
52
+ and not inspect.isgetsetdescriptor(obj)
53
+ and not isinstance(obj, type)
54
+ and not inspect.ismodule(obj)
55
+ ):
56
+ obj = type(obj)
57
+
58
+ if (name := fullname.__dict__.setdefault(_CACHE_KEY, dict()).get(obj)) is not None:
59
+ return name
60
+
61
+ if inspect.ismodule(obj):
62
+ fullname.__dict__[_CACHE_KEY][obj] = name = _module_name(obj)
63
+ return name
64
+
65
+ qualname = obj.__qualname__
66
+ orig_obj = obj
67
+
68
+ if _is_routine_method(obj) or _is_routine_descriptor(obj):
69
+ obj_cls = obj.__objclass__ if _is_routine_descriptor(obj) else obj.__self__
70
+ obj = type(obj_cls) if not isinstance(obj_cls, type) else obj_cls
71
+
72
+ module: str = getattr(orig_obj, "__module__", None) or obj.__module__
73
+ if _is_builtin(obj) and module != builtins.__name__:
74
+ module = builtins.__name__
75
+ else:
76
+ module = _module_name(sys.modules[module])
77
+
78
+ fullname.__dict__[_CACHE_KEY][orig_obj] = name = ".".join((module, qualname))
79
+ return name
80
+
81
+
82
+ __all__ = ("fullname",)
@@ -0,0 +1,13 @@
1
+ from telegrinder.tools.global_context.abc import ABCGlobalContext, CtxVar, GlobalCtxVar
2
+ from telegrinder.tools.global_context.builtin_context import TelegrinderContext
3
+ from telegrinder.tools.global_context.global_context import GlobalContext, ctx_var, runtime_init
4
+
5
+ __all__ = (
6
+ "ABCGlobalContext",
7
+ "CtxVar",
8
+ "GlobalContext",
9
+ "GlobalCtxVar",
10
+ "TelegrinderContext",
11
+ "ctx_var",
12
+ "runtime_init",
13
+ )
@@ -0,0 +1,63 @@
1
+ import dataclasses
2
+ import typing
3
+ from abc import ABC, abstractmethod
4
+
5
+ type CtxVariable[T = typing.Any] = CtxVar[T] | GlobalCtxVar[T]
6
+
7
+ NOVALUE: typing.Final = object()
8
+
9
+
10
+ @dataclasses.dataclass(frozen=True)
11
+ class CtxVar[T = typing.Any]:
12
+ value: T
13
+ factory: typing.Any = dataclasses.field(default=NOVALUE, kw_only=True)
14
+ const: bool = dataclasses.field(default=False, kw_only=True)
15
+
16
+
17
+ @dataclasses.dataclass(repr=False, frozen=True)
18
+ class GlobalCtxVar[T = typing.Any](CtxVar[T]):
19
+ name: str
20
+ value: T
21
+ factory: typing.Any = dataclasses.field(default=NOVALUE, kw_only=True)
22
+ const: bool = dataclasses.field(default=False, kw_only=True)
23
+
24
+ def __repr__(self) -> str:
25
+ return "<{}({}={})>".format(
26
+ type(self).__name__,
27
+ self.name,
28
+ repr(CtxVar(self.value, const=self.const, factory=self.factory)),
29
+ )
30
+
31
+ @classmethod
32
+ def from_var(
33
+ cls,
34
+ name: str,
35
+ ctx_value: T | CtxVariable[T],
36
+ const: bool = False,
37
+ ) -> typing.Self:
38
+ var = CtxVar(ctx_value, const=const) if not isinstance(ctx_value, CtxVar | GlobalCtxVar) else ctx_value
39
+ if var.value is NOVALUE and var.factory is not NOVALUE:
40
+ var = dataclasses.replace(var, value=var.factory())
41
+ return cls(**dict(var.__dict__) | dict(name=name))
42
+
43
+
44
+ class ABCGlobalContext[T = typing.Any](ABC):
45
+ @abstractmethod
46
+ def __getattr__(self, __name: str) -> typing.Any:
47
+ pass
48
+
49
+ @abstractmethod
50
+ def __setattr__(self, __name: str, __value: T | CtxVariable[T]) -> None:
51
+ pass
52
+
53
+ @abstractmethod
54
+ def __delattr__(self, __name: str) -> None:
55
+ pass
56
+
57
+
58
+ __all__ = (
59
+ "ABCGlobalContext",
60
+ "CtxVar",
61
+ "CtxVariable",
62
+ "GlobalCtxVar",
63
+ )
@@ -0,0 +1,45 @@
1
+ from __future__ import annotations
2
+
3
+ import re
4
+ import typing
5
+
6
+ from nodnod.scope import Scope
7
+ from vbml.patcher.abc import ABCPatcher
8
+ from vbml.patcher.patcher import Patcher
9
+
10
+ from telegrinder.modules import logger
11
+ from telegrinder.tools.global_context.global_context import GlobalContext, ctx_var, runtime_init
12
+ from telegrinder.tools.loop_wrapper import LoopWrapper
13
+
14
+
15
+ @runtime_init
16
+ class TelegrinderContext(GlobalContext, thread_safe=True):
17
+ """The thread-safe type-hinted telegrinder context.
18
+
19
+ Example:
20
+ ```
21
+ from telegrinder.tools.global_context import GlobalContext, TelegrinderContext
22
+
23
+ telegrinder_ctx = TelegrinderContext(...) # with type-hints
24
+ assert telegrinder_ctx == GlobalContext("telegrinder") # ok
25
+ ```
26
+
27
+ """
28
+
29
+ __ctx_name__ = "telegrinder"
30
+
31
+ node_global_scope: typing.Final[Scope] = ctx_var(
32
+ default_factory=lambda: Scope(detail="global"),
33
+ init=False,
34
+ const=True,
35
+ )
36
+ vbml_pattern_flags: re.RegexFlag | None = ctx_var(default=None, init=False)
37
+ vbml_patcher: ABCPatcher = ctx_var(default_factory=Patcher, init=False)
38
+ loop_wrapper: typing.Final[LoopWrapper] = ctx_var(default_factory=LoopWrapper, init=False, const=True)
39
+
40
+ async def close_global_scope(self) -> None:
41
+ await self.node_global_scope.close()
42
+ logger.debug("Node global scope closed")
43
+
44
+
45
+ __all__ = ("TelegrinderContext",)