telegrinder 0.2.0__tar.gz → 0.2.0.post2__tar.gz

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 (145) hide show
  1. {telegrinder-0.2.0 → telegrinder-0.2.0.post2}/PKG-INFO +1 -1
  2. {telegrinder-0.2.0 → telegrinder-0.2.0.post2}/pyproject.toml +1 -1
  3. {telegrinder-0.2.0 → telegrinder-0.2.0.post2}/telegrinder/bot/cute_types/base.py +13 -14
  4. {telegrinder-0.2.0 → telegrinder-0.2.0.post2}/telegrinder/bot/cute_types/update.py +6 -5
  5. {telegrinder-0.2.0 → telegrinder-0.2.0.post2}/telegrinder/bot/rules/callback_data.py +8 -2
  6. {telegrinder-0.2.0 → telegrinder-0.2.0.post2}/telegrinder/model.py +8 -6
  7. {telegrinder-0.2.0 → telegrinder-0.2.0.post2}/telegrinder/node/base.py +8 -1
  8. {telegrinder-0.2.0 → telegrinder-0.2.0.post2}/telegrinder/node/command.py +3 -3
  9. {telegrinder-0.2.0 → telegrinder-0.2.0.post2}/telegrinder/node/composer.py +1 -1
  10. {telegrinder-0.2.0 → telegrinder-0.2.0.post2}/telegrinder/node/event.py +12 -9
  11. {telegrinder-0.2.0 → telegrinder-0.2.0.post2}/telegrinder/node/me.py +3 -4
  12. {telegrinder-0.2.0 → telegrinder-0.2.0.post2}/telegrinder/node/polymorphic.py +3 -3
  13. {telegrinder-0.2.0 → telegrinder-0.2.0.post2}/telegrinder/tools/keyboard.py +6 -1
  14. {telegrinder-0.2.0 → telegrinder-0.2.0.post2}/LICENSE +0 -0
  15. {telegrinder-0.2.0 → telegrinder-0.2.0.post2}/readme.md +0 -0
  16. {telegrinder-0.2.0 → telegrinder-0.2.0.post2}/telegrinder/__init__.py +0 -0
  17. {telegrinder-0.2.0 → telegrinder-0.2.0.post2}/telegrinder/api/__init__.py +0 -0
  18. {telegrinder-0.2.0 → telegrinder-0.2.0.post2}/telegrinder/api/api.py +0 -0
  19. {telegrinder-0.2.0 → telegrinder-0.2.0.post2}/telegrinder/api/error.py +0 -0
  20. {telegrinder-0.2.0 → telegrinder-0.2.0.post2}/telegrinder/api/response.py +0 -0
  21. {telegrinder-0.2.0 → telegrinder-0.2.0.post2}/telegrinder/api/token.py +0 -0
  22. {telegrinder-0.2.0 → telegrinder-0.2.0.post2}/telegrinder/bot/__init__.py +0 -0
  23. {telegrinder-0.2.0 → telegrinder-0.2.0.post2}/telegrinder/bot/bot.py +0 -0
  24. {telegrinder-0.2.0 → telegrinder-0.2.0.post2}/telegrinder/bot/cute_types/__init__.py +0 -0
  25. {telegrinder-0.2.0 → telegrinder-0.2.0.post2}/telegrinder/bot/cute_types/callback_query.py +0 -0
  26. {telegrinder-0.2.0 → telegrinder-0.2.0.post2}/telegrinder/bot/cute_types/chat_join_request.py +0 -0
  27. {telegrinder-0.2.0 → telegrinder-0.2.0.post2}/telegrinder/bot/cute_types/chat_member_updated.py +0 -0
  28. {telegrinder-0.2.0 → telegrinder-0.2.0.post2}/telegrinder/bot/cute_types/inline_query.py +0 -0
  29. {telegrinder-0.2.0 → telegrinder-0.2.0.post2}/telegrinder/bot/cute_types/message.py +0 -0
  30. {telegrinder-0.2.0 → telegrinder-0.2.0.post2}/telegrinder/bot/cute_types/utils.py +0 -0
  31. {telegrinder-0.2.0 → telegrinder-0.2.0.post2}/telegrinder/bot/dispatch/__init__.py +0 -0
  32. {telegrinder-0.2.0 → telegrinder-0.2.0.post2}/telegrinder/bot/dispatch/abc.py +0 -0
  33. {telegrinder-0.2.0 → telegrinder-0.2.0.post2}/telegrinder/bot/dispatch/context.py +0 -0
  34. {telegrinder-0.2.0 → telegrinder-0.2.0.post2}/telegrinder/bot/dispatch/dispatch.py +0 -0
  35. {telegrinder-0.2.0 → telegrinder-0.2.0.post2}/telegrinder/bot/dispatch/handler/__init__.py +0 -0
  36. {telegrinder-0.2.0 → telegrinder-0.2.0.post2}/telegrinder/bot/dispatch/handler/abc.py +0 -0
  37. {telegrinder-0.2.0 → telegrinder-0.2.0.post2}/telegrinder/bot/dispatch/handler/func.py +0 -0
  38. {telegrinder-0.2.0 → telegrinder-0.2.0.post2}/telegrinder/bot/dispatch/handler/message_reply.py +0 -0
  39. {telegrinder-0.2.0 → telegrinder-0.2.0.post2}/telegrinder/bot/dispatch/middleware/__init__.py +0 -0
  40. {telegrinder-0.2.0 → telegrinder-0.2.0.post2}/telegrinder/bot/dispatch/middleware/abc.py +0 -0
  41. {telegrinder-0.2.0 → telegrinder-0.2.0.post2}/telegrinder/bot/dispatch/process.py +0 -0
  42. {telegrinder-0.2.0 → telegrinder-0.2.0.post2}/telegrinder/bot/dispatch/return_manager/__init__.py +0 -0
  43. {telegrinder-0.2.0 → telegrinder-0.2.0.post2}/telegrinder/bot/dispatch/return_manager/abc.py +0 -0
  44. {telegrinder-0.2.0 → telegrinder-0.2.0.post2}/telegrinder/bot/dispatch/return_manager/callback_query.py +0 -0
  45. {telegrinder-0.2.0 → telegrinder-0.2.0.post2}/telegrinder/bot/dispatch/return_manager/inline_query.py +0 -0
  46. {telegrinder-0.2.0 → telegrinder-0.2.0.post2}/telegrinder/bot/dispatch/return_manager/message.py +0 -0
  47. {telegrinder-0.2.0 → telegrinder-0.2.0.post2}/telegrinder/bot/dispatch/view/__init__.py +0 -0
  48. {telegrinder-0.2.0 → telegrinder-0.2.0.post2}/telegrinder/bot/dispatch/view/abc.py +0 -0
  49. {telegrinder-0.2.0 → telegrinder-0.2.0.post2}/telegrinder/bot/dispatch/view/box.py +0 -0
  50. {telegrinder-0.2.0 → telegrinder-0.2.0.post2}/telegrinder/bot/dispatch/view/callback_query.py +0 -0
  51. {telegrinder-0.2.0 → telegrinder-0.2.0.post2}/telegrinder/bot/dispatch/view/chat_join_request.py +0 -0
  52. {telegrinder-0.2.0 → telegrinder-0.2.0.post2}/telegrinder/bot/dispatch/view/chat_member.py +0 -0
  53. {telegrinder-0.2.0 → telegrinder-0.2.0.post2}/telegrinder/bot/dispatch/view/inline_query.py +0 -0
  54. {telegrinder-0.2.0 → telegrinder-0.2.0.post2}/telegrinder/bot/dispatch/view/message.py +0 -0
  55. {telegrinder-0.2.0 → telegrinder-0.2.0.post2}/telegrinder/bot/dispatch/view/raw.py +0 -0
  56. {telegrinder-0.2.0 → telegrinder-0.2.0.post2}/telegrinder/bot/dispatch/waiter_machine/__init__.py +0 -0
  57. {telegrinder-0.2.0 → telegrinder-0.2.0.post2}/telegrinder/bot/dispatch/waiter_machine/machine.py +0 -0
  58. {telegrinder-0.2.0 → telegrinder-0.2.0.post2}/telegrinder/bot/dispatch/waiter_machine/middleware.py +0 -0
  59. {telegrinder-0.2.0 → telegrinder-0.2.0.post2}/telegrinder/bot/dispatch/waiter_machine/short_state.py +0 -0
  60. {telegrinder-0.2.0 → telegrinder-0.2.0.post2}/telegrinder/bot/polling/__init__.py +0 -0
  61. {telegrinder-0.2.0 → telegrinder-0.2.0.post2}/telegrinder/bot/polling/abc.py +0 -0
  62. {telegrinder-0.2.0 → telegrinder-0.2.0.post2}/telegrinder/bot/polling/polling.py +0 -0
  63. {telegrinder-0.2.0 → telegrinder-0.2.0.post2}/telegrinder/bot/rules/__init__.py +0 -0
  64. {telegrinder-0.2.0 → telegrinder-0.2.0.post2}/telegrinder/bot/rules/abc.py +0 -0
  65. {telegrinder-0.2.0 → telegrinder-0.2.0.post2}/telegrinder/bot/rules/adapter/__init__.py +0 -0
  66. {telegrinder-0.2.0 → telegrinder-0.2.0.post2}/telegrinder/bot/rules/adapter/abc.py +0 -0
  67. {telegrinder-0.2.0 → telegrinder-0.2.0.post2}/telegrinder/bot/rules/adapter/errors.py +0 -0
  68. {telegrinder-0.2.0 → telegrinder-0.2.0.post2}/telegrinder/bot/rules/adapter/event.py +0 -0
  69. {telegrinder-0.2.0 → telegrinder-0.2.0.post2}/telegrinder/bot/rules/adapter/node.py +0 -0
  70. {telegrinder-0.2.0 → telegrinder-0.2.0.post2}/telegrinder/bot/rules/adapter/raw_update.py +0 -0
  71. {telegrinder-0.2.0 → telegrinder-0.2.0.post2}/telegrinder/bot/rules/chat_join.py +0 -0
  72. {telegrinder-0.2.0 → telegrinder-0.2.0.post2}/telegrinder/bot/rules/command.py +0 -0
  73. {telegrinder-0.2.0 → telegrinder-0.2.0.post2}/telegrinder/bot/rules/enum_text.py +0 -0
  74. {telegrinder-0.2.0 → telegrinder-0.2.0.post2}/telegrinder/bot/rules/func.py +0 -0
  75. {telegrinder-0.2.0 → telegrinder-0.2.0.post2}/telegrinder/bot/rules/fuzzy.py +0 -0
  76. {telegrinder-0.2.0 → telegrinder-0.2.0.post2}/telegrinder/bot/rules/inline.py +0 -0
  77. {telegrinder-0.2.0 → telegrinder-0.2.0.post2}/telegrinder/bot/rules/integer.py +0 -0
  78. {telegrinder-0.2.0 → telegrinder-0.2.0.post2}/telegrinder/bot/rules/is_from.py +0 -0
  79. {telegrinder-0.2.0 → telegrinder-0.2.0.post2}/telegrinder/bot/rules/markup.py +0 -0
  80. {telegrinder-0.2.0 → telegrinder-0.2.0.post2}/telegrinder/bot/rules/mention.py +0 -0
  81. {telegrinder-0.2.0 → telegrinder-0.2.0.post2}/telegrinder/bot/rules/message.py +0 -0
  82. {telegrinder-0.2.0 → telegrinder-0.2.0.post2}/telegrinder/bot/rules/message_entities.py +0 -0
  83. {telegrinder-0.2.0 → telegrinder-0.2.0.post2}/telegrinder/bot/rules/node.py +0 -0
  84. {telegrinder-0.2.0 → telegrinder-0.2.0.post2}/telegrinder/bot/rules/regex.py +0 -0
  85. {telegrinder-0.2.0 → telegrinder-0.2.0.post2}/telegrinder/bot/rules/rule_enum.py +0 -0
  86. {telegrinder-0.2.0 → telegrinder-0.2.0.post2}/telegrinder/bot/rules/start.py +0 -0
  87. {telegrinder-0.2.0 → telegrinder-0.2.0.post2}/telegrinder/bot/rules/text.py +0 -0
  88. {telegrinder-0.2.0 → telegrinder-0.2.0.post2}/telegrinder/bot/rules/update.py +0 -0
  89. {telegrinder-0.2.0 → telegrinder-0.2.0.post2}/telegrinder/bot/scenario/__init__.py +0 -0
  90. {telegrinder-0.2.0 → telegrinder-0.2.0.post2}/telegrinder/bot/scenario/abc.py +0 -0
  91. {telegrinder-0.2.0 → telegrinder-0.2.0.post2}/telegrinder/bot/scenario/checkbox.py +0 -0
  92. {telegrinder-0.2.0 → telegrinder-0.2.0.post2}/telegrinder/bot/scenario/choice.py +0 -0
  93. {telegrinder-0.2.0 → telegrinder-0.2.0.post2}/telegrinder/client/__init__.py +0 -0
  94. {telegrinder-0.2.0 → telegrinder-0.2.0.post2}/telegrinder/client/abc.py +0 -0
  95. {telegrinder-0.2.0 → telegrinder-0.2.0.post2}/telegrinder/client/aiohttp.py +0 -0
  96. {telegrinder-0.2.0 → telegrinder-0.2.0.post2}/telegrinder/modules.py +0 -0
  97. {telegrinder-0.2.0 → telegrinder-0.2.0.post2}/telegrinder/msgspec_json.py +0 -0
  98. {telegrinder-0.2.0 → telegrinder-0.2.0.post2}/telegrinder/msgspec_utils.py +0 -0
  99. {telegrinder-0.2.0 → telegrinder-0.2.0.post2}/telegrinder/node/__init__.py +0 -0
  100. {telegrinder-0.2.0 → telegrinder-0.2.0.post2}/telegrinder/node/attachment.py +0 -0
  101. {telegrinder-0.2.0 → telegrinder-0.2.0.post2}/telegrinder/node/callback_query.py +0 -0
  102. {telegrinder-0.2.0 → telegrinder-0.2.0.post2}/telegrinder/node/container.py +0 -0
  103. {telegrinder-0.2.0 → telegrinder-0.2.0.post2}/telegrinder/node/message.py +0 -0
  104. {telegrinder-0.2.0 → telegrinder-0.2.0.post2}/telegrinder/node/rule.py +0 -0
  105. {telegrinder-0.2.0 → telegrinder-0.2.0.post2}/telegrinder/node/scope.py +0 -0
  106. {telegrinder-0.2.0 → telegrinder-0.2.0.post2}/telegrinder/node/source.py +0 -0
  107. {telegrinder-0.2.0 → telegrinder-0.2.0.post2}/telegrinder/node/text.py +0 -0
  108. {telegrinder-0.2.0 → telegrinder-0.2.0.post2}/telegrinder/node/tools/__init__.py +0 -0
  109. {telegrinder-0.2.0 → telegrinder-0.2.0.post2}/telegrinder/node/tools/generator.py +0 -0
  110. {telegrinder-0.2.0 → telegrinder-0.2.0.post2}/telegrinder/node/update.py +0 -0
  111. {telegrinder-0.2.0 → telegrinder-0.2.0.post2}/telegrinder/py.typed +0 -0
  112. {telegrinder-0.2.0 → telegrinder-0.2.0.post2}/telegrinder/rules.py +0 -0
  113. {telegrinder-0.2.0 → telegrinder-0.2.0.post2}/telegrinder/tools/__init__.py +0 -0
  114. {telegrinder-0.2.0 → telegrinder-0.2.0.post2}/telegrinder/tools/buttons.py +0 -0
  115. {telegrinder-0.2.0 → telegrinder-0.2.0.post2}/telegrinder/tools/error_handler/__init__.py +0 -0
  116. {telegrinder-0.2.0 → telegrinder-0.2.0.post2}/telegrinder/tools/error_handler/abc.py +0 -0
  117. {telegrinder-0.2.0 → telegrinder-0.2.0.post2}/telegrinder/tools/error_handler/error.py +0 -0
  118. {telegrinder-0.2.0 → telegrinder-0.2.0.post2}/telegrinder/tools/error_handler/error_handler.py +0 -0
  119. {telegrinder-0.2.0 → telegrinder-0.2.0.post2}/telegrinder/tools/formatting/__init__.py +0 -0
  120. {telegrinder-0.2.0 → telegrinder-0.2.0.post2}/telegrinder/tools/formatting/html.py +0 -0
  121. {telegrinder-0.2.0 → telegrinder-0.2.0.post2}/telegrinder/tools/formatting/links.py +0 -0
  122. {telegrinder-0.2.0 → telegrinder-0.2.0.post2}/telegrinder/tools/formatting/spec_html_formats.py +0 -0
  123. {telegrinder-0.2.0 → telegrinder-0.2.0.post2}/telegrinder/tools/global_context/__init__.py +0 -0
  124. {telegrinder-0.2.0 → telegrinder-0.2.0.post2}/telegrinder/tools/global_context/abc.py +0 -0
  125. {telegrinder-0.2.0 → telegrinder-0.2.0.post2}/telegrinder/tools/global_context/global_context.py +0 -0
  126. {telegrinder-0.2.0 → telegrinder-0.2.0.post2}/telegrinder/tools/global_context/telegrinder_ctx.py +0 -0
  127. {telegrinder-0.2.0 → telegrinder-0.2.0.post2}/telegrinder/tools/i18n/__init__.py +0 -0
  128. {telegrinder-0.2.0 → telegrinder-0.2.0.post2}/telegrinder/tools/i18n/base.py +0 -0
  129. {telegrinder-0.2.0 → telegrinder-0.2.0.post2}/telegrinder/tools/i18n/middleware/__init__.py +0 -0
  130. {telegrinder-0.2.0 → telegrinder-0.2.0.post2}/telegrinder/tools/i18n/middleware/base.py +0 -0
  131. {telegrinder-0.2.0 → telegrinder-0.2.0.post2}/telegrinder/tools/i18n/simple.py +0 -0
  132. {telegrinder-0.2.0 → telegrinder-0.2.0.post2}/telegrinder/tools/kb_set/__init__.py +0 -0
  133. {telegrinder-0.2.0 → telegrinder-0.2.0.post2}/telegrinder/tools/kb_set/base.py +0 -0
  134. {telegrinder-0.2.0 → telegrinder-0.2.0.post2}/telegrinder/tools/kb_set/yaml.py +0 -0
  135. {telegrinder-0.2.0 → telegrinder-0.2.0.post2}/telegrinder/tools/limited_dict.py +0 -0
  136. {telegrinder-0.2.0 → telegrinder-0.2.0.post2}/telegrinder/tools/loop_wrapper/__init__.py +0 -0
  137. {telegrinder-0.2.0 → telegrinder-0.2.0.post2}/telegrinder/tools/loop_wrapper/abc.py +0 -0
  138. {telegrinder-0.2.0 → telegrinder-0.2.0.post2}/telegrinder/tools/loop_wrapper/loop_wrapper.py +0 -0
  139. {telegrinder-0.2.0 → telegrinder-0.2.0.post2}/telegrinder/tools/magic.py +0 -0
  140. {telegrinder-0.2.0 → telegrinder-0.2.0.post2}/telegrinder/tools/parse_mode.py +0 -0
  141. {telegrinder-0.2.0 → telegrinder-0.2.0.post2}/telegrinder/types/__init__.py +0 -0
  142. {telegrinder-0.2.0 → telegrinder-0.2.0.post2}/telegrinder/types/enums.py +0 -0
  143. {telegrinder-0.2.0 → telegrinder-0.2.0.post2}/telegrinder/types/methods.py +0 -0
  144. {telegrinder-0.2.0 → telegrinder-0.2.0.post2}/telegrinder/types/objects.py +0 -0
  145. {telegrinder-0.2.0 → telegrinder-0.2.0.post2}/telegrinder/verification_utils.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: telegrinder
