telegrinder 0.1.dev158__py3-none-any.whl → 0.1.dev160__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 (128) hide show
  1. telegrinder/__init__.py +4 -2
  2. telegrinder/api/__init__.py +0 -0
  3. telegrinder/api/abc.py +6 -4
  4. telegrinder/api/api.py +25 -11
  5. telegrinder/api/error.py +0 -0
  6. telegrinder/api/response.py +0 -0
  7. telegrinder/bot/__init__.py +0 -0
  8. telegrinder/bot/bot.py +0 -0
  9. telegrinder/bot/cute_types/__init__.py +0 -0
  10. telegrinder/bot/cute_types/base.py +100 -9
  11. telegrinder/bot/cute_types/callback_query.py +426 -37
  12. telegrinder/bot/cute_types/inline_query.py +44 -17
  13. telegrinder/bot/cute_types/message.py +2885 -117
  14. telegrinder/bot/cute_types/update.py +14 -7
  15. telegrinder/bot/cute_types/utils.py +542 -0
  16. telegrinder/bot/dispatch/__init__.py +0 -0
  17. telegrinder/bot/dispatch/abc.py +0 -0
  18. telegrinder/bot/dispatch/composition.py +0 -0
  19. telegrinder/bot/dispatch/context.py +0 -0
  20. telegrinder/bot/dispatch/dispatch.py +0 -0
  21. telegrinder/bot/dispatch/handler/__init__.py +0 -0
  22. telegrinder/bot/dispatch/handler/abc.py +0 -0
  23. telegrinder/bot/dispatch/handler/func.py +1 -1
  24. telegrinder/bot/dispatch/handler/message_reply.py +0 -0
  25. telegrinder/bot/dispatch/middleware/__init__.py +0 -0
  26. telegrinder/bot/dispatch/middleware/abc.py +0 -0
  27. telegrinder/bot/dispatch/process.py +0 -0
  28. telegrinder/bot/dispatch/return_manager/__init__.py +0 -0
  29. telegrinder/bot/dispatch/return_manager/abc.py +0 -0
  30. telegrinder/bot/dispatch/return_manager/callback_query.py +0 -0
  31. telegrinder/bot/dispatch/return_manager/inline_query.py +0 -0
  32. telegrinder/bot/dispatch/return_manager/message.py +0 -0
  33. telegrinder/bot/dispatch/view/__init__.py +0 -0
  34. telegrinder/bot/dispatch/view/abc.py +40 -29
  35. telegrinder/bot/dispatch/view/box.py +0 -0
  36. telegrinder/bot/dispatch/view/callback_query.py +0 -0
  37. telegrinder/bot/dispatch/view/inline_query.py +0 -0
  38. telegrinder/bot/dispatch/view/message.py +0 -0
  39. telegrinder/bot/dispatch/waiter_machine/__init__.py +0 -0
  40. telegrinder/bot/dispatch/waiter_machine/machine.py +0 -0
  41. telegrinder/bot/dispatch/waiter_machine/middleware.py +0 -0
  42. telegrinder/bot/dispatch/waiter_machine/short_state.py +0 -0
  43. telegrinder/bot/polling/__init__.py +0 -0
  44. telegrinder/bot/polling/abc.py +0 -0
  45. telegrinder/bot/polling/polling.py +7 -11
  46. telegrinder/bot/rules/__init__.py +0 -0
  47. telegrinder/bot/rules/abc.py +1 -1
  48. telegrinder/bot/rules/adapter/__init__.py +0 -0
  49. telegrinder/bot/rules/adapter/abc.py +0 -0
  50. telegrinder/bot/rules/adapter/errors.py +0 -0
  51. telegrinder/bot/rules/adapter/event.py +12 -6
  52. telegrinder/bot/rules/adapter/raw_update.py +0 -0
  53. telegrinder/bot/rules/callback_data.py +3 -11
  54. telegrinder/bot/rules/command.py +0 -0
  55. telegrinder/bot/rules/enum_text.py +0 -0
  56. telegrinder/bot/rules/func.py +0 -0
  57. telegrinder/bot/rules/fuzzy.py +0 -0
  58. telegrinder/bot/rules/inline.py +2 -1
  59. telegrinder/bot/rules/integer.py +0 -0
  60. telegrinder/bot/rules/is_from.py +0 -0
  61. telegrinder/bot/rules/markup.py +3 -1
  62. telegrinder/bot/rules/mention.py +0 -0
  63. telegrinder/bot/rules/message_entities.py +3 -1
  64. telegrinder/bot/rules/regex.py +1 -1
  65. telegrinder/bot/rules/rule_enum.py +0 -0
  66. telegrinder/bot/rules/start.py +0 -0
  67. telegrinder/bot/rules/text.py +0 -0
  68. telegrinder/bot/scenario/__init__.py +0 -0
  69. telegrinder/bot/scenario/abc.py +0 -0
  70. telegrinder/bot/scenario/checkbox.py +0 -0
  71. telegrinder/bot/scenario/choice.py +0 -0
  72. telegrinder/client/__init__.py +2 -2
  73. telegrinder/client/abc.py +35 -12
  74. telegrinder/client/aiohttp.py +35 -22
  75. telegrinder/model.py +70 -23
  76. telegrinder/modules.py +22 -12
  77. telegrinder/msgspec_json.py +0 -0
  78. telegrinder/msgspec_utils.py +30 -10
  79. telegrinder/node/__init__.py +0 -0
  80. telegrinder/node/attachment.py +0 -0
  81. telegrinder/node/base.py +0 -0
  82. telegrinder/node/composer.py +0 -0
  83. telegrinder/node/container.py +0 -0
  84. telegrinder/node/message.py +0 -0
  85. telegrinder/node/rule.py +0 -0
  86. telegrinder/node/source.py +0 -0
  87. telegrinder/node/text.py +0 -0
  88. telegrinder/node/tools/__init__.py +0 -0
  89. telegrinder/node/tools/generator.py +0 -0
  90. telegrinder/node/update.py +0 -0
  91. telegrinder/rules.py +0 -0
  92. telegrinder/tools/__init__.py +2 -30
  93. telegrinder/tools/buttons.py +19 -5
  94. telegrinder/tools/error_handler/__init__.py +2 -0
  95. telegrinder/tools/error_handler/abc.py +5 -1
  96. telegrinder/tools/error_handler/error.py +10 -0
  97. telegrinder/tools/error_handler/error_handler.py +100 -81
  98. telegrinder/tools/formatting/__init__.py +0 -0
  99. telegrinder/tools/formatting/html.py +0 -0
  100. telegrinder/tools/formatting/links.py +0 -0
  101. telegrinder/tools/formatting/spec_html_formats.py +0 -0
  102. telegrinder/tools/global_context/__init__.py +0 -0
  103. telegrinder/tools/global_context/abc.py +0 -0
  104. telegrinder/tools/global_context/global_context.py +65 -67
  105. telegrinder/tools/global_context/telegrinder_ctx.py +0 -0
  106. telegrinder/tools/i18n/__init__.py +0 -0
  107. telegrinder/tools/i18n/base.py +0 -0
  108. telegrinder/tools/i18n/middleware/__init__.py +0 -0
  109. telegrinder/tools/i18n/middleware/base.py +0 -0
  110. telegrinder/tools/i18n/simple.py +0 -0
  111. telegrinder/tools/kb_set/__init__.py +0 -0
  112. telegrinder/tools/kb_set/base.py +0 -0
  113. telegrinder/tools/kb_set/yaml.py +3 -3
  114. telegrinder/tools/keyboard.py +17 -26
  115. telegrinder/tools/loop_wrapper/__init__.py +0 -0
  116. telegrinder/tools/loop_wrapper/abc.py +0 -0
  117. telegrinder/tools/loop_wrapper/loop_wrapper.py +0 -0
  118. telegrinder/tools/magic.py +1 -1
  119. telegrinder/tools/parse_mode.py +0 -0
  120. telegrinder/types/__init__.py +0 -0
  121. telegrinder/types/enums.py +13 -9
  122. telegrinder/types/methods.py +783 -673
  123. telegrinder/types/objects.py +317 -163
  124. {telegrinder-0.1.dev158.dist-info → telegrinder-0.1.dev160.dist-info}/LICENSE +0 -0
  125. {telegrinder-0.1.dev158.dist-info → telegrinder-0.1.dev160.dist-info}/METADATA +9 -7
  126. {telegrinder-0.1.dev158.dist-info → telegrinder-0.1.dev160.dist-info}/RECORD +42 -41
  127. {telegrinder-0.1.dev158.dist-info → telegrinder-0.1.dev160.dist-info}/WHEEL +1 -1
  128. telegrinder/tools/inline_query.py +0 -684
