telegrinder 0.1.dev20__py3-none-any.whl → 0.1.dev159__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 (132) hide show
  1. telegrinder/__init__.py +129 -22
  2. telegrinder/api/__init__.py +11 -2
  3. telegrinder/api/abc.py +25 -9
  4. telegrinder/api/api.py +47 -28
  5. telegrinder/api/error.py +14 -4
  6. telegrinder/api/response.py +11 -7
  7. telegrinder/bot/__init__.py +68 -7
  8. telegrinder/bot/bot.py +30 -24
  9. telegrinder/bot/cute_types/__init__.py +11 -1
  10. telegrinder/bot/cute_types/base.py +138 -0
  11. telegrinder/bot/cute_types/callback_query.py +458 -15
  12. telegrinder/bot/cute_types/inline_query.py +30 -24
  13. telegrinder/bot/cute_types/message.py +2982 -78
  14. telegrinder/bot/cute_types/update.py +30 -0
  15. telegrinder/bot/cute_types/utils.py +794 -0
  16. telegrinder/bot/dispatch/__init__.py +56 -3
  17. telegrinder/bot/dispatch/abc.py +9 -7
  18. telegrinder/bot/dispatch/composition.py +74 -0
  19. telegrinder/bot/dispatch/context.py +71 -0
  20. telegrinder/bot/dispatch/dispatch.py +86 -49
  21. telegrinder/bot/dispatch/handler/__init__.py +3 -0
  22. telegrinder/bot/dispatch/handler/abc.py +11 -5
  23. telegrinder/bot/dispatch/handler/func.py +41 -32
  24. telegrinder/bot/dispatch/handler/message_reply.py +46 -0
  25. telegrinder/bot/dispatch/middleware/__init__.py +2 -0
  26. telegrinder/bot/dispatch/middleware/abc.py +10 -4
  27. telegrinder/bot/dispatch/process.py +53 -49
  28. telegrinder/bot/dispatch/return_manager/__init__.py +19 -0
  29. telegrinder/bot/dispatch/return_manager/abc.py +95 -0
  30. telegrinder/bot/dispatch/return_manager/callback_query.py +19 -0
  31. telegrinder/bot/dispatch/return_manager/inline_query.py +14 -0
  32. telegrinder/bot/dispatch/return_manager/message.py +25 -0
  33. telegrinder/bot/dispatch/view/__init__.py +14 -2
  34. telegrinder/bot/dispatch/view/abc.py +128 -2
  35. telegrinder/bot/dispatch/view/box.py +38 -0
  36. telegrinder/bot/dispatch/view/callback_query.py +13 -39
  37. telegrinder/bot/dispatch/view/inline_query.py +11 -39
  38. telegrinder/bot/dispatch/view/message.py +11 -47
  39. telegrinder/bot/dispatch/waiter_machine/__init__.py +9 -0
  40. telegrinder/bot/dispatch/waiter_machine/machine.py +116 -0
  41. telegrinder/bot/dispatch/waiter_machine/middleware.py +76 -0
  42. telegrinder/bot/dispatch/waiter_machine/short_state.py +37 -0
  43. telegrinder/bot/polling/__init__.py +2 -0
  44. telegrinder/bot/polling/abc.py +11 -4
  45. telegrinder/bot/polling/polling.py +89 -40
  46. telegrinder/bot/rules/__init__.py +91 -5
  47. telegrinder/bot/rules/abc.py +81 -63
  48. telegrinder/bot/rules/adapter/__init__.py +11 -0
  49. telegrinder/bot/rules/adapter/abc.py +21 -0
  50. telegrinder/bot/rules/adapter/errors.py +5 -0
  51. telegrinder/bot/rules/adapter/event.py +49 -0
  52. telegrinder/bot/rules/adapter/raw_update.py +24 -0
  53. telegrinder/bot/rules/callback_data.py +159 -38
  54. telegrinder/bot/rules/command.py +116 -0
  55. telegrinder/bot/rules/enum_text.py +28 -0
  56. telegrinder/bot/rules/func.py +17 -17
  57. telegrinder/bot/rules/fuzzy.py +13 -10
  58. telegrinder/bot/rules/inline.py +61 -0
  59. telegrinder/bot/rules/integer.py +12 -7
  60. telegrinder/bot/rules/is_from.py +148 -7
  61. telegrinder/bot/rules/markup.py +21 -18
  62. telegrinder/bot/rules/mention.py +17 -0
  63. telegrinder/bot/rules/message_entities.py +33 -0
  64. telegrinder/bot/rules/regex.py +27 -19
  65. telegrinder/bot/rules/rule_enum.py +74 -0
  66. telegrinder/bot/rules/start.py +25 -13
  67. telegrinder/bot/rules/text.py +23 -14
  68. telegrinder/bot/scenario/__init__.py +2 -0
  69. telegrinder/bot/scenario/abc.py +12 -5
  70. telegrinder/bot/scenario/checkbox.py +48 -30
  71. telegrinder/bot/scenario/choice.py +16 -10
  72. telegrinder/client/__init__.py +3 -1
  73. telegrinder/client/abc.py +26 -16
  74. telegrinder/client/aiohttp.py +54 -32
  75. telegrinder/model.py +119 -40
  76. telegrinder/modules.py +189 -21
  77. telegrinder/msgspec_json.py +14 -0
  78. telegrinder/msgspec_utils.py +227 -0
  79. telegrinder/node/__init__.py +31 -0
  80. telegrinder/node/attachment.py +71 -0
  81. telegrinder/node/base.py +93 -0
  82. telegrinder/node/composer.py +71 -0
  83. telegrinder/node/container.py +22 -0
  84. telegrinder/node/message.py +18 -0
  85. telegrinder/node/rule.py +56 -0
  86. telegrinder/node/source.py +31 -0
  87. telegrinder/node/text.py +13 -0
  88. telegrinder/node/tools/__init__.py +3 -0
  89. telegrinder/node/tools/generator.py +40 -0
  90. telegrinder/node/update.py +12 -0
  91. telegrinder/rules.py +1 -1
  92. telegrinder/tools/__init__.py +138 -4
  93. telegrinder/tools/buttons.py +89 -51
  94. telegrinder/tools/error_handler/__init__.py +8 -0
  95. telegrinder/tools/error_handler/abc.py +30 -0
  96. telegrinder/tools/error_handler/error_handler.py +156 -0
  97. telegrinder/tools/formatting/__init__.py +81 -3
  98. telegrinder/tools/formatting/html.py +283 -37
  99. telegrinder/tools/formatting/links.py +32 -0
  100. telegrinder/tools/formatting/spec_html_formats.py +121 -0
  101. telegrinder/tools/global_context/__init__.py +12 -0
  102. telegrinder/tools/global_context/abc.py +66 -0
  103. telegrinder/tools/global_context/global_context.py +451 -0
  104. telegrinder/tools/global_context/telegrinder_ctx.py +25 -0
  105. telegrinder/tools/i18n/__init__.py +12 -0
  106. telegrinder/tools/i18n/base.py +31 -0
  107. telegrinder/tools/i18n/middleware/__init__.py +3 -0
  108. telegrinder/tools/i18n/middleware/base.py +26 -0
  109. telegrinder/tools/i18n/simple.py +48 -0
  110. telegrinder/tools/kb_set/__init__.py +2 -0
  111. telegrinder/tools/kb_set/base.py +3 -0
  112. telegrinder/tools/kb_set/yaml.py +28 -17
  113. telegrinder/tools/keyboard.py +84 -62
  114. telegrinder/tools/loop_wrapper/__init__.py +4 -0
  115. telegrinder/tools/loop_wrapper/abc.py +18 -0
  116. telegrinder/tools/loop_wrapper/loop_wrapper.py +132 -0
  117. telegrinder/tools/magic.py +48 -23
  118. telegrinder/tools/parse_mode.py +1 -2
  119. telegrinder/types/__init__.py +1 -0
  120. telegrinder/types/enums.py +653 -0
  121. telegrinder/types/methods.py +4107 -1279
  122. telegrinder/types/objects.py +4771 -1745
  123. {telegrinder-0.1.dev20.dist-info → telegrinder-0.1.dev159.dist-info}/LICENSE +2 -1
  124. telegrinder-0.1.dev159.dist-info/METADATA +109 -0
  125. telegrinder-0.1.dev159.dist-info/RECORD +126 -0
  126. {telegrinder-0.1.dev20.dist-info → telegrinder-0.1.dev159.dist-info}/WHEEL +1 -1
  127. telegrinder/bot/dispatch/waiter.py +0 -38
  128. telegrinder/result.py +0 -38
  129. telegrinder/tools/formatting/abc.py +0 -52
  130. telegrinder/tools/formatting/markdown.py +0 -57
  131. telegrinder-0.1.dev20.dist-info/METADATA +0 -22
  132. telegrinder-0.1.dev20.dist-info/RECORD +0 -71