3
- Version: 0.2.0
3
+ Version: 0.2.0.post2
4
4
  Summary: Modern visionary telegram bot framework.
5
5
  Home-page: https://github.com/timoniq/telegrinder
6
6
  License: MIT
@@ -1,6 +1,6 @@
1
1
  [tool.poetry]
2
2
  name = "telegrinder"
3
- version = "0.2.0"
3
+ version = "0.2.0.post2"
4
4
  description = "Modern visionary telegram bot framework."
5
5
  authors = ["timoniq <tesseradecades@mail.ru>"]
6
6
  maintainers = ["luwqz1 <howluwqz1@gmail.com>"]
@@ -55,22 +55,21 @@ if typing.TYPE_CHECKING:
55
55
  ...
56
56
 
57
57
  else:
58
- from fntypes.co import Some, Variative
58
+ from fntypes.co import Nothing, Some, Variative
59
59
 
60
60
  from telegrinder.msgspec_utils import Option, decoder
61
61
  from telegrinder.msgspec_utils import get_class_annotations as _get_class_annotations
62
62
 
63
- def _get_cute_from_args(args):
64
- for hint in args:
65
- if not isinstance(hint, type):
66
- hint = typing.get_origin(hint) or hint
67
- if not isinstance(hint, type):
68
- continue
63
+ def _get_cute_from_generic(generic_args):
64
+ for arg in generic_args:
65
+ orig_arg = typing.get_origin(arg) or arg
69
66
 
