danielutils 1.1.28__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (287) hide show
  1. danielutils/__init__.py +50 -0
  2. danielutils/abstractions/__init__.py +3 -0
  3. danielutils/abstractions/db/__init__.py +10 -0
  4. danielutils/abstractions/db/database.py +206 -0
  5. danielutils/abstractions/db/database_definitions.py +219 -0
  6. danielutils/abstractions/db/database_exceptions.py +34 -0
  7. danielutils/abstractions/db/database_factory.py +70 -0
  8. danielutils/abstractions/db/database_initializer.py +261 -0
  9. danielutils/abstractions/db/database_models.py +213 -0
  10. danielutils/abstractions/db/dependencies.py +6 -0
  11. danielutils/abstractions/db/implementations/__init__.py +4 -0
  12. danielutils/abstractions/db/implementations/in_memory_database.py +365 -0
  13. danielutils/abstractions/db/implementations/persistent_in_memory_database.py +174 -0
  14. danielutils/abstractions/db/implementations/redis_database.py +403 -0
  15. danielutils/abstractions/db/implementations/sqlite_database.py +491 -0
  16. danielutils/abstractions/multiprogramming/__init__.py +3 -0
  17. danielutils/abstractions/multiprogramming/multi_id.py +26 -0
  18. danielutils/abstractions/multiprogramming/worker.py +104 -0
  19. danielutils/abstractions/multiprogramming/worker_pool.py +98 -0
  20. danielutils/abstractions/repl.py +125 -0
  21. danielutils/aliases.py +174 -0
  22. danielutils/async_/__init__.py +7 -0
  23. danielutils/async_/async_cmd.py +28 -0
  24. danielutils/async_/async_command.py +879 -0
  25. danielutils/async_/async_layered_command.py +125 -0
  26. danielutils/async_/async_retry_executor.py +86 -0
  27. danielutils/async_/async_worker_pool.py +110 -0
  28. danielutils/async_/time_strategy.py +73 -0
  29. danielutils/async_/utils.py +57 -0
  30. danielutils/better_builtins/__init__.py +3 -0
  31. danielutils/better_builtins/counter.py +48 -0
  32. danielutils/better_builtins/frange.py +266 -0
  33. danielutils/better_builtins/typed_builtins/__init__.py +4 -0
  34. danielutils/better_builtins/typed_builtins/factory.py +126 -0
  35. danielutils/better_builtins/typed_builtins/tdict.py +36 -0
  36. danielutils/better_builtins/typed_builtins/tlist.py +223 -0
  37. danielutils/better_builtins/typed_builtins/tset.py +67 -0
  38. danielutils/better_builtins/typed_builtins/ttuple.py +15 -0
  39. danielutils/colors.py +203 -0
  40. danielutils/context_managers/__init__.py +5 -0
  41. danielutils/context_managers/attr_context.py +40 -0
  42. danielutils/context_managers/multi_context.py +34 -0
  43. danielutils/context_managers/optional_context.py +33 -0
  44. danielutils/context_managers/state_context.py +25 -0
  45. danielutils/context_managers/temporary_file.py +108 -0
  46. danielutils/convenience.py +12 -0
  47. danielutils/conversions/__init__.py +2 -0
  48. danielutils/conversions/main_conversions.py +131 -0
  49. danielutils/conversions/specialized_conversions/__init__.py +2 -0
  50. danielutils/conversions/specialized_conversions/to_hex.py +25 -0
  51. danielutils/conversions/specialized_conversions/to_int.py +19 -0
  52. danielutils/custom_types.py +18 -0
  53. danielutils/data_structures/__init__.py +8 -0
  54. danielutils/data_structures/algorithms.py +52 -0
  55. danielutils/data_structures/comparer.py +47 -0
  56. danielutils/data_structures/default_dict.py +23 -0
  57. danielutils/data_structures/functions.py +35 -0
  58. danielutils/data_structures/graph/__init__.py +4 -0
  59. danielutils/data_structures/graph/binary_node.py +71 -0
  60. danielutils/data_structures/graph/graph.py +208 -0
  61. danielutils/data_structures/graph/multinode.py +101 -0
  62. danielutils/data_structures/graph/node.py +60 -0
  63. danielutils/data_structures/heap/__init__.py +3 -0
  64. danielutils/data_structures/heap/heap.py +119 -0
  65. danielutils/data_structures/heap/max_heap.py +18 -0
  66. danielutils/data_structures/heap/min_heap.py +18 -0
  67. danielutils/data_structures/queue/__init__.py +3 -0
  68. danielutils/data_structures/queue/atomic_queue.py +15 -0
  69. danielutils/data_structures/queue/priority_queue.py +110 -0
  70. danielutils/data_structures/queue/queue.py +92 -0
  71. danielutils/data_structures/stack.py +102 -0
  72. danielutils/data_structures/trees/__init__.py +2 -0
  73. danielutils/data_structures/trees/binary_syntax_tree.py +84 -0
  74. danielutils/data_structures/trees/binary_tree.py +63 -0
  75. danielutils/date.py +43 -0
  76. danielutils/date_time.py +23 -0
  77. danielutils/decorators/__init__.py +18 -0
  78. danielutils/decorators/atomic.py +45 -0
  79. danielutils/decorators/attach.py +61 -0
  80. danielutils/decorators/chain_decorators.py +39 -0
  81. danielutils/decorators/decorate_conditionally.py +32 -0
  82. danielutils/decorators/delay_call.py +47 -0
  83. danielutils/decorators/deprecate.py +46 -0
  84. danielutils/decorators/final.py +39 -0
  85. danielutils/decorators/limit_recursion.py +63 -0
  86. danielutils/decorators/memo.py +68 -0
  87. danielutils/decorators/normalize_decorator.py +55 -0
  88. danielutils/decorators/overload.py +204 -0
  89. danielutils/decorators/partially_implemented.py +32 -0
  90. danielutils/decorators/processify.py +74 -0
  91. danielutils/decorators/property.py +42 -0
  92. danielutils/decorators/singleton.py +59 -0
  93. danielutils/decorators/threadify.py +40 -0
  94. danielutils/decorators/timeout.py +82 -0
  95. danielutils/decorators/total_ordering.py +82 -0
  96. danielutils/decorators/validate.py +227 -0
  97. danielutils/exceptions.py +90 -0
  98. danielutils/functions/__init__.py +13 -0
  99. danielutils/functions/areoneof.py +47 -0
  100. danielutils/functions/check_foreach.py +39 -0
  101. danielutils/functions/factorial.py +24 -0
  102. danielutils/functions/flatten.py +33 -0
  103. danielutils/functions/foreach.py +20 -0
  104. danielutils/functions/isoftype.py +475 -0
  105. danielutils/functions/isoneof.py +71 -0
  106. danielutils/functions/multiloop.py +51 -0
  107. danielutils/functions/parallel_for.py +47 -0
  108. danielutils/functions/partition.py +45 -0
  109. danielutils/functions/powerset.py +31 -0
  110. danielutils/functions/subseteq.py +25 -0
  111. danielutils/functions/types_subseteq.py +51 -0
  112. danielutils/generators/__init__.py +3 -0
  113. danielutils/generators/conditional_generator.py +59 -0
  114. danielutils/generators/generator_from_stream.py +31 -0
  115. danielutils/generators/join_generators.py +109 -0
  116. danielutils/internet.py +92 -0
  117. danielutils/io_.py +451 -0
  118. danielutils/java/__init__.py +18 -0
  119. danielutils/java/interfaces/__init__.py +1 -0
  120. danielutils/java/interfaces/comparable.py +31 -0
  121. danielutils/java/java_interface.py +59 -0
  122. danielutils/logging_/__init__.py +2 -0
  123. danielutils/logging_/_impl/__init__.py +4 -0
  124. danielutils/logging_/_impl/builtin_impls/__init__.py +2 -0
  125. danielutils/logging_/_impl/builtin_impls/file_logger.py +35 -0
  126. danielutils/logging_/_impl/builtin_impls/print_logger.py +21 -0
  127. danielutils/logging_/_impl/log_level.py +13 -0
  128. danielutils/logging_/_impl/logger.py +109 -0
  129. danielutils/logging_/_impl/logger_strategy_impl_base.py +37 -0
  130. danielutils/logging_/utils.py +199 -0
  131. danielutils/lombok/__init__.py +1 -0
  132. danielutils/lombok/builder.py +68 -0
  133. danielutils/math_/__init__.py +2 -0
  134. danielutils/math_/constants.py +103 -0
  135. danielutils/math_/functions.py +34 -0
  136. danielutils/math_/math_print.py +52 -0
  137. danielutils/math_/math_symbols.py +125 -0
  138. danielutils/metaclasses/__init__.py +5 -0
  139. danielutils/metaclasses/atomic_class_meta.py +36 -0
  140. danielutils/metaclasses/implicit_data_deleter_meta.py +87 -0
  141. danielutils/metaclasses/instance_cache_meta.py +66 -0
  142. danielutils/metaclasses/interface.py +328 -0
  143. danielutils/metaclasses/overload_meta.py +48 -0
  144. danielutils/mock_/__init__.py +1 -0
  145. danielutils/mock_/mock_module.py +27 -0
  146. danielutils/path.py +55 -0
  147. danielutils/print_.py +123 -0
  148. danielutils/progress_bar/__init__.py +3 -0
  149. danielutils/progress_bar/ascii_progress_bar.py +161 -0
  150. danielutils/progress_bar/progress_bar.py +75 -0
  151. danielutils/progress_bar/progress_bar_pool.py +72 -0
  152. danielutils/protocols/__init__.py +3 -0
  153. danielutils/protocols/dictable.py +38 -0
  154. danielutils/protocols/evaluable.py +26 -0
  155. danielutils/protocols/serializable.py +51 -0
  156. danielutils/py.typed +0 -0
  157. danielutils/random_.py +47 -0
  158. danielutils/reflection/__init__.py +6 -0
  159. danielutils/reflection/declarations.py +288 -0
  160. danielutils/reflection/file/__init__.py +1 -0
  161. danielutils/reflection/file/file_reflection.py +85 -0
  162. danielutils/reflection/function/__init__.py +1 -0
  163. danielutils/reflection/function/function_reflections.py +152 -0
  164. danielutils/reflection/info_classes/__init__.py +6 -0
  165. danielutils/reflection/info_classes/argument_info.py +278 -0
  166. danielutils/reflection/info_classes/class_info.py +204 -0
  167. danielutils/reflection/info_classes/decorator_info.py +54 -0
  168. danielutils/reflection/info_classes/file_info.py +1180 -0
  169. danielutils/reflection/info_classes/folder_info.py +592 -0
  170. danielutils/reflection/info_classes/function_info.py +551 -0
  171. danielutils/reflection/info_classes/import_info.py +207 -0
  172. danielutils/reflection/interpreter/__init__.py +9 -0
  173. danielutils/reflection/interpreter/callstack.py +57 -0
  174. danielutils/reflection/interpreter/get_traceback.py +27 -0
  175. danielutils/reflection/interpreter/interpreter.py +41 -0
  176. danielutils/reflection/interpreter/is_debugging.py +13 -0
  177. danielutils/reflection/interpreter/os_.py +33 -0
  178. danielutils/reflection/interpreter/packages.py +33 -0
  179. danielutils/reflection/interpreter/python_version.py +33 -0
  180. danielutils/reflection/interpreter/signals.py +18 -0
  181. danielutils/reflection/interpreter/tracer.py +175 -0
  182. danielutils/reflection/module/__init__.py +3 -0
  183. danielutils/reflection/module/lazy_module.py +104 -0
  184. danielutils/reflection/module/module_reflections.py +34 -0
  185. danielutils/reflection/module/package_reflection.py +161 -0
  186. danielutils/retry_executor/__init__.py +3 -0
  187. danielutils/retry_executor/backoff_strategies/__init__.py +6 -0
  188. danielutils/retry_executor/backoff_strategies/constant_backoff.py +25 -0
  189. danielutils/retry_executor/backoff_strategies/exponential_backoff.py +26 -0
  190. danielutils/retry_executor/backoff_strategies/functional_backoff.py +29 -0
  191. danielutils/retry_executor/backoff_strategies/linear_backoff.py +23 -0
  192. danielutils/retry_executor/backoff_strategies/multiplicative_backoff.py +23 -0
  193. danielutils/retry_executor/backoff_strategies/no_backoff.py +17 -0
  194. danielutils/retry_executor/backoff_strategy.py +28 -0
  195. danielutils/retry_executor/retry_executor.py +55 -0
  196. danielutils/snippets/__init__.py +1 -0
  197. danielutils/snippets/try_get.py +28 -0
  198. danielutils/system/__init__.py +3 -0
  199. danielutils/system/independent.py +197 -0
  200. danielutils/system/layered_command.py +130 -0
  201. danielutils/system/windows/__init__.py +2 -0
  202. danielutils/system/windows/utils/__init__.py +1 -0
  203. danielutils/system/windows/utils/filetime.py +19 -0
  204. danielutils/system/windows/win32_ctime.py +183 -0
  205. danielutils/system/windows/windows.py +78 -0
  206. danielutils/testing/__init__.py +1 -0
  207. danielutils/testing/unittest_/__init__.py +2 -0
  208. danielutils/testing/unittest_/always_teardown_testcase.py +75 -0
  209. danielutils/testing/unittest_/auto_cwd_testcase.py +114 -0
  210. danielutils/text.py +169 -0
  211. danielutils/time.py +64 -0
  212. danielutils/tools/__init__.py +1 -0
  213. danielutils/tools/unittest_test_runner/__init__.py +9 -0
  214. danielutils/tools/unittest_test_runner/cli.py +70 -0
  215. danielutils/tools/unittest_test_runner/core/__init__.py +17 -0
  216. danielutils/tools/unittest_test_runner/core/discovery.py +322 -0
  217. danielutils/tools/unittest_test_runner/core/execution.py +244 -0
  218. danielutils/tools/unittest_test_runner/core/parser.py +163 -0
  219. danielutils/tools/unittest_test_runner/core/runner.py +561 -0
  220. danielutils/tools/unittest_test_runner/models.py +82 -0
  221. danielutils/university/__init__.py +7 -0
  222. danielutils/university/computability_and_complexity/__init__.py +2 -0
  223. danielutils/university/computability_and_complexity/discreate_finite_automaton.py +89 -0
  224. danielutils/university/computability_and_complexity/languages/__init__.py +0 -0
  225. danielutils/university/computability_and_complexity/languages/language.py +17 -0
  226. danielutils/university/computability_and_complexity/languages/sat.py +71 -0
  227. danielutils/university/computability_and_complexity/turing_machine.py +47 -0
  228. danielutils/university/databases/__init__.py +1 -0
  229. danielutils/university/databases/all.py +756 -0
  230. danielutils/university/image_proccesing/__init__.py +1 -0
  231. danielutils/university/image_proccesing/encoding/__init__.py +3 -0
  232. danielutils/university/image_proccesing/encoding/encoding.py +35 -0
  233. danielutils/university/image_proccesing/encoding/lossless/__init__.py +4 -0
  234. danielutils/university/image_proccesing/encoding/lossless/huffman.py +0 -0
  235. danielutils/university/image_proccesing/encoding/lossless/lossless_encoding.py +10 -0
  236. danielutils/university/image_proccesing/encoding/lossless/lzw.py +0 -0
  237. danielutils/university/image_proccesing/encoding/lossless/run_length.py +22 -0
  238. danielutils/university/image_proccesing/encoding/lossy/__init__.py +1 -0
  239. danielutils/university/image_proccesing/encoding/lossy/lossy_encoding.py +10 -0
  240. danielutils/university/image_proccesing/tansformations/__init__.py +0 -0
  241. danielutils/university/image_proccesing/tansformations/gaussian.py +0 -0
  242. danielutils/university/image_proccesing/tansformations/gradient.py +0 -0
  243. danielutils/university/image_proccesing/tansformations/hough.py +0 -0
  244. danielutils/university/image_proccesing/tansformations/laplacian.py +0 -0
  245. danielutils/university/image_proccesing/tansformations/transformation.py +11 -0
  246. danielutils/university/linear_algebra/__init__.py +1 -0
  247. danielutils/university/linear_algebra/matrix.py +404 -0
  248. danielutils/university/machine_learning/__init__.py +0 -0
  249. danielutils/university/machine_learning/activation_functions/__init__.py +2 -0
  250. danielutils/university/machine_learning/activation_functions/activation_function.py +11 -0
  251. danielutils/university/machine_learning/activation_functions/relu.py +11 -0
  252. danielutils/university/machine_learning/neuron.py +15 -0
  253. danielutils/university/oop/__init__.py +1 -0
  254. danielutils/university/oop/observer.py +31 -0
  255. danielutils/university/oop/strategy.py +9 -0
  256. danielutils/university/probability/__init__.py +7 -0
  257. danielutils/university/probability/conditional_variable/__init__.py +3 -0
  258. danielutils/university/probability/conditional_variable/conditional_variable.py +138 -0
  259. danielutils/university/probability/conditional_variable/continuous/__init__.py +0 -0
  260. danielutils/university/probability/conditional_variable/discrete/__init__.py +8 -0
  261. danielutils/university/probability/conditional_variable/discrete/bernoulli.py +38 -0
  262. danielutils/university/probability/conditional_variable/discrete/bernoulli_sum.py +31 -0
  263. danielutils/university/probability/conditional_variable/discrete/binomial.py +40 -0
  264. danielutils/university/probability/conditional_variable/discrete/conditional_from_discrete_probability_func.py +36 -0
  265. danielutils/university/probability/conditional_variable/discrete/discrete.py +42 -0
  266. danielutils/university/probability/conditional_variable/discrete/geometric.py +43 -0
  267. danielutils/university/probability/conditional_variable/discrete/poisson.py +32 -0
  268. danielutils/university/probability/conditional_variable/discrete/uniform.py +33 -0
  269. danielutils/university/probability/distributions.py +35 -0
  270. danielutils/university/probability/expressions/__init__.py +2 -0
  271. danielutils/university/probability/expressions/accumulation_expression.py +291 -0
  272. danielutils/university/probability/expressions/probability_expression.py +110 -0
  273. danielutils/university/probability/funcs/__init__.py +4 -0
  274. danielutils/university/probability/funcs/covariance.py +12 -0
  275. danielutils/university/probability/funcs/expected_value.py +41 -0
  276. danielutils/university/probability/funcs/probability_function.py +14 -0
  277. danielutils/university/probability/funcs/variance.py +17 -0
  278. danielutils/university/probability/operator.py +79 -0
  279. danielutils/university/probability/protocols.py +32 -0
  280. danielutils/university/probability/supp.py +101 -0
  281. danielutils/university/probability/transformation.py +44 -0
  282. danielutils/versioned_imports.py +31 -0
  283. danielutils-1.1.28.dist-info/METADATA +390 -0
  284. danielutils-1.1.28.dist-info/RECORD +287 -0
  285. danielutils-1.1.28.dist-info/WHEEL +5 -0
  286. danielutils-1.1.28.dist-info/licenses/LICENSE +21 -0
  287. danielutils-1.1.28.dist-info/top_level.txt +1 -0
