Pytdbot 0.8.4.dev2__tar.gz → 0.8.4.dev3__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.
Files changed (58) hide show
  1. {Pytdbot-0.8.4.dev2 → Pytdbot-0.8.4.dev3}/PKG-INFO +3 -2
  2. {Pytdbot-0.8.4.dev2 → Pytdbot-0.8.4.dev3}/Pytdbot.egg-info/PKG-INFO +3 -2
  3. {Pytdbot-0.8.4.dev2 → Pytdbot-0.8.4.dev3}/README.md +2 -1
  4. {Pytdbot-0.8.4.dev2 → Pytdbot-0.8.4.dev3}/pytdbot/__init__.py +1 -1
  5. {Pytdbot-0.8.4.dev2 → Pytdbot-0.8.4.dev3}/pytdbot/client.py +76 -68
  6. {Pytdbot-0.8.4.dev2 → Pytdbot-0.8.4.dev3}/LICENSE +0 -0
  7. {Pytdbot-0.8.4.dev2 → Pytdbot-0.8.4.dev3}/MANIFEST.in +0 -0
  8. {Pytdbot-0.8.4.dev2 → Pytdbot-0.8.4.dev3}/Pytdbot.egg-info/SOURCES.txt +0 -0
  9. {Pytdbot-0.8.4.dev2 → Pytdbot-0.8.4.dev3}/Pytdbot.egg-info/dependency_links.txt +0 -0
  10. {Pytdbot-0.8.4.dev2 → Pytdbot-0.8.4.dev3}/Pytdbot.egg-info/requires.txt +0 -0
  11. {Pytdbot-0.8.4.dev2 → Pytdbot-0.8.4.dev3}/Pytdbot.egg-info/top_level.txt +0 -0
  12. {Pytdbot-0.8.4.dev2 → Pytdbot-0.8.4.dev3}/pytdbot/exception/__init__.py +0 -0
  13. {Pytdbot-0.8.4.dev2 → Pytdbot-0.8.4.dev3}/pytdbot/filters.py +0 -0
  14. {Pytdbot-0.8.4.dev2 → Pytdbot-0.8.4.dev3}/pytdbot/generate_files.py +0 -0
  15. {Pytdbot-0.8.4.dev2 → Pytdbot-0.8.4.dev3}/pytdbot/generate_json.py +0 -0
  16. {Pytdbot-0.8.4.dev2 → Pytdbot-0.8.4.dev3}/pytdbot/handlers/__init__.py +0 -0
  17. {Pytdbot-0.8.4.dev2 → Pytdbot-0.8.4.dev3}/pytdbot/handlers/decorators.py +0 -0
  18. {Pytdbot-0.8.4.dev2 → Pytdbot-0.8.4.dev3}/pytdbot/handlers/handler.py +0 -0
  19. {Pytdbot-0.8.4.dev2 → Pytdbot-0.8.4.dev3}/pytdbot/handlers/updates.py +0 -0
  20. {Pytdbot-0.8.4.dev2 → Pytdbot-0.8.4.dev3}/pytdbot/methods/__init__.py +0 -0
  21. {Pytdbot-0.8.4.dev2 → Pytdbot-0.8.4.dev3}/pytdbot/methods/methods.py +0 -0
  22. {Pytdbot-0.8.4.dev2 → Pytdbot-0.8.4.dev3}/pytdbot/methods/tdlibfunctions.py +0 -0
  23. {Pytdbot-0.8.4.dev2 → Pytdbot-0.8.4.dev3}/pytdbot/td_api.json +0 -0
  24. {Pytdbot-0.8.4.dev2 → Pytdbot-0.8.4.dev3}/pytdbot/td_api.tl +0 -0
  25. {Pytdbot-0.8.4.dev2 → Pytdbot-0.8.4.dev3}/pytdbot/tdjson/__init__.py +0 -0
  26. {Pytdbot-0.8.4.dev2 → Pytdbot-0.8.4.dev3}/pytdbot/tdjson/tdjson.py +0 -0
  27. {Pytdbot-0.8.4.dev2 → Pytdbot-0.8.4.dev3}/pytdbot/types/__init__.py +0 -0
  28. {Pytdbot-0.8.4.dev2 → Pytdbot-0.8.4.dev3}/pytdbot/types/buttons/__init__.py +0 -0
  29. {Pytdbot-0.8.4.dev2 → Pytdbot-0.8.4.dev3}/pytdbot/types/buttons/base.py +0 -0
  30. {Pytdbot-0.8.4.dev2 → Pytdbot-0.8.4.dev3}/pytdbot/types/buttons/force_reply.py +0 -0
  31. {Pytdbot-0.8.4.dev2 → Pytdbot-0.8.4.dev3}/pytdbot/types/buttons/inline_keyboard.py +0 -0
  32. {Pytdbot-0.8.4.dev2 → Pytdbot-0.8.4.dev3}/pytdbot/types/buttons/remove_keyboard.py +0 -0
  33. {Pytdbot-0.8.4.dev2 → Pytdbot-0.8.4.dev3}/pytdbot/types/buttons/show_keyboard.py +0 -0
  34. {Pytdbot-0.8.4.dev2 → Pytdbot-0.8.4.dev3}/pytdbot/types/inputfile/__init__.py +0 -0
  35. {Pytdbot-0.8.4.dev2 → Pytdbot-0.8.4.dev3}/pytdbot/types/inputfile/base.py +0 -0
  36. {Pytdbot-0.8.4.dev2 → Pytdbot-0.8.4.dev3}/pytdbot/types/inputfile/input_file_generated.py +0 -0
  37. {Pytdbot-0.8.4.dev2 → Pytdbot-0.8.4.dev3}/pytdbot/types/inputfile/input_file_id.py +0 -0
  38. {Pytdbot-0.8.4.dev2 → Pytdbot-0.8.4.dev3}/pytdbot/types/inputfile/input_file_local.py +0 -0
  39. {Pytdbot-0.8.4.dev2 → Pytdbot-0.8.4.dev3}/pytdbot/types/inputfile/input_file_remote.py +0 -0
  40. {Pytdbot-0.8.4.dev2 → Pytdbot-0.8.4.dev3}/pytdbot/types/inputfile/input_thumbnail.py +0 -0
  41. {Pytdbot-0.8.4.dev2 → Pytdbot-0.8.4.dev3}/pytdbot/types/logstream/__init__.py +0 -0
  42. {Pytdbot-0.8.4.dev2 → Pytdbot-0.8.4.dev3}/pytdbot/types/logstream/base.py +0 -0
  43. {Pytdbot-0.8.4.dev2 → Pytdbot-0.8.4.dev3}/pytdbot/types/logstream/log_stream_default.py +0 -0
  44. {Pytdbot-0.8.4.dev2 → Pytdbot-0.8.4.dev3}/pytdbot/types/logstream/log_stream_empty.py +0 -0
  45. {Pytdbot-0.8.4.dev2 → Pytdbot-0.8.4.dev3}/pytdbot/types/logstream/log_stream_file.py +0 -0
  46. {Pytdbot-0.8.4.dev2 → Pytdbot-0.8.4.dev3}/pytdbot/types/plugins/__init__.py +0 -0
  47. {Pytdbot-0.8.4.dev2 → Pytdbot-0.8.4.dev3}/pytdbot/types/plugins/plugins.py +0 -0
  48. {Pytdbot-0.8.4.dev2 → Pytdbot-0.8.4.dev3}/pytdbot/types/result/__init__.py +0 -0
  49. {Pytdbot-0.8.4.dev2 → Pytdbot-0.8.4.dev3}/pytdbot/types/result/result.py +0 -0
  50. {Pytdbot-0.8.4.dev2 → Pytdbot-0.8.4.dev3}/pytdbot/types/update/__init__.py +0 -0
  51. {Pytdbot-0.8.4.dev2 → Pytdbot-0.8.4.dev3}/pytdbot/types/update/chatActions.py +0 -0
  52. {Pytdbot-0.8.4.dev2 → Pytdbot-0.8.4.dev3}/pytdbot/types/update/update.py +0 -0
  53. {Pytdbot-0.8.4.dev2 → Pytdbot-0.8.4.dev3}/pytdbot/utils/__init__.py +0 -0
  54. {Pytdbot-0.8.4.dev2 → Pytdbot-0.8.4.dev3}/pytdbot/utils/escape.py +0 -0
  55. {Pytdbot-0.8.4.dev2 → Pytdbot-0.8.4.dev3}/pytdbot/utils/text_format.py +0 -0
  56. {Pytdbot-0.8.4.dev2 → Pytdbot-0.8.4.dev3}/requirements.txt +0 -0
  57. {Pytdbot-0.8.4.dev2 → Pytdbot-0.8.4.dev3}/setup.cfg +0 -0
  58. {Pytdbot-0.8.4.dev2 → Pytdbot-0.8.4.dev3}/setup.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: Pytdbot