70
- if hint in (Variative, Some, Option):
71
- return _get_cute_from_args(typing.get_args(hint))
72
- if issubclass(hint, BaseCute):
73
- return hint
67
+ if not isinstance(orig_arg, type):
68
+ continue
69
+ if orig_arg in (Variative, Some, Option):
70
+ return _get_cute_from_generic(typing.get_args(arg))
71
+ if issubclass(arg, BaseCute):
72
+ return arg
74
73
 
75
74
  return None
76
75
 
@@ -79,8 +78,8 @@ else:
79
78
 
80
79
  for key, hint in annotations.items():
81
80
  if not isinstance(hint, type):
82
- if (val := _get_cute_from_args(typing.get_args(hint))) is not None:
83
- cute_annotations[key] = val
81
+ if (cute := _get_cute_from_generic(typing.get_args(hint))) is not None:
82
+ cute_annotations[key] = cute
84
83
 
85
84
  elif issubclass(hint, BaseCute):
86
85
  cute_annotations[key] = hint
@@ -120,7 +119,7 @@ else:
120
119
  cls.__cute_annotations__[field].from_update(_get_value(value), bound_api=bound_api),
121
120
  type=cls.__annotations__[field],
122
121
  )
123
- if field in cls.__cute_annotations__ and value
122
+ if field in cls.__cute_annotations__ and not isinstance(value, Nothing)
124
123
  else value