@@ -0,0 +1,121 @@
1
+ import dataclasses
2
+ import typing
3
+
4
+ from telegrinder.types.enums import ProgrammingLanguage
5
+
6
+ SpecialFormat = typing.Union[
7
+ "ChannelBoostLink",
8
+ "Mention",
9
+ "Link",
10
+ "PreCode",
11
+ "TgEmoji",
12
+ "StartBotLink",
13
+ "StartGroupLink",
14
+ "ResolveDomain",
15
+ "InviteChatLink",
16
+ ]
17
+
18
+
19
+ def is_spec_format(obj: typing.Any) -> typing.TypeGuard[SpecialFormat]:
20
+ return (
21
+ dataclasses.is_dataclass(obj)
22
+ and hasattr(obj, "__formatter_name__")
23
+ and isinstance(obj, BaseSpecFormat)
24
+ )
25
+
26
+
27
+ @dataclasses.dataclass(repr=False)
28
+ class BaseSpecFormat:
29
+ __formatter_name__: typing.ClassVar[str]
30
+
31
+ def __repr__(self) -> str:
32
+ return f"<{self.__class__.__name__!r}: {self.__formatter_name__!r}>"
33
+
34
+
35
+ @dataclasses.dataclass
36
+ class ChannelBoostLink(BaseSpecFormat):
37
+ __formatter_name__ = "channel_boost_link"
38
+
39
+ channel_username: str
40
+ string: str | None = None
41
+
42
+
43
+ @dataclasses.dataclass
44
+ class InviteChatLink(BaseSpecFormat):
45
+ __formatter_name__ = "invite_chat_link"
46
+
47
+ invite_link: str
48
+ string: str | None = None
49
+
50
+
51
+ @dataclasses.dataclass
52
+ class Mention(BaseSpecFormat):
53
+ __formatter_name__ = "mention"
54
+
55
+ string: str
56
+ user_id: int
57
+
58
+
59
+ @dataclasses.dataclass
60
+ class Link(BaseSpecFormat):
61
+ __formatter_name__ = "link"
62
+
63
+ href: str
64
+ string: str | None = None
65
+
66
+
67
+ @dataclasses.dataclass
68
+ class PreCode(BaseSpecFormat):
69
+ __formatter_name__ = "pre_code"
70
+
71
+ string: str
72
+ lang: str | ProgrammingLanguage | None = None
73
+
74
+
75
+ @dataclasses.dataclass
76
+ class TgEmoji(BaseSpecFormat):
77
+ __formatter_name__ = "tg_emoji"
78
+
79
+ string: str
80
+ emoji_id: int
81
+
82
+
83
+ @dataclasses.dataclass
84
+ class StartBotLink(BaseSpecFormat):
85
+ __formatter_name__ = "start_bot_link"
86
+
87
+ bot_username: str
88
+ data: str
89
+ string: str | None
90
+
91
+
92
+ @dataclasses.dataclass
93
+ class StartGroupLink(BaseSpecFormat):
94
+ __formatter_name__ = "start_group_link"
95
+
96
+ bot_username: str
97
+ data: str
98
+ string: str | None = None
99
+
100
+
101
+ @dataclasses.dataclass
102
+ class ResolveDomain(BaseSpecFormat):
103
+ __formatter_name__ = "resolve_domain"
104
+
105
+ username: str
106
+ string: str | None = None
107
+
108
+
109
+ __all__ = (
110
+ "BaseSpecFormat",
111
+ "ChannelBoostLink",
112
+ "InviteChatLink",
113
+ "Link",
114
+ "Mention",
115
+ "PreCode",
116
+ "ResolveDomain",
117
+ "SpecialFormat",
118
+ "StartBotLink",
119
+ "StartGroupLink",
120
+ "TgEmoji",
121
+ )
@@ -0,0 +1,12 @@
1
+ from .abc import ABCGlobalContext, CtxVar, GlobalCtxVar
2
+ from .global_context import GlobalContext, ctx_var
3
+ from .telegrinder_ctx import TelegrinderCtx
4
+
5
+ __all__ = (
6
+ "ABCGlobalContext",
7
+ "CtxVar",
8
+ "GlobalContext",
9
+ "GlobalCtxVar",
10
+ "TelegrinderCtx",
11
+ "ctx_var",
12
+ )
@@ -0,0 +1,66 @@
1
+ from __future__ import annotations
2
+
3
+ import dataclasses
4
+ from abc import ABC, abstractmethod
5
+
6
+ import typing_extensions as typing
7
+
8
+ T = typing.TypeVar("T", default=typing.Any)
9
+
10
+
11
+ @dataclasses.dataclass(repr=False, frozen=True)
12
+ class CtxVar(typing.Generic[T]):
13
+ value: T
14
+ const: bool = dataclasses.field(default=False, kw_only=True)
15
+
16
+ def __repr__(self) -> str:
17
+ return "<{}(value={!r})>".format(
18
+ ("Const" if self.const else "") + CtxVar.__name__,
19
+ self.value,
20
+ )
21
+
22
+
23
+ @dataclasses.dataclass(repr=False, frozen=True)
24
+ class GlobalCtxVar(typing.Generic[T]):
25
+ name: str
26
+ value: T
27
+ const: bool = dataclasses.field(default=False, kw_only=True)
28
+
29
+ def __repr__(self) -> str:
30
+ return "<{}({}={})>".format(
31
+ self.__class__.__name__,
32
+ self.name,
33
+ repr(CtxVar(self.value, const=self.const)),
34
+ )
35
+
36
+ @classmethod
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
39
+ params = ctx_value.__dict__
40
+ params["name"] = name
41
+ return cls(**params)
42
+
43
+
44
+ class ABCGlobalContext(ABC, typing.Generic[T]):
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
+ CtxVariable = CtxVar[T] | GlobalCtxVar[T]
59
+
60
+
61
+ __all__ = (
62
+ "ABCGlobalContext",
63
+ "CtxVar",
64
+ "CtxVariable",
65
+ "GlobalCtxVar",
66
+ )
@@ -0,0 +1,451 @@
1
+ import dataclasses
2
+ from copy import deepcopy
3
+ from functools import wraps
4
+
5
+ import typing_extensions as typing
6
+ from fntypes.co import Error, Nothing, Ok, Option, Result, Some
7
+
8
+ from telegrinder.modules import logger
9
+ from telegrinder.msgspec_utils import msgspec_convert
10
+
11
+ from .abc import ABCGlobalContext, CtxVar, CtxVariable, GlobalCtxVar
12
+
13
+ T = typing.TypeVar("T")
14
+ F = typing.TypeVar("F", bound=typing.Callable)
15
+ CtxValueT = typing.TypeVar("CtxValueT", default=typing.Any)
16
+
17
+ if typing.TYPE_CHECKING:
18
+
19
+ _: typing.TypeAlias = None
20
+ else:
21
+
22
+ _ = lambda: None
23
+
24
+
25
+ def type_check(value: object, value_type: type[T]) -> typing.TypeGuard[T]:
26
+ return True if value_type is object else bool(msgspec_convert(value, value_type))
27
+
28
+
29
+ def is_dunder(name: str) -> bool:
30
+ return name.startswith("__") and name.endswith("__")
31
+
32
+
33
+ def get_orig_class(obj: T) -> type[T]:
34
+ return getattr(obj, "__orig_class__", obj.__class__)
35
+
36
+
37
+ def root_protection(func: F) -> F:
38
+ if func.__name__ not in ("__setattr__", "__getattr__", "__delattr__"):
39
+ raise RuntimeError(
40
+ "You cannot decorate a {!r} function with this decorator, only "
41
+ "'__setattr__', __getattr__', '__delattr__' methods.".format(
42
+ func.__name__,
43
+ )
44
+ )
45
+
46
+ @wraps(func)
47
+ def wrapper(self: "GlobalContext", __name: str, *args) -> typing.Any:
48
+ if self.is_root_attribute(__name) and __name in (
49
+ self.__dict__ | self.__class__.__dict__
50
+ ):
51
+ root_attr = self.get_root_attribute(__name).unwrap()
52
+ if all((not root_attr.can_be_rewritten, not root_attr.can_be_read)):
53
+ raise AttributeError(
54
+ f"Unable to set, get, delete root attribute {__name!r}."
55
+ )
56
+ if func.__name__ == "__setattr__" and not root_attr.can_be_rewritten:
57
+ raise AttributeError(f"Unable to set root attribute {__name!r}.")
58
+ if func.__name__ == "__getattr__" and not root_attr.can_be_read:
59
+ raise AttributeError(f"Unable to get root attribute {__name!r}.")
60
+ if func.__name__ == "__delattr__":
61
+ raise AttributeError(f"Unable to delete root attribute {__name!r}.")
62
+
63
+ return func(self, __name, *args) # type: ignore
64
+
65
+ return wrapper # type: ignore
66
+
67
+
68
+ def ctx_var(value: T, *, const: bool = False) -> T:
69
+ """Example:
70
+ ```
71
+ class MyCtx(GlobalContext):
72
+ name: typing.Final[str]
73
+ URL: typing.Final = ctx_var("https://google.com", const=True)
74
+
75
+ ctx = MyCtx(name=ctx_var("Alex", const=True))
76
+ ctx.URL #: 'https://google.com'
77
+ ctx.URL = '...' #: type checking error & exception 'TypeError'
78
+ ```
79
+ """
80
+
81
+ return typing.cast(T, CtxVar(value, const=const))
82
+
83
+
84
+ @dataclasses.dataclass(frozen=True, eq=False)
85
+ class RootAttr:
86
+ name: str
87
+ _: dataclasses.KW_ONLY
88
+ can_be_read: bool = dataclasses.field(default=True, kw_only=True)
89
+ can_be_rewritten: bool = dataclasses.field(default=False, kw_only=True)
90
+
91
+ def __eq__(self, __value: str) -> bool:
92
+ return self.name == __value
93
+
94
+
95
+ @dataclasses.dataclass(repr=False, frozen=True)
96
+ class Storage:
97
+ _storage: dict[str, "GlobalContext"] = dataclasses.field(
98
+ default_factory=lambda: {},
99
+ init=False,
100
+ )
101
+
102
+ def __repr__(self) -> str:
103
+ return "<ContextStorage: %s>" % ", ".join(
104
+ "ctx @" + repr(x) for x in self._storage
105
+ )
106
+
107
+ @property
108
+ def storage(self) -> dict[str, "GlobalContext"]:
109
+ return self._storage.copy()
110
+
111
+ def set(self, name: str, ctx: "GlobalContext") -> None:
112
+ self._storage.setdefault(name, ctx)
113
+
114
+ def get(self, ctx_name: str) -> Option["GlobalContext"]:
115
+ ctx = self._storage.get(ctx_name)
116
+ return Some(ctx) if ctx is not None else Nothing()
117
+
118
+ def delete(self, ctx_name: str) -> None:
119
+ assert self._storage.pop(ctx_name, None) is not None, f"Context {ctx_name!r} is not defined in storage."
120
+
121
+
122
+ @typing.dataclass_transform(
123
+ kw_only_default=True,
124
+ order_default=True,
125
+ field_specifiers=(ctx_var,),
126
+ )
127
+ class GlobalContext(ABCGlobalContext, typing.Generic[CtxValueT], dict[str, GlobalCtxVar[CtxValueT]]):
128
+ """GlobalContext.
129
+
130
+ ```
131
+ ctx = GlobalContext()
132
+ ctx["client"] = Client()
133
+ ctx.address = CtxVar("128.0.0.7:8888", const=True)
134
+
135
+ def request():
136
+ data = {"user": "root_user", "password": "secret_password"}
137
+ ctx.client.request(ctx.address + "/login", data)
138
+ """
139
+
140
+ __ctx_name__: str | None
141
+ __storage__: typing.ClassVar[Storage] = Storage()
142
+ __root_attributes__: typing.ClassVar[tuple[RootAttr, ...]] = (
143
+ RootAttr("__ctx_name__"),
144
+ RootAttr("__root_attributes__"),
145
+ RootAttr("__storage__"),
146
+ )
147
+
148
+ def __new__(
149
+ cls,
150
+ ctx_name: str | None = None,
151
+ /,
152
+ **variables: typing.Any | CtxVar[CtxValueT],
153
+ ) -> typing.Self:
154
+ """Create or get from storage a new `GlobalContext` object."""
155
+
156
+ if not issubclass(GlobalContext, cls):
157
+ defaults = {}
158
+ for name in cls.__annotations__:
159
+ if (
160
+ name in cls.__dict__
161
+ and name not in cls.__root_attributes__
162
+ ):
163
+ defaults[name] = getattr(cls, name)
164
+ delattr(cls, name)
165
+ if isinstance(defaults[name], CtxVar) and defaults[name].const:
166
+ variables.pop(name, None)
167
+
168
+ variables = defaults | variables
169
+
170
+ ctx_name = getattr(cls, "__ctx_name__", ctx_name)
171
+ if ctx_name is None:
172
+ ctx = dict.__new__(cls)
173
+ elif ctx_name in cls.__storage__.storage:
174
+ ctx = cls.__storage__.get(ctx_name).unwrap()
175
+ else:
176
+ ctx = dict.__new__(cls, ctx_name)
177
+ cls.__storage__.set(ctx_name, ctx)
178
+
179
+ ctx.set_context_variables(variables)
180
+ return ctx # type: ignore
181
+
182
+ def __init__(
183
+ self,
184
+ ctx_name: str | None = None,
185
+ /,
186
+ **variables: CtxValueT | CtxVariable[CtxValueT],
187
+ ):
188
+ """Initialization of `GlobalContext` with passed variables."""
189
+
190
+ if not hasattr(self, "__ctx_name__"):
191
+ self.__ctx_name__ = ctx_name
192
+
193
+ if variables and not self:
194
+ self.set_context_variables(variables)
195
+
196
+ def __repr__(self) -> str:
197
+ return "<{!r} -> ({})>".format(
198
+ f"{self.__class__.__name__}@{self.ctx_name}",
199
+ ", ".join(repr(var) for var in self),
200
+ )
201
+
202
+ def __eq__(self, __value: "GlobalContext") -> bool:
203
+ """Returns True if the names of context stores
204
+ that use self and __value instances are equivalent."""
205
+
206
+ return self.__ctx_name__ == __value.__ctx_name__
207
+
208
+ def __setitem__(self, __name: str, __value: CtxValueT | CtxVariable[CtxValueT]):
209
+ if is_dunder(__name):
210
+ raise NameError("Cannot set a context variable with dunder name.")
211
+ var = self.get(__name)
212
+ if var and var.unwrap().const:
213
+ raise TypeError(
214
+ f"Unable to set variable {__name!r}, because it's a constant."
215
+ )
216
+ dict.__setitem__(self, __name, GlobalCtxVar.collect(__name, __value))
217
+
218
+ def __getitem__(self, __name: str) -> CtxValueT:
219
+ return self.get(__name).unwrap().value
220
+
221
+ def __delitem__(self, __name: str):
222
+ var = self.get(__name).unwrap()
223
+ if var.const:
224
+ raise TypeError(
225
+ f"Unable to delete variable {__name!r}, because it's a constant."
226
+ )
227
+ dict.__delitem__(self, __name)
228
+
229
+ @root_protection
230
+ def __setattr__(self, __name: str, __value: CtxValueT | CtxVariable[CtxValueT]):
231
+ """Setting a context variable."""
232
+
233
+ if is_dunder(__name):
234
+ return object.__setattr__(self, __name, __value)
235
+ self.__setitem__(__name, __value)
236
+
237
+ @root_protection
238
+ def __getattr__(self, __name: str) -> CtxValueT:
239
+ """Getting a context variable."""
240
+
241
+ if is_dunder(__name):
242
+ return object.__getattribute__(self, __name)
243
+ return self.__getitem__(__name)
244
+
245
+ @root_protection
246
+ def __delattr__(self, __name: str) -> None:
247
+ """Removing a context variable."""
248
+
249
+ if is_dunder(__name):
250
+ return object.__delattr__(self, __name)
251
+ self.__delitem__(__name)
252
+
253
+ @property
254
+ def ctx_name(self) -> str:
255
+ """Context name."""
256
+
257
+ return self.__ctx_name__ or "<Unnamed ctx at %#x>" % id(self)
258
+
259
+ @classmethod
260
+ def is_root_attribute(cls, name: str) -> bool:
261
+ """Returns True if exists root attribute
262
+ otherwise False."""
263
+
264
+ return name in cls.__root_attributes__
265
+
266
+ def set_context_variables(self, variables: typing.Mapping[str, CtxValueT | CtxVariable[CtxValueT]]) -> None:
267
+ """Set context variables from mapping."""
268
+
269
+ for name, var in variables.items():
270
+ self[name] = var
271
+
272
+ def get_root_attribute(self, name: str) -> Option[RootAttr]:
273
+ """Get root attribute by name."""
274
+
275
+ if self.is_root_attribute(name):
276
+ for rattr in self.__root_attributes__:
277
+ if rattr.name == name:
278
+ return Some(rattr)
279
+ return Nothing()
280
+
281
+ def items(self) -> list[tuple[str, GlobalCtxVar[CtxValueT]]]:
282
+ """Return context variables as set-like items."""
283
+
284
+ return list(dict.items(self))
285
+
286
+ def keys(self) -> list[str]:
287
+ """Returns context variable names as keys."""
288
+
289
+ return list(dict.keys(self))
290
+
291
+ def values(self) -> list[GlobalCtxVar[CtxValueT]]:
292
+ """Returns context variables as values."""
293
+
294
+ return list(dict.values(self))
295
+
296
+ def update(self, other: typing.Self) -> None:
297
+ """Update context."""
298
+
299
+ dict.update(dict(other.items()))
300
+
301
+ def copy(self) -> typing.Self:
302
+ """Copy context. Returns copied context without ctx_name."""
303
+
304
+ return self.__class__(**self.dict())
305
+
306
+ def dict(self) -> dict[str, GlobalCtxVar[CtxValueT]]:
307
+ """Returns context as dict."""
308
+
309
+ return {name: deepcopy(var) for name, var in self.items()}
310
+
311
+ @typing.overload
312
+ def pop(self, var_name: str) -> Option[GlobalCtxVar[CtxValueT]]:
313
+ ...
314
+
315
+ @typing.overload
316
+ def pop(
317
+ self,
318
+ var_name: str,
319
+ var_value_type: type[T],
320
+ ) -> Option[GlobalCtxVar[T]]:
321
+ ...
322
+
323
+ def pop(
324
+ self,
325
+ var_name: str,
326
+ var_value_type: type[T] = object
327
+ ) -> Option[GlobalCtxVar[T]]:
328
+ """Pop context variable by name.
329
+ Returns Option[GlobalCtxVar[T]] object.
330
+ """
331
+
332
+ val = self.get(var_name, var_value_type)
333
+ if val:
334
+ del self[var_name]
335
+ return val
336
+ return Nothing()
337
+
338
+ @typing.overload
339
+ def get(self, var_name: str) -> Option[GlobalCtxVar[CtxValueT]]:
340
+ ...
341
+
342
+ @typing.overload
343
+ def get(
344
+ self,
345
+ var_name: str,
346
+ var_value_type: type[T],
347
+ ) -> Option[GlobalCtxVar[T]]:
348
+ ...
349
+
350
+ def get(
351
+ self,
352
+ var_name: str,
353
+ var_value_type: type[T] = object,
354
+ ) -> Option[GlobalCtxVar[T]]:
355
+ """Get context variable by name.
356
+ Returns `GlobalCtxVar[value_type]` object."""
357
+
358
+ generic_types = typing.get_args(get_orig_class(self))
359
+ if generic_types and var_value_type is object:
360
+ var_value_type = generic_types[0]
361
+ var = dict.get(self, var_name)
362
+ if var is None:
363
+ return Nothing()
364
+ assert type_check(var.value, var_value_type), (
365
+ "Context variable value type of {!r} does not correspond to the expected type {!r}.".format(
366
+ type(var.value).__name__,
367
+ getattr(var_value_type, "__name__")
368
+ if isinstance(var_value_type, type)
369
+ else repr(var_value_type),
370
+ )
371
+ )
372
+ return Some(var)
373
+
374
+ @typing.overload
375
+ def get_value(self, var_name: str) -> Option[CtxValueT]:
376
+ ...
377
+
378
+ @typing.overload
379
+ def get_value(
380
+ self,
381
+ var_name: str,
382
+ var_value_type: type[T],
383
+ ) -> Option[T]:
384
+ ...
385
+
386
+ def get_value(
387
+ self,
388
+ var_name: str,
389
+ var_value_type: type[T] = object,
390
+ ) -> Option[T]:
391
+ """Get context variable value by name."""
392
+
393
+ return self.get(var_name, var_value_type).map(lambda var: var.value)
394
+
395
+ def rename(self, old_var_name: str, new_var_name: str) -> Result[_, str]:
396
+ """Rename context variable."""
397
+
398
+ var = self.get(old_var_name).unwrap()
399
+ if var.const:
400
+ return Error(
401
+ f"Unable to rename variable {old_var_name!r}, "
402
+ "because it's a constant."
403
+ )
404
+ del self[old_var_name]
405
+ self[new_var_name] = var.value
406
+ return Ok(_())
407
+
408
+ def clear(self, *, include_consts: bool = False) -> None:
409
+ """Clear context. If `include_consts = True`,
410
+ then the context is completely cleared."""
411
+
412
+ if not self:
413
+ return
414
+ if include_consts:
415
+ logger.warning(
416
+ "Constants from the global context {!r} have been cleaned up!",
417
+ self.ctx_name + " at %#x" % id(self),
418
+ )
419
+ return dict.clear(self)
420
+
421
+ for name, var in self.dict().items():
422
+ if var.const:
423
+ continue
424
+ del self[name]
425
+
426
+ def delete_ctx(self) -> Result[_, str]:
427
+ """Delete context by `ctx_name`."""
428
+
429
+ if not self.__ctx_name__:
430
+ return Error("Cannot delete unnamed context.")
431
+ ctx = self.__storage__.get(self.ctx_name).unwrap()
432
+ dict.clear(ctx)
433
+ self.__storage__.delete(self.ctx_name)
434
+ logger.warning(f"Global context {self.ctx_name!r} has been deleted!")
435
+ return Ok(_())
436
+
437
+
438
+ __all__ = (
439
+ "ABCGlobalContext",
440
+ "CtxVar",
441
+ "CtxVariable",
442
+ "GlobalContext",
443
+ "GlobalCtxVar",
444
+ "RootAttr",
445
+ "Storage",
446
+ "ctx_var",
447
+ "get_orig_class",
448
+ "is_dunder",
449
+ "root_protection",
450
+ "type_check",
451
+ )
@@ -0,0 +1,25 @@
1
+ import typing
2
+
3
+ import vbml
4
+
5
+ from telegrinder.tools.global_context import GlobalContext, ctx_var
6
+
7
+
8
+ class TelegrinderCtx(GlobalContext):
9
+ """Basic type-hinted telegrinder context with context name `"telegrinder"`.
10
+
11
+ You can use this class or GlobalContext:
12
+ ```
13
+ from telegrinder.tools.global_context import GlobalContext, TelegrinderCtx
14
+
15
+ ctx1 = TelegrinderCtx()
16
+ ctx2 = GlobalContext("telegrinder") # same, but without the type-hints
17
+ assert ctx1 == ctx2 # ok
18
+ ```"""
19
+
20
+ __ctx_name__ = "telegrinder"
21
+
22
+ vbml_patcher: typing.ClassVar[vbml.Patcher] = ctx_var(vbml.Patcher(), const=True)
23
+
24
+
25
+ __all__ = ("TelegrinderCtx",)
@@ -0,0 +1,12 @@
1
+ from .base import ABCI18n, ABCTranslator, I18nEnum
2
+ from .middleware import ABCTranslatorMiddleware
3
+ from .simple import SimpleI18n, SimpleTranslator
4
+
5
+ __all__ = (
6
+ "ABCI18n",
7
+ "ABCTranslator",
8
+ "ABCTranslatorMiddleware",
9
+ "I18nEnum",
10
+ "SimpleI18n",
11
+ "SimpleTranslator",
12
+ )