@@ -16,6 +16,12 @@ from telegrinder.msgspec_utils import Option
16
16
  from telegrinder.types.objects import Update
17
17
 
18
18
  EventType = typing.TypeVar("EventType", bound=BaseCute)
19
+ MiddlewareT = typing.TypeVar("MiddlewareT", bound=ABCMiddleware)
20
+
21
+ FuncType: typing.TypeAlias = typing.Callable[
22
+ typing.Concatenate[EventType, ...],
23
+ typing.Coroutine[typing.Any, typing.Any, typing.Any],
24
+ ]
19
25
 
20
26
 
21
27
  class ABCView(ABC):
@@ -40,7 +46,7 @@ class ABCStateView(ABCView, typing.Generic[EventType]):
40
46
  def __repr__(self) -> str:
41
47
  return "<{!r}: {}>".format(
42
48
  self.__class__.__name__,
43
- ", ".join(f"{k}={v!r}" for k, v in self.__dict__.items())
49
+ ", ".join(f"{k}={v!r}" for k, v in self.__dict__.items()),
44
50
  )
45
51
 
46
52
 
@@ -55,21 +61,19 @@ class BaseView(ABCView, typing.Generic[EventType]):
55
61
  for base in cls.__dict__.get("__orig_bases__", ()):
56
62
  if issubclass(typing.get_origin(base) or base, ABCView):