3
- Version: 0.8.4.dev2
3
+ Version: 0.8.4.dev3
4
4
  Summary: Easy-to-use asynchronous TDLib wrapper for Python.
5
5
  Home-page: https://github.com/pytdbot/client
6
6
  Author: AYMEN Mohammed
@@ -36,10 +36,11 @@ Pytdbot (Python TDLib) is an asynchronous [**TDLib**](https://github.com/tdlib/t
36
36
  ### Installation
37
37
  > For better performance, it's recommended to install [orjson](https://github.com/ijl/orjson#install) or [ujson](https://github.com/ultrajson/ultrajson#ultrajson).
38
38
 
39
+ You can install Pytdbot using pip:
39
40
  ```bash
40
41
  pip install pytdbot
41
42
  ```
42
- From github (dev version)
43
+ To install the development version from Github, use the following command:
43
44
  ```bash
44
45
  pip install git+https://github.com/pytdbot/client.git
45
46
  ```
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: Pytdbot
3
- Version: 0.8.4.dev2
3
+ Version: 0.8.4.dev3
4
4
  Summary: Easy-to-use asynchronous TDLib wrapper for Python.
5
5
  Home-page: https://github.com/pytdbot/client
6
6
  Author: AYMEN Mohammed
@@ -36,10 +36,11 @@ Pytdbot (Python TDLib) is an asynchronous [**TDLib**](https://github.com/tdlib/t
36
36
  ### Installation
37
37
  > For better performance, it's recommended to install [orjson](https://github.com/ijl/orjson#install) or [ujson](https://github.com/ultrajson/ultrajson#ultrajson).
38
38
 
39
+ You can install Pytdbot using pip:
39
40
  ```bash
40
41
  pip install pytdbot
41
42
  ```
42
- From github (dev version)
43
+ To install the development version from Github, use the following command:
43
44
  ```bash
44
45
  pip install git+https://github.com/pytdbot/client.git
45
46
  ```
@@ -20,10 +20,11 @@ Pytdbot (Python TDLib) is an asynchronous [**TDLib**](https://github.com/tdlib/t
20
20
  ### Installation
21
21
  > For better performance, it's recommended to install [orjson](https://github.com/ijl/orjson#install) or [ujson](https://github.com/ultrajson/ultrajson#ultrajson).
22
22
 
23
+ You can install Pytdbot using pip:
23
24
  ```bash
24
25
  pip install pytdbot
25
26
  ```
26
- From github (dev version)
27
+ To install the development version from Github, use the following command:
27
28
  ```bash
28
29
  pip install git+https://github.com/pytdbot/client.git
29
30
  ```
@@ -2,7 +2,7 @@ from . import types, utils, filters, exception
2
2
  from .tdjson import TdJson
3
3
  from .client import Client
4
4
 
5
- __version__ = "0.8.4dev2"
5
+ __version__ = "0.8.4dev3"
6
6
  __copyright__ = "Copyright (c) 2022-2023 AYMEN Mohammed ~ https://github.com/AYMENJD"
7
7
  __license__ = "MIT License"
8
8
 
@@ -12,8 +12,7 @@ from typing import Callable, Union
12
12
  from logging import getLogger, DEBUG
13
13
  from base64 import b64encode
14
14
  from deepdiff import DeepDiff
15
- from concurrent.futures import ThreadPoolExecutor
16
- from threading import current_thread, main_thread
15
+ from threading import current_thread, main_thread, Thread
17
16
  from json import dumps
18
17
 
19
18
  from .tdjson import TdJson
@@ -94,7 +93,7 @@ class Client(Decorators, Methods):
94
93
  If any request is rate limited (flood waited) the client will repeat the request after sleeping the required amount of seconds returned by the error. If the ``retry after`` value is higher than ``sleep_threshold`` the error is returned. Defaults to ``None`` (Disabled)
95
94
 
96
95
  workers (``int``, *optional*):
97
- Number of workers for handling updates. Defaults to ``5``
96
+ Number of workers to handle updates. Defaults to ``5``. If set to ``None``, updates will be immediately handled instead of being queued, which can impact performance.
98
97
 
99
98
  td_verbosity (``int``, *optional*):
100
99
  Verbosity level of TDLib. Defaults to ``2``
@@ -140,7 +139,7 @@ class Client(Decorators, Methods):
140
139
  self.default_parse_mode = (
141
140
  default_parse_mode
142
141
  if isinstance(default_parse_mode, str)
143
- and default_parse_mode in ["markdown", "markdownv2", "html"]
142
+ and default_parse_mode in ("markdown", "markdownv2", "html")
144
143
  else None
145
144
  )
146
145
  self.system_language_code = system_language_code
@@ -169,9 +168,9 @@ class Client(Decorators, Methods):
169
168
  self._handlers = {"initializer": [], "finalizer": []}
170
169
  self._results = {}
171
170
  self._tdjson = TdJson(lib_path, td_verbosity)
172
- self._executor = ThreadPoolExecutor(5, "Pytdbot")
173
- self._workers_tasks = None
174
171
  self._retry_after_prefex = "Too Many Requests: retry after "
172
+ self._workers_tasks = None
173
+ self.__listen_loop_thread = Thread(target=self.__listen_loop, daemon=True)
175
174
  self.__authorization_state = None
176
175
  self.__authorization = None
177
176
  self.__cache = {"is_coro_filter": {}}
@@ -218,14 +217,19 @@ class Client(Decorators, Methods):
218
217
  if not self.is_running:
219
218
  logger.info("Starting pytdbot client...")
220
219
 
221
- self._workers_tasks = [
222
- self.loop.create_task(self._update_worker(x + 1))
223
- for x in range(self.workers)
224
- ]
220
+ if isinstance(self.workers, int):
221
+ self._workers_tasks = [
222
+ self.loop.create_task(self._queue_update_worker())
223
+ for x in range(self.workers)
224
+ ]
225
+ self.__is_queue_worker = True
225
226
 
226
- logger.info("Started with %s workers", self.workers)
227
+ logger.info("Started with %s workers", self.workers)
228
+ else:
229
+ self.__is_queue_worker = False
230
+ logger.info("Started with unlimited updates processes")
227
231
 
228
- self.loop.create_task(self.__listen_loop())
232
+ self.__listen_loop_thread.start()
229
233
 
230
234
  if login:
231
235
  await self.login()
@@ -238,7 +242,7 @@ class Client(Decorators, Methods):
238
242
 
239
243
  self.__login = True
240
244
 
241
- await self.getOption("version") # Ping TDLib to start authorization proccess
245
+ await self.getOption("version") # Ping TDLib to start authorization process
242
246
 
243
247
  while self.authorization_state != "authorizationStateReady":
244
248
  await asyncio.sleep(0.1)
@@ -503,12 +507,7 @@ class Client(Decorators, Methods):
503
507
  def __send(self, request: dict) -> None:
504
508
  return self._tdjson.send(
505
509
  request
506
- ) # tdjson.send is asynchronous, So we don't need run_in_executor. This improves performance
507
-
508
- async def __receive(self, timeout: float = 2.0) -> dict:
509
- return await self.loop.run_in_executor(
510
- self._executor, self._tdjson.receive, timeout
511
- )
510
+ ) # tdjson.send is non-blocking method, So we don't need run_in_executor. This improves performance
512
511
 
513
512
  def _check_init_args(self):
514
513
  if not isinstance(self.__api_id, int):
@@ -523,14 +522,12 @@ class Client(Decorators, Methods):
523
522
  raise TypeError("files_directory must be str")
524
523
  elif not isinstance(self.td_verbosity, int):
525
524
  raise TypeError("td_verbosity must be int")
526
- elif not isinstance(self.workers, int):
527
- raise TypeError("workers must be int")
528
525
  elif type(Update) is not type(self.update_class):
529
526
  raise TypeError(
530
527
  "update_class must be instance of class pytdbot.types.Update"
531
528
  )
532
529
 
533
- if self.workers < 1:
530
+ if isinstance(self.workers, int) and self.workers < 1:
534
531
  raise ValueError("workers must be greater than 0")
535
532
 
536
533
  def get_retry_after_time(self, error_message: str) -> int:
@@ -589,13 +586,13 @@ class Client(Decorators, Methods):
589
586
  self.__cache["is_coro_filter"][func] = is_coro
590
587
  return is_coro
591
588
 
592
- async def __listen_loop(self):
589
+ def __listen_loop(self):
593
590
  try:
594
591
  self.is_running = True
595
592
  logger.info("Listening to updates...")
596
593
 
597
594
  while self.is_running:
598
- update = await self.__receive(100000.0) # seconds
595
+ update = self._tdjson.receive(100000.0) # seconds
599
596
  if update is None:
600
597
  continue
601
598
  self._process_update(update)
@@ -624,19 +621,36 @@ class Client(Decorators, Methods):
624
621
  logger.error(f"{update['@extra']['option']}: {update['message']}")
625
622
  else:
626
623
  if update["@type"] == "updateAuthorizationState":
627
- self.loop.create_task(self.__handle_authorization_state(update))
624
+ asyncio.run_coroutine_threadsafe(
625
+ self.__handle_authorization_state(update), loop=self.loop
626
+ )
628
627
  elif update["@type"] == "updateMessageSendSucceeded":
629
- self.loop.create_task(self.__handle_update_message_succeeded(update))
628
+ asyncio.run_coroutine_threadsafe(
629
+ self.__handle_update_message_succeeded(update), loop=self.loop
630
+ )
630
631
  elif update["@type"] == "updateMessageSendFailed":
631
- self.loop.create_task(self.__handle_update_message_failed(update))
632
+ asyncio.run_coroutine_threadsafe(
633
+ self.__handle_update_message_failed(update), loop=self.loop
634
+ )
632
635
  elif update["@type"] == "updateConnectionState":
633
- self.loop.create_task(self.__handle_connection_state(update))
636
+ asyncio.run_coroutine_threadsafe(
637
+ self.__handle_connection_state(update), loop=self.loop
638
+ )
634
639
  elif update["@type"] == "updateOption":
635
- self.loop.create_task(self.__handle_update_option(update))
640
+ asyncio.run_coroutine_threadsafe(
641
+ self.__handle_update_option(update), loop=self.loop
642
+ )
636
643
  elif update["@type"] == "updateUser":
637
- self.loop.create_task(self.__handle_update_user(update))
644
+ asyncio.run_coroutine_threadsafe(
645
+ self.__handle_update_user(update), loop=self.loop
646
+ )
638
647
 
639
- self.queue.put_nowait(update)
648
+ if self.__is_queue_worker:
649
+ self.queue.put_nowait(update)
650
+ else:
651
+ asyncio.run_coroutine_threadsafe(
652
+ self._handle_update(update), loop=self.loop
653
+ )
640
654
 
641
655
  async def __run_initializers(self, update):
642
656
  for initializer in self._handlers["initializer"]:
@@ -692,39 +706,35 @@ class Client(Decorators, Methods):
692
706
  except Exception:
693
707
  logger.exception(f"Finalizer {finalizer} failed")
694
708
 
695
- async def _update_worker(self, worker_id: int):
696
- self.is_running = True
697
- while self.is_running:
698
- try:
699
- update = await self.queue.get()
700
- if "@type" not in update:
701
- continue
709
+ async def _handle_update(self, update):
710
+ if (
711
+ logger.root.level >= DEBUG
712
+ ): # dumping all updates can create performance issues
713
+ logger.debug(
714
+ f"Received: {dumps(update, indent=4)}",
715
+ )
702
716
 
703
- if (
704
- logger.root.level >= DEBUG
705
- ): # dumping all updates can create performance issues
706
- logger.debug(
707
- f"w{worker_id}: Received: {dumps(update, indent=4)}",
708
- )
717
+ if update["@type"] in self._handlers:
718
+ update = self.update_class(self, update)
719
+ if (
720
+ update["@type"] == "updateNewMessage"
721
+ and update["message"]["is_outgoing"]
722
+ and "sending_state" in update["message"]
723
+ ):
724
+ return
709
725
 
710
- if update["@type"] in self._handlers:
711
- update = self.update_class(self, update)
712
- if (
713
- update["@type"] == "updateNewMessage"
714
- and update["message"]["is_outgoing"]
715
- and "sending_state" in update["message"]
716
- ):
717
- continue
726
+ try:
727
+ await self.__run_initializers(update)
728
+ await self.__run_handlers(update)
729
+ except StopHandlers:
730
+ pass
731
+ finally:
732
+ await self.__run_finalizers(update)
718
733
 
719
- try:
720
- await self.__run_initializers(update)
721
- await self.__run_handlers(update)
722
- except StopHandlers:
723
- pass
724
- finally:
725
- await self.__run_finalizers(update)
726
- except Exception:
727
- logger.exception("Exception in _update_worker")
734
+ async def _queue_update_worker(self):
735
+ self.is_running = True
736
+ while self.is_running:
737
+ await self._handle_update(await self.queue.get())
728
738
 
729
739
  async def set_td_paramaters(self):
730
740
  """Make a call to :meth:`~pytdbot.Client.setTdlibParameters` with the current client init parameters
@@ -1014,8 +1024,7 @@ class Client(Decorators, Methods):
1014
1024
  print(f"Your 2FA password hint is: {self.__authorization['password_hint']}")
1015
1025
 
1016
1026
  while self.is_running:
1017
- password = await self.loop.run_in_executor(
1018
- self._executor,
1027
+ password = await asyncio.to_thread(
1019
1028
  getpass,
1020
1029
  "Enter your 2FA password {}: ".format(
1021
1030
  "(empty to recover)"
@@ -1074,10 +1083,9 @@ class Client(Decorators, Methods):
1074
1083
  self.is_authenticated = False
1075
1084
  self.is_running = False
1076
1085
 
1077
- for worker_task in self._workers_tasks:
1078
- worker_task.cancel()
1079
-
1080
- self._executor.shutdown(wait=False, cancel_futures=True)
1086
+ if self.__is_queue_worker:
1087
+ for worker_task in self._workers_tasks:
1088
+ worker_task.cancel()
1081
1089
 
1082
1090
  def _register_signal_handlers(self):
1083
1091
  def _handle_signal():
@@ -1103,7 +1111,7 @@ class Client(Decorators, Methods):
1103
1111
  pass
1104
1112
 
1105
1113
  def __ainput(self, prompt: str):
1106
- return self.loop.run_in_executor(self._executor, input, prompt)
1114
+ return asyncio.to_thread(input, prompt)
1107
1115
 
1108
1116
  def _print_welcome(self):
1109
1117
  print(f"Welcome to Pytdbot (v{pytdbot.__version__}). {pytdbot.__copyright__}")
File without changes
File without changes
File without changes
File without changes