@@ -0,0 +1,125 @@
1
+ import sys
2
+ import logging
3
+ from typing import Optional, Tuple, List
4
+
5
+ from .async_cmd import async_cmd
6
+ from ..logging_.utils import get_logger
7
+ logger = get_logger(__name__)
8
+
9
+
10
+ class AsyncLayeredCommand:
11
+ class_capture_stdout: bool = True
12
+ class_capture_stderr: bool = True
13
+ class_raise_on_fail: bool = False
14
+ class_verbose: bool = False
15
+ _class_prev_instance: Optional['AsyncLayeredCommand'] = None
16
+ _id: int = 0
17
+
18
+ @property
19
+ def prev(self):
20
+ return self._prev_instance
21
+
22
+ @prev.setter
23
+ def prev(self, value: Optional['AsyncLayeredCommand'] = None):
24
+ self._prev_instance = value
25
+
26
+ def __init__(
27
+ self,
28
+ command: Optional[str] = None,
29
+ *,
30
+ prev_instance: Optional['AsyncLayeredCommand'] = None,
31
+ instance_capture_stdout: Optional[bool] = None,
32
+ instance_capture_stderr: Optional[bool] = None,
33
+ instance_raise_on_fail: Optional[bool] = None,
34
+ instance_verbose: Optional[bool] = None
35
+ ):
36
+ logger.debug("Initializing AsyncLayeredCommand with command='%s', prev_instance=%s", command, prev_instance is not None)
37
+ self._command = command if command is not None else ""
38
+ self._instance_capture_stdout = instance_capture_stdout
39
+ self._instance_capture_stderr = instance_capture_stderr
40
+ self._instance_raise_on_fail = instance_raise_on_fail
41
+ self._instance_verbose = instance_verbose
42
+ self._prev_instance = prev_instance if prev_instance is not None else AsyncLayeredCommand._class_prev_instance
43
+ self._cur_class_prev_instance = AsyncLayeredCommand._class_prev_instance
44
+ AsyncLayeredCommand._class_prev_instance = self
45
+ self._has_entered: bool = False
46
+ logger.debug("AsyncLayeredCommand initialized successfully")
47
+
48
+ def __enter__(self):
49
+ logger.debug("Entering AsyncLayeredCommand context")
50
+ self._open()
51
+ return self
52
+
53
+ def __exit__(self, exc_type, exc_val, exc_tb):
54
+ logger.debug("Exiting AsyncLayeredCommand context, exc_type=%s", exc_type)
55
+ if self.prev is self._cur_class_prev_instance:
56
+ AsyncLayeredCommand._class_prev_instance = self.prev
57
+
58
+ def _open(self) -> None:
59
+ logger.debug("Opening AsyncLayeredCommand")
60
+ self._has_entered = True
61
+
62
+ def _build_command(self, *commands: str) -> str:
63
+ logger.debug("Building command with %d additional commands", len(commands))
64
+ res = ""
65
+ if self.prev is not None:
66
+ prev = self.prev._build_command()
67
+ res += f"{prev} & " if prev != "" else ""
68
+ if self._command != "":
69
+ result = res + " & ".join([self._command, *commands])
70
+ else:
71
+ result = res + " & ".join(commands)
72
+ logger.debug("Built command: %s", result)
73
+ return result
74
+
75
+ def _error(self, predicate: bool, command: str, code: int, command_verbose: Optional[bool]) -> None:
76
+ if predicate:
77
+ logger.error("Command '%s' failed with exit code %d", command, code)
78
+ verbose = self._merge_values(command_verbose, self._instance_verbose, AsyncLayeredCommand.class_verbose)
79
+ if verbose:
80
+ raise RuntimeError(f"command '{command}' failed with exit code {code}")
81
+ sys.exit(1)
82
+
83
+ @staticmethod
84
+ def _merge_values(command: Optional[bool], instance: Optional[bool], class_: bool) -> bool:
85
+ return command if command is not None else (instance if instance is not None else class_)
86
+
87
+ async def execute(
88
+ self,
89
+ *commands: str,
90
+ command_capture_stdout: Optional[bool] = None,
91
+ command_capture_stderr: Optional[bool] = None,
92
+ command_raise_on_fail: Optional[bool] = None,
93
+ command_verbose: Optional[bool] = None
94
+ ) -> Tuple[int, List[str], List[str]]:
95
+ logger.info("Executing AsyncLayeredCommand with %d commands", len(commands))
96
+ if not self._has_entered:
97
+ logger.error("LayeredCommand must be used with a context manager")
98
+ raise RuntimeError(
99
+ "LayeredCommand must be used with a context manager. Use as: `with LayeredCommand(...) as l1:`")
100
+ capture_stdout = self._merge_values(command_capture_stdout, self._instance_capture_stdout,
101
+ self.class_capture_stdout)
102
+ capture_stderr = self._merge_values(command_capture_stderr, self._instance_capture_stderr,
103
+ self.class_capture_stderr)
104
+ raise_on_fail = self._merge_values(command_raise_on_fail, self._instance_raise_on_fail,
105
+ self.class_raise_on_fail)
106
+
107
+ command = self._build_command(*commands)
108
+ logger.debug("Executing command with capture_stdout=%s, capture_stderr=%s", capture_stdout, capture_stderr)
109
+ if not capture_stdout and not capture_stderr:
110
+ code = (await async_cmd(command))[0]
111
+ logger.debug("Command executed with return code %d", code)
112
+ self._error(raise_on_fail and code != 0, command, code, command_verbose)
113
+ return code, [], []
114
+
115
+ code, stdout, stderr = await async_cmd(command, capture_stdout=True, capture_stderr=True)
116
+ logger.info("Command execution completed with return code %d", code)
117
+ return code, stdout.decode().splitlines() if stdout else [], stderr.decode().splitlines() if stderr else []
118
+
119
+ async def __call__(self, *args, **kwargs) -> Tuple[int, List[str], List[str]]:
120
+ return await self.execute(*args, **kwargs)
121
+
122
+
123
+ __all__ = [
124
+ 'AsyncLayeredCommand'
125
+ ]
@@ -0,0 +1,86 @@
1
+ import asyncio
2
+ import functools
3
+ import logging
4
+ from datetime import datetime
5
+ from typing import Literal, Optional, Any, Mapping, Iterable, Callable, Coroutine
6
+
7
+ from ..custom_types import Supplier
8
+ from ..decorators import normalize_decorator
9
+ from .time_strategy import LinearTimeStrategy, ConstantTimeStrategy
10
+ from ..versioned_imports import ParamSpec
11
+ from ..logging_.utils import get_logger
12
+
13
+ P = ParamSpec("P")
14
+ logger = get_logger(__name__)
15
+
16
+
17
+ class AsyncRetryExecutor:
18
+ def __init__(
19
+ self,
20
+ timeout_strategy: Supplier[float] = LinearTimeStrategy(30, 5),
21
+ delay_strategy: Supplier[float] = ConstantTimeStrategy(0)
22
+ ) -> None:
23
+ logger.info("Initializing AsyncRetryExecutor with timeout_strategy=%s, delay_strategy=%s", type(timeout_strategy).__name__, type(delay_strategy).__name__)
24
+ self.timeout_strategy = timeout_strategy
25
+ self.delay_strategy = delay_strategy
26
+ logger.debug("AsyncRetryExecutor initialized successfully")
27
+
28
+ def is_transient(self, e: Exception) -> bool:
29
+ """
30
+ This function will return true if the exception that was raised at a specific attempt should be ignored and we should try again with respet to the amount of retries left
31
+ Args:
32
+ e: exception caught
33
+
34
+ Returns:
35
+ boolean
36
+ """
37
+ return False
38
+
39
+ async def execute(
40
+ self,
41
+ func: Callable[P, Coroutine],
42
+ *,
43
+ args: Optional[Iterable] = None,
44
+ kwargs: Optional[Mapping] = None,
45
+ max_tries: int = 5
46
+ ) -> Optional[Any]:
47
+ args = list(args) if args else []
48
+ kwargs = dict(kwargs) if kwargs else {}
49
+ logger.info("Starting async retry execution for %s with max_tries=%d", func.__name__, max_tries)
50
+
51
+ for i in range(1, max_tries + 1):
52
+ timeout = self.timeout_strategy()
53
+ delay = self.delay_strategy()
54
+ logger.debug("Attempt %d/%d with timeout=%ss, delay=%ss", i, max_tries, timeout, delay)
55
+ try:
56
+ result = await asyncio.wait_for(func(*args, **kwargs), timeout=timeout)
57
+ logger.info("Async retry execution succeeded on attempt %d", i)
58
+ return result
59
+ except Exception as e:
60
+ if self.is_transient(e):
61
+ logger.warning("Failed attempt %d/%d with transient exception: %s: %s", i, max_tries, type(e).__name__, e)
62
+ if i < max_tries - 1 and delay > 0:
63
+ logger.debug("Waiting %ss before next attempt", delay)
64
+ await asyncio.sleep(delay)
65
+ else:
66
+ logger.error("Non-transient exception on attempt %d: %s: %s", i, type(e).__name__, e)
67
+ raise e
68
+ logger.error("Failed all %d attempts for %s", max_tries, func.__name__)
69
+ raise RuntimeError(f"Failed all attempts")
70
+
71
+
72
+ @normalize_decorator
73
+ def with_async_retry(func, *retry_executor_args, max_tries: int = 5, **retry_executor_kwargs):
74
+ retry_executor = AsyncRetryExecutor(*retry_executor_args, **retry_executor_kwargs)
75
+
76
+ @functools.wraps(func)
77
+ async def wrapper(*args, **kwargs):
78
+ return await retry_executor.execute(func, args=args, kwargs=kwargs, max_tries=max_tries)
79
+
80
+ return wrapper
81
+
82
+
83
+ __all__ = [
84
+ "AsyncRetryExecutor",
85
+ "with_async_retry"
86
+ ]
@@ -0,0 +1,110 @@
1
+ import asyncio
2
+ import json
3
+ import logging
4
+ from collections import defaultdict
5
+ from typing import Callable, Optional, Coroutine, List, Iterable, Any, Mapping, Tuple
6
+
7
+ try:
8
+ from tqdm import tqdm
9
+ except ImportError:
10
+ from ..mock_ import MockImportObject
11
+
12
+ tqdm = MockImportObject("'tqdm' is not installed. Please install 'tqdm' to use AsyncWorkerPool feature.") # type: ignore
13
+
14
+ from ..logging_.utils import get_logger
15
+
16
+ class AsyncWorkerPool:
17
+ DEFAULT_ORDER_IF_KEY_EXISTS = (
18
+ "pool", "timestamp", "worker_id", "task_id", "task_name", "num_tasks", "tasks", "level", "message", "exception"
19
+ )
20
+
21
+ # Default logger instance
22
+ _logger = get_logger(__name__)
23
+
24
+ @staticmethod
25
+ def log(level: int, message: str, *args, **kwargs) -> None:
26
+ """
27
+ Static logging method that delegates to the default logger.
28
+ End users can override this method to customize logging behavior.
29
+
30
+ Args:
31
+ level: Log level (DEBUG=10, INFO=20, WARNING=30, ERROR=40, CRITICAL=50)
32
+ message: Log message
33
+ *args: Additional arguments for message formatting
34
+ **kwargs: Additional keyword arguments for logging
35
+ """
36
+ AsyncWorkerPool._logger.log(level, message, *args, **kwargs)
37
+
38
+ def __init__(self, pool_name: str, num_workers: int = 5, show_pbar: bool = False) -> None:
39
+ self.log(logging.INFO, "Initializing AsyncWorkerPool '%s' with %d workers, show_pbar=%s", pool_name, num_workers, show_pbar)
40
+ self._num_workers: int = num_workers
41
+ self._pool_name: str = pool_name
42
+ self._show_pbar: bool = show_pbar
43
+ self._pbar: Optional[tqdm] = None
44
+ self._queue: asyncio.Queue[
45
+ Optional[Tuple[Callable, Iterable[Any], Mapping[Any, Any], Optional[str]]]] = asyncio.Queue()
46
+ self._workers: List = []
47
+ self.log(logging.DEBUG, "AsyncWorkerPool '%s' initialized successfully", pool_name)
48
+
49
+ async def worker(self, worker_id) -> None:
50
+ """Worker coroutine that continuously fetches and executes tasks from the queue."""
51
+ self.log(logging.DEBUG, "Worker %d starting", worker_id)
52
+ task_index = 0
53
+ tasks = defaultdict(list)
54
+ while True:
55
+ task = await self._queue.get()
56
+ if task is None: # Sentinel value to shut down the worker
57
+ self.log(logging.DEBUG, "Worker %d received shutdown signal", worker_id)
58
+ break
59
+ func, args, kwargs, name = task
60
+ task_index += 1
61
+ self.log(logging.INFO, "Task %d '%s' started on worker %d", task_index, name, worker_id)
62
+ try:
63
+ await func(*args, **kwargs)
64
+ tasks["success"].append(name)
65
+ self.log(logging.INFO, "Task %d '%s' finished on worker %d", task_index, name, worker_id)
66
+ except Exception as e:
67
+ self.log(logging.ERROR, "Task %d '%s' failed on worker %d: %s: %s", task_index, name, worker_id, type(e).__name__, e)
68
+ tasks["failure"].append(name)
69
+
70
+ if self._pbar:
71
+ self._pbar.update(1)
72
+ self._queue.task_done()
73
+ self.log(logging.INFO, "Worker %d completed %d tasks (success: %d, failure: %d)", worker_id, task_index, len(tasks['success']), len(tasks['failure']))
74
+ if tasks['success']:
75
+ self.log(logging.INFO, "Worker %d successful tasks: [%s]", worker_id, ', '.join(map(str, tasks['success'])))
76
+ if tasks['failure']:
77
+ self.log(logging.WARNING, "Worker %d failed tasks: [%s]", worker_id, ', '.join(map(str, tasks['failure'])))
78
+
79
+ async def start(self) -> None:
80
+ """Starts the worker pool."""
81
+ self.log(logging.INFO, "Starting worker pool '%s' with %d workers", self._pool_name, self._num_workers)
82
+ if self._show_pbar:
83
+ self._pbar = tqdm(total=self._queue.qsize(), desc="#Tasks")
84
+ self._workers = [asyncio.create_task(self.worker(i + 1)) for i in range(self._num_workers)]
85
+ self.log(logging.INFO, "Worker pool '%s' started successfully", self._pool_name)
86
+
87
+ async def submit(
88
+ self,
89
+ func: Callable[..., Coroutine[None, None, None]],
90
+ args: Optional[Iterable[Any]] = None,
91
+ kwargs: Optional[Mapping[Any, Any]] = None,
92
+ name: Optional[str] = None
93
+ ) -> None:
94
+ """Submit a new task to the queue."""
95
+ self.log(logging.DEBUG, "Adding new job '%s' to queue", name)
96
+ await self._queue.put((func, args or (), kwargs or {}, name))
97
+
98
+ async def join(self) -> None:
99
+ """Stops the worker pool by waiting for all tasks to complete and shutting down workers."""
100
+ self.log(logging.INFO, "Starting join process for worker pool '%s'", self._pool_name)
101
+ await self._queue.join() # Wait until all tasks are processed
102
+ for _ in range(self._num_workers):
103
+ await self._queue.put(None) # Send sentinel values to stop workers
104
+ await asyncio.gather(*self._workers) # Wait for workers to finish
105
+ self.log(logging.INFO, "Join process completed for worker pool '%s'", self._pool_name)
106
+
107
+
108
+ __all__ = [
109
+ "AsyncWorkerPool",
110
+ ]
@@ -0,0 +1,73 @@
1
+ from abc import ABC, abstractmethod
2
+ import logging
3
+ from ..logging_.utils import get_logger
4
+ logger = get_logger(__name__)
5
+
6
+
7
+ class TimeStrategy(ABC):
8
+ @abstractmethod
9
+ def next(self): ...
10
+
11
+ def __call__(self, *args, **kwargs):
12
+ return self.next()
13
+
14
+ @abstractmethod
15
+ def reset(self): ...
16
+
17
+
18
+ class ConstantTimeStrategy(TimeStrategy):
19
+ def __init__(self, timeout: float):
20
+ logger.debug("Initializing ConstantTimeStrategy with timeout=%s", timeout)
21
+ self.timeout = timeout
22
+
23
+ def next(self) -> float:
24
+ logger.debug("ConstantTimeStrategy returning timeout=%s", self.timeout)
25
+ return self.timeout
26
+
27
+ def reset(self) -> None:
28
+ logger.debug("ConstantTimeStrategy reset called (no-op)")
29
+ pass # No state to reset
30
+
31
+
32
+ class LinearTimeStrategy(TimeStrategy):
33
+ def __init__(self, base_timeout: float, step: float):
34
+ logger.debug("Initializing LinearTimeStrategy with base_timeout=%s, step=%s", base_timeout, step)
35
+ self.base_timeout = base_timeout
36
+ self.step = step
37
+ self.current_timeout = base_timeout
38
+
39
+ def next(self) -> float:
40
+ timeout = self.current_timeout
41
+ self.current_timeout += self.step
42
+ logger.debug("LinearTimeStrategy returning timeout=%s, next will be %s", timeout, self.current_timeout)
43
+ return timeout
44
+
45
+ def reset(self) -> None:
46
+ logger.debug("LinearTimeStrategy resetting to base_timeout=%s", self.base_timeout)
47
+ self.current_timeout = self.base_timeout
48
+
49
+
50
+ class MultiplicativeTimeStrategy(TimeStrategy):
51
+ def __init__(self, base_timeout: float, factor: float):
52
+ logger.debug("Initializing MultiplicativeTimeStrategy with base_timeout=%s, factor=%s", base_timeout, factor)
53
+ self.base_timeout = base_timeout
54
+ self.factor = factor
55
+ self.current_timeout = base_timeout
56
+
57
+ def next(self) -> float:
58
+ timeout = self.current_timeout
59
+ self.current_timeout *= self.factor
60
+ logger.debug("MultiplicativeTimeStrategy returning timeout=%s, next will be %s", timeout, self.current_timeout)
61
+ return timeout
62
+
63
+ def reset(self) -> None:
64
+ logger.debug("MultiplicativeTimeStrategy resetting to base_timeout=%s", self.base_timeout)
65
+ self.current_timeout = self.base_timeout
66
+
67
+
68
+ __all__ = [
69
+ "TimeStrategy",
70
+ "ConstantTimeStrategy",
71
+ "LinearTimeStrategy",
72
+ "MultiplicativeTimeStrategy"
73
+ ]
@@ -0,0 +1,57 @@
1
+ import asyncio
2
+ import logging
3
+ from asyncio import Task
4
+ from typing import List, Coroutine, Any, Tuple, Optional, Set, AsyncIterator, Iterator, TypeVar
5
+ from ..logging_.utils import get_logger
6
+ logger = get_logger(__name__)
7
+
8
+
9
+ async def return_first(coros: List[Coroutine], timeout: Optional[int] = None) -> List[Tuple[int, Any]]:
10
+ logger.debug("return_first called with %d coroutines, timeout=%s", len(coros), timeout)
11
+ tasks: List[Task] = [asyncio.create_task(coro) for coro in coros]
12
+ result: Tuple[Set[Task], Set[Task]] = await asyncio.wait(tasks, timeout=timeout,
13
+ return_when=asyncio.FIRST_COMPLETED)
14
+ done: Set[Task] = result[0]
15
+ # pending: Set[Task] = result[1]
16
+
17
+ res = []
18
+ for task in done:
19
+ res.append((tasks.index(task), task.result()))
20
+
21
+ logger.debug("return_first completed with %d results", len(res))
22
+ return res
23
+
24
+
25
+ async def return_all(coros: List[Coroutine], timeout: Optional[int] = None) -> List[Any]:
26
+ logger.debug("return_all called with %d coroutines, timeout=%s", len(coros), timeout)
27
+ tasks: List[Task] = [asyncio.create_task(coro) for coro in coros]
28
+ result: Tuple[Set[Task], Set[Task]] = await asyncio.wait(tasks, timeout=timeout,
29
+ return_when=asyncio.ALL_COMPLETED)
30
+ done: Set[Task] = result[0]
31
+
32
+ res = [task.result() for task in done]
33
+ logger.debug("return_all completed with %d results", len(res))
34
+ return res
35
+
36
+
37
+ async def cast_aiter(itr: Iterator) -> AsyncIterator:
38
+ logger.debug("cast_aiter called")
39
+ for x in itr:
40
+ yield x
41
+
42
+
43
+ T = TypeVar("T")
44
+
45
+
46
+ async def async_enumerate(iterable: AsyncIterator[T], start: int = 0) -> AsyncIterator[Tuple[int, T]]:
47
+ index = start
48
+ async for item in iterable:
49
+ yield index, item
50
+ index += 1
51
+
52
+
53
+ __all__ = [
54
+ "return_first",
55
+ "return_all",
56
+ 'cast_aiter'
57
+ ]
@@ -0,0 +1,3 @@
1
+ from .typed_builtins import *
2
+ from .frange import *
3
+ from .counter import *
@@ -0,0 +1,48 @@
1
+ from typing import Union
2
+ from ..metaclasses import AtomicClassMeta
3
+
4
+
5
+ class Counter:
6
+ """A simple counter class
7
+ """
8
+
9
+ def __init__(self, initial_value: Union[int, float] = 0, increment_amount: Union[int, float] = 1) -> None:
10
+ self.value = initial_value
11
+ self.increment_value = increment_amount
12
+
13
+ def increment(self) -> None:
14
+ """increments the stored value by the increment amount
15
+ """
16
+ self.value += self.increment_value
17
+
18
+ def decrement(self) -> None:
19
+ """decrements the stored value by the increment amount
20
+ """
21
+ self.value -= self.increment_value
22
+
23
+ def get(self) -> Union[int, float]:
24
+ """returns the current value of the counter
25
+
26
+ Returns:
27
+ Union[int, float]: value
28
+ """
29
+ return self.value
30
+
31
+ def set(self, value: Union[int, float]):
32
+ """sets the values of the counter
33
+
34
+ Args:
35
+ value (Union[int, float]): value to set
36
+ """
37
+ self.value = value
38
+
39
+
40
+ class AtomicCounter(Counter, metaclass=AtomicClassMeta): # type:ignore
41
+ """A Counter Class which is Atomic
42
+ """
43
+
44
+
45
+ __all__ = [
46
+ "Counter",
47
+ "AtomicCounter"
48
+ ]