57
63
  for generic_type in typing.get_args(base):
58
- if issubclass(typing.get_origin(generic_type) or generic_type, BaseCute):
64
+ if issubclass(
65
+ typing.get_origin(generic_type) or generic_type, BaseCute
66
+ ):
59
67
  return Some(generic_type)
60
68
  return Nothing()
61
-
69
+
62
70
  @classmethod
63
- def get_event_raw(cls, update: Update) -> Option[Model]:
64
- match cls.get_event_type():
65
- case Some(event_type):
66
- for field in update.__struct_fields__:
67
- event_raw = getattr(update, field)
68
- if isinstance(event_raw, Some | Nothing):
69
- event_raw = event_raw.unwrap_or_none()
70
- if event_raw is not None and issubclass(event_type, event_raw.__class__):
71
- return Some(event_raw)
72
- return Nothing()
71
+ def get_raw_event(cls, update: Update) -> Option[Model]:
72
+ match update.update_type:
73
+ case Some(update_type):
74
+ return getattr(update, update_type.value)
75
+ case _:
76
+ return Nothing()
73
77
 
74
78
  def __call__(
75
79
  self,
@@ -77,13 +81,8 @@ class BaseView(ABCView, typing.Generic[EventType]):
77
81
  is_blocking: bool = True,
78
82
  error_handler: ErrorHandlerT | None = None,
79
83
  ):