125
124
  for field, value in update.to_dict().items()
126
125
  },
@@ -1,8 +1,9 @@
1
1
  import typing
2
+ from functools import cached_property
2
3
 
3
4
  from fntypes.co import Nothing, Some
4
5
 
5
- from telegrinder.api import API
6
+ from telegrinder.api.api import API
6
7
  from telegrinder.bot.cute_types.base import BaseCute
7
8
  from telegrinder.bot.cute_types.callback_query import CallbackQueryCute
8
9
  from telegrinder.bot.cute_types.chat_join_request import ChatJoinRequestCute
@@ -10,9 +11,9 @@ from telegrinder.bot.cute_types.chat_member_updated import ChatMemberUpdatedCute
10
11
  from telegrinder.bot.cute_types.inline_query import InlineQueryCute
11
12
  from telegrinder.bot.cute_types.message import MessageCute
12
13
  from telegrinder.msgspec_utils import Option
13
- from telegrinder.types import Model, Update
14
+ from telegrinder.types.objects import Model, Update
14
15
 
15
- ModelT = typing.TypeVar("ModelT", bound=Model)
16
+ EventModel = typing.TypeVar("EventModel", bound=Model)
16
17
 
17
18
 
18
19
  class UpdateCute(BaseCute[Update], Update, kw_only=True):
@@ -61,11 +62,11 @@ class UpdateCute(BaseCute[Update], Update, kw_only=True):
61
62
  """Optional. A request to join the chat has been sent. The bot must have the can_invite_users
62
63
  administrator right in the chat to receive these updates."""
63
64
 
64
- @property
65
+ @cached_property
65
66
  def incoming_update(self) -> Model:
66
67
  return getattr(self, self.update_type.value).unwrap()
67
68
 
68
- def get_event(self, event_model: type[ModelT]) -> Option[ModelT]:
69
+ def get_event(self, event_model: type[EventModel]) -> Option[EventModel]:
69
70
  if isinstance(self.incoming_update, event_model):
70
71
  return Some(self.incoming_update)
71
72
  return Nothing()
@@ -135,12 +135,18 @@ class CallbackDataJsonEq(CallbackQueryDataRule):
135
135
 
136
136
 
137
137
  class CallbackDataJsonModel(CallbackQueryDataRule):
138
- def __init__(self, model: type[msgspec.Struct] | type[DataclassInstance], /) -> None:
138
+ def __init__(
139
+ self,
140
+ model: type[msgspec.Struct] | type[DataclassInstance],
141
+ *,
142
+ alias: str | None = None,
143
+ ) -> None:
139
144
  self.model = model