80
- def wrapper(
81
- func: typing.Callable[
82
- typing.Concatenate[EventType, ...],
83
- typing.Coroutine,
84
- ]
85
- ):
86
- func_handler = FuncHandler(
84
+ def wrapper(func: FuncType[EventType]):
85
+ func_handler = FuncHandler[EventType, FuncType[EventType], ErrorHandlerT](
87
86
  func,
88
87
  [*self.auto_rules, *rules],
89
88
  is_blocking,
@@ -94,22 +93,34 @@ class BaseView(ABCView, typing.Generic[EventType]):
94
93
  return func_handler
95
94
 
96
95
  return wrapper
97
-
96
+
98
97
  def register_middleware(self, *args: typing.Any, **kwargs: typing.Any):
99
- def wrapper(cls: type[ABCMiddleware[EventType]]):
98
+ def wrapper(cls: type[MiddlewareT]) -> type[MiddlewareT]:
100
99
  self.middlewares.append(cls(*args, **kwargs))
101
100
  return cls
102
-
101
+
103
102
  return wrapper
104
-
103
+
105
104
  async def check(self, event: Update) -> bool:
106
- return bool(self.get_event_raw(event))
107
-
105
+ match self.get_raw_event(event):
106
+ case Some(e) if issubclass(
107
+ self.get_event_type().expect(
108
+ "{!r} has no event type in generic.".format(self.__class__.__name__),
109
+ ),
110
+ e.__class__,
111
+ ):
112
+ return True
113
+ case _:
114
+ return False
115
+
108
116
  async def process(self, event: Update, api: ABCAPI) -> bool:
109
- event_raw = self.get_event_raw(event).unwrap()
110
- event_type = self.get_event_type().unwrap()
111
117
  return await process_inner(
112
- event_type(**event_raw.to_dict(), api=api),
118
+ self.get_event_type()
119
+ .unwrap()
120
+ .from_update(
121
+ update=self.get_raw_event(event).unwrap(),
122
+ bound_api=api,
123
+ ),
113
124
  event,
114
125
  self.middlewares,
115
126
  self.handlers,
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
@@ -71,16 +71,14 @@ class Polling(ABCPolling):
71
71
  async def listen(self) -> typing.AsyncGenerator[list[Update], None]:
72
72
  logger.debug("Listening polling")
73
73
  reconn_counter = 0
74
-
74
+
75
75
  while not self._stop:
76
76
  try:
77
77
  updates = await self.get_updates()
78
78
  reconn_counter = 0
79
79
  if not updates:
80
80
  continue
81
- updates_list: list[Update] = decoder.decode(
82
- updates, type=list[Update]
83
- )
81
+ updates_list: list[Update] = decoder.decode(updates, type=list[Update])
84
82
  if updates_list:
85
83
  yield updates_list
86
84
  self.offset = updates_list[-1].update_id + 1
@@ -100,16 +98,14 @@ class Polling(ABCPolling):
100
98
  self.stop()
101
99
  exit(9)
102
100
  else:
103
- logger.warning("Server disconnected, waiting 5 seconds to reconnetion...")
101
+ logger.warning(
102
+ "Server disconnected, waiting 5 seconds to reconnetion...",
103
+ )
104
104
  reconn_counter += 1
105
105
  await asyncio.sleep(self.reconnection_timeout)
106
106
  except aiohttp.ClientConnectorError:
107
- logger.error(
108
- "Client connection failed, polling stopping! "
109
- "Please, check your internet connection."
110
- )
111
- self.stop()
112
- exit(3)
107
+ logger.error("Client connection failed, attempted to reconnect...")
108
+ await asyncio.sleep(self.reconnection_timeout)
113
109
  except BaseException as e:
114
110
  logger.exception(e)
115
111
 
File without changes
@@ -19,7 +19,7 @@ Update: typing.TypeAlias = UpdateCute
19
19
  def with_caching_translations(func):
20
20
  """Should be used as decorator for .translate method. Caches rule translations."""
21
21
 
22
- async def wrapper(self: "ABCRule", translator: ABCTranslator):
22
+ async def wrapper(self: "ABCRule[typing.Any]", translator: ABCTranslator):
23
23
  if translation := get_cached_translation(self, translator.locale):
24
24
  return translation
25
25
  translation = await func(self, translator)
File without changes
File without changes
File without changes
@@ -14,14 +14,20 @@ CuteT = typing.TypeVar("CuteT", bound=BaseCute)
14
14
 
15
15
 
16
16
  class EventAdapter(ABCAdapter[Update, CuteT]):
17
- def __init__(self, event_name: str, model: type[CuteT]):
17
+ def __init__(self, event_name: str, model: type[CuteT]) -> None:
18
18
  self.event_name = event_name
19
19
  self.model = model
20
20
 
21
21
  def __repr__(self) -> str:
22
- return "<{}: adapt Update.{} -> {}>".format(
22
+ raw_update_type = Update.__annotations__.get(self.event_name, "Unknown")
23
+ raw_update_type = (
24
+ typing.get_args(raw_update_type)[0].__forward_arg__
25
+ if typing.get_args(raw_update_type)
26
+ else raw_update_type
27
+ )
28
+ return "<{}: adapt {} -> {}>".format(
23
29
  self.__class__.__name__,
24
- self.event_name,
30
+ raw_update_type,
25
31
  self.model.__name__,
26
32
  )
27
33
 
@@ -29,14 +35,14 @@ class EventAdapter(ABCAdapter[Update, CuteT]):
29
35
  update_dct = update.to_dict()
30
36
  if self.event_name not in update_dct:
31
37
  return Error(
32
- AdapterError(f"Update is not of event type {self.event_name!r}.")
38
+ AdapterError(f"Update is not of event type {self.event_name!r}."),
33
39
  )
34
40
  if update_dct[self.event_name] is Nothing:
35
41
  return Error(
36
- AdapterError(f"Update is not an {self.event_name!r}.")
42
+ AdapterError(f"Update is not an {self.event_name!r}."),
37
43
  )
38
44
  return Ok(
39
- self.model.from_update(update_dct[self.event_name].unwrap(), bound_api=api)
45
+ self.model.from_update(update_dct[self.event_name].unwrap(), bound_api=api),
40
46
  )
41
47
 
42
48
 
File without changes
@@ -14,18 +14,10 @@ from telegrinder.tools.buttons import DataclassInstance
14
14
  from .abc import ABCRule
15
15
  from .markup import Markup, PatternLike, check_string
16
16
 
17
- if typing.TYPE_CHECKING:
17
+ T = typing.TypeVar("T")
18
18
 
19
- T = typing.TypeVar("T")
20
- Ref: typing.TypeAlias = typing.Annotated[T, ...]
21
- else:
22
-
23
- class Ref:
24
- def __class_getitem__(cls, code: str) -> typing.ForwardRef:
25
- return typing.ForwardRef(code)
26
-
27
-
28
- CallbackQuery = CallbackQueryCute
19
+ Ref: typing.TypeAlias = typing.Annotated[T, ...]
20
+ CallbackQuery: typing.TypeAlias = CallbackQueryCute
29
21
  Validator: typing.TypeAlias = typing.Callable[[typing.Any], bool | typing.Awaitable[bool]]
30
22
  MapDict: typing.TypeAlias = dict[
31
23
  str, typing.Any | type[typing.Any] | Validator | list[Ref["MapDict"]] | Ref["MapDict"]
File without changes
File without changes
File without changes
File without changes
@@ -1,4 +1,5 @@
1
1
  import abc
2
+ import typing
2
3
 
3
4
  from telegrinder.bot.cute_types import InlineQueryCute
4
5
  from telegrinder.bot.dispatch.context import Context
@@ -8,7 +9,7 @@ from telegrinder.types.enums import ChatType
8
9
 
9
10
  from .markup import Markup, PatternLike, check_string
10
11
 
11
- InlineQuery = InlineQueryCute
12
+ InlineQuery: typing.TypeAlias = InlineQueryCute
12
13
 
13
14
 
14
15
  class InlineQueryRule(ABCRule[InlineQuery], abc.ABC):
File without changes
File without changes
@@ -1,3 +1,5 @@
1
+ import typing
2
+
1
3
  import vbml
2
4
 
3
5
  from telegrinder.bot.dispatch.context import Context
@@ -6,7 +8,7 @@ from telegrinder.tools.global_context import TelegrinderCtx
6
8
  from .abc import Message
7
9
  from .text import TextMessageRule
8
10
 
9
- PatternLike = str | vbml.Pattern
11
+ PatternLike: typing.TypeAlias = str | vbml.Pattern
10
12
  global_ctx = TelegrinderCtx()
11
13
 
12
14
 
File without changes
@@ -1,10 +1,12 @@
1
+ import typing
2
+
1
3
  from telegrinder.bot.dispatch.context import Context
2
4
  from telegrinder.types.enums import MessageEntityType
3
5
  from telegrinder.types.objects import MessageEntity
4
6
 
5
7
  from .abc import Message, MessageRule
6
8
 
7
- Entity = str | MessageEntityType
9
+ Entity: typing.TypeAlias = str | MessageEntityType
8
10
 
9
11
 
10
12
  class HasEntities(MessageRule):
@@ -6,7 +6,7 @@ from telegrinder.bot.dispatch.context import Context
6
6
  from .abc import Message
7
7
  from .text import TextMessageRule
8
8
 
9
- PatternLike = str | typing.Pattern[str]
9
+ PatternLike: typing.TypeAlias = str | typing.Pattern[str]
10
10
 
11
11
 
12
12
  class Regex(TextMessageRule):
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
@@ -1,4 +1,4 @@
1
- from .abc import ABCClient, ClientData
1
+ from .abc import ABCClient
2
2
  from .aiohttp import AiohttpClient
3
3
 
4
- __all__ = ("ABCClient", "AiohttpClient", "ClientData")
4
+ __all__ = ("ABCClient", "AiohttpClient")
telegrinder/client/abc.py CHANGED
@@ -1,35 +1,49 @@
1
1
  import typing
2
2
  from abc import ABC, abstractmethod
3
3
 
4
- ClientData = typing.Any
5
-
6
4
 
7
5
  class ABCClient(ABC):
8
6
  @abstractmethod
9
- def __init__(self, *args, **kwargs):
7
+ def __init__(self, *args: typing.Any, **kwargs: typing.Any):
10
8
  pass
11
9
 
12
10
  @abstractmethod
13
11
  async def request_text(
14
- self, url: str, method: str = "GET", data: dict | None = None, **kwargs
12
+ self,
13
+ url: str,
14
+ method: str = "GET",
15
+ data: dict[str, typing.Any] | None = None,
16
+ **kwargs: typing.Any,
15
17
  ) -> str:
16
18
  pass
17
19
 
18
20
  @abstractmethod
19
21
  async def request_json(
20
- self, url: str, method: str = "GET", data: dict | None = None, **kwargs
21
- ) -> dict:
22
+ self,
23
+ url: str,
24
+ method: str = "GET",
25
+ data: dict[str, typing.Any] | None = None,
26
+ **kwargs: typing.Any,
27
+ ) -> dict[str, typing.Any]:
22
28
  pass
23
29
 
24
30
  @abstractmethod
25
31
  async def request_content(
26
- self, url: str, method: str = "GET", data: dict | None = None, **kwargs
32
+ self,
33
+ url: str,
34
+ method: str = "GET",
35
+ data: dict[str, typing.Any] | None = None,
36
+ **kwargs: typing.Any,
27
37
  ) -> bytes:
28
38
  pass
29
39
 
30
40
  @abstractmethod
31
41
  async def request_bytes(
32
- self, url: str, method: str = "GET", data: dict | None = None, **kwargs
42
+ self,
43
+ url: str,
44
+ method: str = "GET",
45
+ data: dict[str, typing.Any] | None = None,
46
+ **kwargs: typing.Any,
33
47
  ) -> bytes:
34
48
  pass
35
49
 
@@ -39,14 +53,23 @@ class ABCClient(ABC):
39
53
 
40
54
  @classmethod
41
55
  @abstractmethod
42
- def get_form(cls, data: dict) -> typing.Any:
56
+ def get_form(
57
+ cls,
58
+ data: dict[str, typing.Any],
59
+ files: dict[str, tuple[str, bytes]] | None = None,
60
+ ) -> typing.Any:
43
61
  pass
44
62
 
45
- async def __aenter__(self) -> "ABCClient":
63
+ async def __aenter__(self) -> typing.Self:
46
64
  return self
47
65
 
48
- async def __aexit__(self, exc_type, exc_val, exc_tb) -> None:
66
+ async def __aexit__(
67
+ self,
68
+ exc_type: type[BaseException],
69
+ exc_val: typing.Any,
70
+ exc_tb: typing.Any,
71
+ ) -> None:
49
72
  await self.close()
50
73
 
51
74
 
52
- __all__ = ("ABCClient", "ClientData")
75
+ __all__ = ("ABCClient",)
@@ -1,3 +1,4 @@
1
+ import secrets
1
2
  import ssl
2
3
  import typing
3
4
 
@@ -18,19 +19,27 @@ class AiohttpClient(ABCClient):
18
19
  session: ClientSession | None = None,
19
20
  json_processing_module: JSONModule | None = None,
20
21
  timeout: aiohttp.ClientTimeout | None = None,
21
- **session_params,
22
- ):
22
+ **session_params: typing.Any,
23
+ ) -> None:
23
24
  self.session = session
24
25
  self.json_processing_module = json_processing_module or json
25
26
  self.session_params = session_params
26
27
  self.timeout = timeout or aiohttp.ClientTimeout(total=0)
27
-
28
+
29
+ def __repr__(self) -> str:
30
+ return "<{}: session={!r}, timeout={}, closed={}>".format(
31
+ self.__class__.__name__,
32
+ self.session,
33
+ self.timeout,
34
+ False if self.session is None else self.session.closed,
35
+ )
36
+
28
37
  async def request_raw(
29
38
  self,
30
39
  url: str,
31
40
  method: str = "GET",
32
- data: dict | None = None,
33
- **kwargs,
41
+ data: dict[str, typing.Any] | None = None,
42
+ **kwargs: typing.Any,
34
43
  ) -> "ClientResponse":
35
44
  if not self.session:
36
45
  self.session = ClientSession(
@@ -54,9 +63,9 @@ class AiohttpClient(ABCClient):
54
63
  self,
55
64
  url: str,
56
65
  method: str = "GET",
57
- data: dict | None = None,
58
- **kwargs,
59
- ) -> dict:
66
+ data: dict[str, typing.Any] | None = None,
67
+ **kwargs: typing.Any,
68
+ ) -> dict[str, typing.Any]:
60
69
  response = await self.request_raw(url, method, data, **kwargs)
61
70
  return await response.json(
62
71
  encoding="utf-8",
@@ -68,8 +77,8 @@ class AiohttpClient(ABCClient):
68
77
  self,
69
78
  url: str,
70
79
  method: str = "GET",
71
- data: dict | aiohttp.FormData | None = None,
72
- **kwargs,
80
+ data: dict[str, typing.Any] | aiohttp.FormData | None = None,
81
+ **kwargs: typing.Any,
73
82
  ) -> str:
74
83
  response = await self.request_raw(url, method, data, **kwargs) # type: ignore
75
84
  return await response.text(encoding="utf-8")
@@ -78,8 +87,8 @@ class AiohttpClient(ABCClient):
78
87
  self,
79
88
  url: str,
80
89
  method: str = "GET",
81
- data: dict | aiohttp.FormData | None = None,
82
- **kwargs,
90
+ data: dict[str, typing.Any] | aiohttp.FormData | None = None,
91
+ **kwargs: typing.Any,
83
92
  ) -> bytes:
84
93
  response = await self.request_raw(url, method, data, **kwargs) # type: ignore
85
94
  if response._body is None:
@@ -90,8 +99,8 @@ class AiohttpClient(ABCClient):
90
99
  self,
91
100
  url: str,
92
101
  method: str = "GET",
93
- data: dict | None = None,
94
- **kwargs,
102
+ data: dict[str, typing.Any] | None = None,
103
+ **kwargs: typing.Any,
95
104
  ) -> bytes:
96
105
  response = await self.request_raw(url, method, data, **kwargs)
97
106
  return response._body
@@ -101,18 +110,22 @@ class AiohttpClient(ABCClient):
101
110
  await self.session.close()
102
111
 
103
112
  @classmethod
104
- def get_form(cls, data: dict) -> aiohttp.formdata.FormData:
113
+ def get_form(
114
+ cls,
115
+ data: dict[str, typing.Any],
116
+ files: dict[str, tuple[str, bytes]] | None = None,
117
+ ) -> aiohttp.formdata.FormData:
118
+ files = files or {}
105
119
  form = aiohttp.formdata.FormData(quote_fields=False)
106
120
  for k, v in data.items():
107
- params = {}
108
- if isinstance(v, tuple):
109
- params["filename"], v = v[0], v[1]
110
- else:
111
- v = str(v)
112
- form.add_field(k, v, **params)
121
+ form.add_field(k, str(v))
122
+
123
+ for n, f in files.items():
124
+ form.add_field(n, f[1], filename=f[0])
125
+
113
126
  return form
114
127
 
115
- def __del__(self):
128
+ def __del__(self) -> None:
116
129
  if self.session and not self.session.closed:
117
130
  if self.session._connector is not None and self.session._connector_owner:
118
131
  self.session._connector.close()