145
+ self.alias = alias or "data"
140
146
 
141
147
  async def check(self, event: CallbackQuery, ctx: Context) -> bool:
142
148
  with suppress(BaseException):
143
- ctx.data = decoder.decode(event.data.unwrap().encode(), type=self.model)
149
+ ctx.set(self.alias, decoder.decode(event.data.unwrap().encode(), type=self.model))
144
150
  return True
145
151
  return False
146
152
 
@@ -16,10 +16,17 @@ if typing.TYPE_CHECKING:
16
16
 
17
17
  T = typing.TypeVar("T")
18
18
 
19
+
20
+ def rename_field(name: str) -> str:
21
+ if name.endswith("_") and name.removesuffix("_") in keyword.kwlist:
22
+ return name.removesuffix("_")
23
+ return name if not keyword.iskeyword(name) else name + "_"
24
+
25
+
19
26
  MODEL_CONFIG: typing.Final[dict[str, typing.Any]] = {
20
27
  "omit_defaults": True,
21
28
  "dict": True,
22
- "rename": {kw + "_": kw for kw in keyword.kwlist},
29
+ "rename": rename_field,
23
30
  }
24
31
 
25
32
 
@@ -58,11 +65,6 @@ def get_params(params: dict[str, typing.Any]) -> dict[str, typing.Any]:
58
65
  return validated_params
59
66
 
60
67
 
61
- def encode_name(name: str) -> str:
62
- # TODO
63
- return MODEL_CONFIG.get("rename", {}).get(name, name)
64
-
65
-
66
68
  class Model(msgspec.Struct, **MODEL_CONFIG):
67
69
  @classmethod
68
70
  def from_data(cls, data: dict[str, typing.Any]) -> typing.Self:
@@ -33,7 +33,9 @@ def is_generator(function: typing.Callable[..., typing.Any]) -> typing.TypeGuard
33
33
 
34
34
 
35
35
  def get_node_calc_lst(node: type["Node"]) -> list[type["Node"]]:
36
- """ Returns flattened list of node types in ordering required to calculate given node. Provides caching for passed node type """
36
+ """Returns flattened list of node types in ordering required to calculate given node.
37
+ Provides caching for passed node type"""
38
+
37
39
  if calc_lst := getattr(node, "__nodes_calc_lst__", None):
38
40
  return calc_lst
39
41
  nodes_lst: list[type["Node"]] = []
@@ -101,6 +103,10 @@ if typing.TYPE_CHECKING:
101
103
  pass
102
104
 
103
105
  else:
106
+ def __init_subclass__(cls, *args, **kwargs): # noqa: N807
107
+ if any(issubclass(base, ScalarNode) for base in cls.__bases__ if base is not ScalarNode):
108
+ raise RuntimeError("Scalar nodes do not support inheritance.")
109
+
104
110
  def create_node(cls, bases, dct):
105
111
  dct.update(cls.__dict__)
106
112
  return type(cls.__name__, bases, dct)
@@ -112,6 +118,7 @@ else:
112
118
  {
113
119
  "as_node": classmethod(lambda cls: create_node(cls, bases, dct)),
114
120
  "scope": Node.scope,
121
+ "__init_subclass__": __init_subclass__,
115
122
  },
116
123
  )
117
124
 
@@ -1,10 +1,10 @@
1
1
  import typing
2
2
  from dataclasses import dataclass, field
3
3
 
4
- from fntypes import Nothing, Option, Some
4
+ from fntypes.option import Nothing, Option, Some
5
5
 
6
- from .base import DataNode
7
- from .text import Text
6
+ from telegrinder.node.base import DataNode
7
+ from telegrinder.node.text import Text
8
8
 
9
9
 
10
10
  def single_split(s: str, separator: str) -> tuple[str, str]:
@@ -46,7 +46,7 @@ async def compose_nodes(
46
46
  logger.debug("Composing nodes: {!r}...", nodes)
47
47
 
48
48
  parent_nodes: dict[type[Node], NodeSession] = {}
49
- event_nodes: dict[type[Node], "NodeSession"] = ctx.get_or_set(CONTEXT_STORE_NODES_KEY, {})
49
+ event_nodes: dict[type[Node], NodeSession] = ctx.get_or_set(CONTEXT_STORE_NODES_KEY, {})
50
50
  data = {Context: ctx} | (data or {})
51
51
 
52
52
  # Create flattened list of ordered nodes to be calculated
@@ -1,3 +1,4 @@
1
+ import dataclasses
1
2
  import typing
2
3
 
3
4
  import msgspec
@@ -5,21 +6,18 @@ import msgspec
5
6
  from telegrinder.api import API
6
7
  from telegrinder.bot.cute_types import BaseCute
7
8
  from telegrinder.bot.dispatch.context import Context
8
- from telegrinder.msgspec_utils import DataclassInstance
9
- from telegrinder.node.base import ComposeError, DataNode, Node
9
+ from telegrinder.msgspec_utils import DataclassInstance, decoder
10
+ from telegrinder.node.base import ComposeError, Node
10
11
  from telegrinder.node.update import UpdateNode
11
12
 
12
13
  if typing.TYPE_CHECKING:
13
14
  Dataclass = typing.TypeVar("Dataclass", bound="DataclassType")
14
15
 
15
- DataclassType: typing.TypeAlias = DataclassInstance | DataNode | msgspec.Struct | dict[str, typing.Any]
16
+ DataclassType: typing.TypeAlias = DataclassInstance | msgspec.Struct | dict[str, typing.Any]
16
17
 
17
18
  EVENT_NODE_KEY = "_event_node"
18
19
 
19
20
 
20
- from telegrinder.msgspec_utils import decoder
21
-
22
-
23
21
  class _EventNode(Node):
24
22
  dataclass: type["DataclassType"]
25
23
 
@@ -41,13 +39,17 @@ class _EventNode(Node):
41
39
  dataclass = cls.dataclass(**raw_update.incoming_update.to_full_dict())
42
40
 
43
41
  elif issubclass(dataclass_type, BaseCute):
44
- dataclass = dataclass_type.from_update(raw_update.incoming_update, bound_api=api)
42
+ if isinstance(raw_update.incoming_update, dataclass_type):
43
+ dataclass = raw_update.incoming_update
44
+ else:
45
+ dataclass = dataclass_type.from_update(raw_update.incoming_update, bound_api=api)
45
46
 
46
- elif issubclass(dataclass_type, (msgspec.Struct, DataclassInstance)): # type: ignore
47
- # FIXME: must be used with encode_name
47
+ elif issubclass(dataclass_type, msgspec.Struct) or dataclasses.is_dataclass(dataclass_type):
48
+ # FIXME: must be used with rename_field
48
49
  dataclass = decoder.convert(
49
50
  raw_update.incoming_update.to_full_dict(),
50
51
  type=cls.dataclass,
52
+ str_keys=True,
51
53
  )
52
54
 
53
55
  else:
@@ -61,6 +63,7 @@ class _EventNode(Node):
61
63
 
62
64
  if typing.TYPE_CHECKING:
63
65
  EventNode: typing.TypeAlias = typing.Annotated["Dataclass", ...]
66
+
64
67
  else:
65
68
  class EventNode(_EventNode):
66
69
  pass
@@ -1,8 +1,7 @@
1
1
  from telegrinder.api.api import API
2
- from telegrinder.types import User
3
-
4
- from .base import ComposeError, ScalarNode
5
- from .scope import GLOBAL
2
+ from telegrinder.node.base import ComposeError, ScalarNode
3
+ from telegrinder.node.scope import GLOBAL
4
+ from telegrinder.types.objects import User
6
5
 
7
6
 
8
7
  class Me(ScalarNode, User):
@@ -22,12 +22,12 @@ class Polymorphic(Node):
22
22
  composition = Composition(impl, True)
23
23
  node_collection = await composition.compose_nodes(update, context)
24
24
  if node_collection is None:
25
- logger.debug("Impl {} composition failed", impl.__name__)
25
+ logger.debug("Impl {!r} composition failed", impl.__name__)
26
26
  continue
27
27
 
28
28
  # To determine whether this is a right morph, all subnodes must be resolved
29
29
  if scope is NodeScope.PER_EVENT and (cls, i) in node_ctx:
30
- logger.debug("Morph is already cached as per_event node, using its value. Impl {} succeeded", impl.__name__)
30
+ logger.debug("Morph is already cached as per_event node, using its value. Impl {!r} succeeded", impl.__name__)
31
31
  res: NodeSession = node_ctx[(cls, i)]
32
32
  await node_collection.close_all()
33
33
  return res.value
@@ -40,7 +40,7 @@ class Polymorphic(Node):
40
40
  node_ctx[(cls, i)] = NodeSession(cls, result, {})
41
41
 
42
42
  await node_collection.close_all(with_value=result)
43
- logger.debug("Impl {} succeeded with value {}", impl.__name__, result)
43
+ logger.debug("Impl {!r} succeeded with value {}", impl.__name__, result)
44
44
  return result
45
45
 
46
46
  raise ComposeError("No implementation found.")
@@ -26,6 +26,11 @@ class KeyboardModel:
26
26
  keyboard: list[list[DictStrAny]]
27
27
 
28
28
 
29
+
30
+ def copy_keyboard(keyboard: list[list[DictStrAny]]) -> list[list[DictStrAny]]:
31
+ return [row.copy() for row in keyboard]
32
+
33
+
29
34
  class ABCMarkup(ABC, typing.Generic[ButtonT]):
30
35
  BUTTON: type[ButtonT]
31
36
  keyboard: list[list[DictStrAny]]
@@ -73,7 +78,7 @@ class ABCMarkup(ABC, typing.Generic[ButtonT]):
73
78
  return copy_keyboard
74
79
 
75
80
  def merge(self, other: typing.Self) -> typing.Self:
76
- self.keyboard.extend(other.keyboard)
81
+ self.keyboard.extend(copy_keyboard(other.keyboard))
77
82
  return self
78
83
 
79
84
 
File without changes
File without changes