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,98 @@
1
+ from queue import Queue
2
+ import logging
3
+ from typing import Optional, Any, Type as t_type, Tuple as Tuple, List as List
4
+ from threading import Semaphore
5
+ from .worker import Worker
6
+ from ...reflection import get_python_version
7
+ from ...logging_.utils import get_logger
8
+
9
+ if get_python_version() >= (3, 9):
10
+ from builtins import type as t_type, tuple as Tuple, list as List # type:ignore
11
+ logger = get_logger(__name__)
12
+
13
+
14
+ class WorkerPool:
15
+ """A worker pool class
16
+ """
17
+
18
+ def __init__(self, num_workers: int, worker_class: t_type[Worker], w_kwargs: dict, global_variables: dict) -> None:
19
+ logger.info("Initializing WorkerPool with %s workers, worker_class=%s", num_workers, worker_class.__name__)
20
+ self.num_workers = num_workers
21
+ self.global_variables: dict = global_variables
22
+ self.q: Queue[Tuple[Any]] = Queue()
23
+ self.worker_class = worker_class
24
+ self.workers: List[Worker] = []
25
+ self.sem = Semaphore(0)
26
+ self.w_kwargs = w_kwargs
27
+ logger.debug("WorkerPool initialized: num_workers=%s, global_vars_count=%s, worker_kwargs_count=%s", num_workers, len(global_variables), len(w_kwargs))
28
+
29
+ def submit(self, job: Any) -> None:
30
+ """submit a job to the pool
31
+ the object can be anything you want as long as you use it
32
+ correctly in your implemented worker class
33
+
34
+ Args:
35
+ job (Any): job object
36
+ """
37
+ logger.debug("Submitting job to pool: job_type=%s", type(job).__name__)
38
+ # we create a tuple to signal that it is indeed a job object and we haven't just got None
39
+ # see Worker._loop
40
+ self.q.put((job,))
41
+ self.sem.release()
42
+ logger.debug("Job submitted successfully, queue_size=%s, semaphore_value=%s", self.q.qsize(), self.sem._value)
43
+
44
+ def _acquire(self) -> Optional[Tuple[Any]]:
45
+ """acquire a new job from the pool
46
+
47
+ Returns:
48
+ Optional[tuple[Any]]: optional tuple of job object
49
+ """
50
+ logger.debug("Acquiring job from pool")
51
+ self.sem.acquire()
52
+ if self.q.unfinished_tasks > 0:
53
+ job = self.q.get()
54
+ logger.debug("Job acquired successfully, remaining_tasks=%s", self.q.unfinished_tasks)
55
+ return job
56
+ logger.debug("No jobs available in pool")
57
+ return None
58
+
59
+ def start(self) -> None:
60
+ """starts running the pool of workers
61
+ """
62
+ logger.info("Starting worker pool with %s workers", self.num_workers)
63
+ for i in range(self.num_workers):
64
+ logger.debug("Creating worker %s/%s", i+1, self.num_workers)
65
+ w = self.worker_class(i, self, **self.w_kwargs)
66
+ w.run()
67
+ self.workers.append(w)
68
+ logger.debug("Worker %s started successfully", i+1)
69
+ logger.info("Worker pool started with %s workers", len(self.workers))
70
+
71
+ def _notify(self) -> None:
72
+ """a function that the worker calls after finishing processing a job object (Any)
73
+ this function is called automatically from Worker.work()
74
+ """
75
+ logger.debug("Worker notification received, unfinished_tasks=%s", self.q.unfinished_tasks)
76
+ self.q.task_done()
77
+ if self.q.unfinished_tasks <= 0:
78
+ logger.debug("All tasks completed, releasing all workers")
79
+ self.sem.release(self.num_workers)
80
+ logger.debug("Notification processed, remaining_tasks=%s", self.q.unfinished_tasks)
81
+
82
+ def join(self) -> None:
83
+ """
84
+ waits for all the workers to finish and will return afterwards
85
+ Returns:
86
+ None
87
+ """
88
+ logger.info("Joining worker pool with %s workers", len(self.workers))
89
+ for i, w in enumerate(self.workers):
90
+ logger.debug("Joining worker %s/%s", i+1, len(self.workers))
91
+ w.thread.join()
92
+ logger.debug("Worker %s joined successfully", i+1)
93
+ logger.info("All workers joined successfully")
94
+
95
+
96
+ __all__ = [
97
+ "WorkerPool"
98
+ ]
@@ -0,0 +1,125 @@
1
+ import copy, re
2
+ import logging
3
+ from typing import Any, Callable, Union, Tuple as Tuple, List as List, Dict as Dict
4
+ from ..reflection import get_python_version # pylint :disable=relative-beyond-top-level
5
+ from ..logging_.utils import get_logger
6
+
7
+ if get_python_version() >= (3, 9):
8
+ from builtins import tuple as Tuple, list as List, dict as Dict # type:ignore
9
+ logger = get_logger(__name__)
10
+
11
+
12
+ class Argument:
13
+ """a class to wrap an argument"""
14
+
15
+ def __init__(self, name: str, optional: bool = False, flag: bool = False) -> None:
16
+ logger.debug("Creating Argument: name=%s, optional=%s, flag=%s", name, optional, flag)
17
+ self.name = name
18
+ self.optional = optional
19
+ self.flag = flag
20
+ logger.debug("Argument created successfully: %s", name)
21
+
22
+
23
+ class Command:
24
+ """a class to wrap a command
25
+ """
26
+
27
+ def __init__(self, command: Union[Argument, str], callback: Callable,
28
+ explanation: str = "", *, options: Tuple[Argument, ...] = tuple()) -> None:
29
+ logger.debug("Creating Command: command=%s, explanation_length=%d, options_count=%d", command if isinstance(command, str) else command.name, len(explanation), len(options))
30
+ self.command = command if isinstance(
31
+ command, Argument) else Argument(command)
32
+ self.callback = callback
33
+ self.explanation = explanation
34
+ self.options = options
35
+ logger.debug("Command created successfully: %s", self.command.name)
36
+
37
+ def __call__(self, *args: Any, **kwargs: Any) -> Any:
38
+ logger.debug("Command '%s' called with args_count=%d, kwargs_count=%d", self.command.name, len(args), len(kwargs))
39
+ if len(args) > 0:
40
+ if args[0] == "help":
41
+ logger.info("Help requested for command: %s", self.command.name)
42
+ if self.explanation != "":
43
+ print(self.explanation)
44
+ logger.debug("Help explanation displayed for command: %s", self.command.name)
45
+ return None
46
+ try:
47
+ result = self.callback(*args, **kwargs)
48
+ logger.debug("Command '%s' executed successfully", self.command.name)
49
+ return result
50
+ except Exception as e:
51
+ logger.error("Command '%s' failed with %s: %s", self.command.name, type(e).__name__, e)
52
+ raise
53
+
54
+
55
+ class REPL:
56
+ """a class to easily create a shell application and get functionality for free
57
+ """
58
+
59
+ # pylint: disable=dangerous-default-value
60
+ def __init__(self, routes: List[Command], *, prompt_symbol: str = ">>> ", exit_keywords: set = {"exit", "quit"}):
61
+ logger.info("Initializing REPL with %d commands, prompt='%s', exit_keywords=%s", len(routes), prompt_symbol, exit_keywords)
62
+ self.prompt_symbol = prompt_symbol
63
+ self.exit_keywords = copy.copy(exit_keywords)
64
+ self.routes: Dict[str, Command] = {
65
+ com.command.name: com for com in routes}
66
+ logger.debug("REPL initialized successfully with commands: %s", list(self.routes.keys()))
67
+
68
+ def run(self) -> None:
69
+ """runs the main loop for the shell
70
+
71
+ Raises:
72
+ e: any error if there is any
73
+ """
74
+ logger.info("Starting REPL main loop")
75
+ while True:
76
+ prompt = input(self.prompt_symbol)
77
+ logger.debug("User input received: '%s'", prompt)
78
+
79
+ if prompt in self.exit_keywords:
80
+ logger.info("Exit keyword '%s' received, stopping REPL", prompt)
81
+ break
82
+
83
+ if prompt == "help":
84
+ logger.info("Help command requested")
85
+ print("Available commands:")
86
+ for com in list(self.routes.keys()) + list(self.exit_keywords):
87
+ print(f"\t{com}")
88
+ logger.debug("Help displayed with %d commands", len(self.routes) + len(self.exit_keywords))
89
+ continue
90
+
91
+ prompt_parts = prompt.split()
92
+ command = prompt_parts[0]
93
+ logger.debug("Processing command: '%s' with %d arguments", command, len(prompt_parts)-1)
94
+
95
+ if command in self.routes:
96
+ try:
97
+ logger.debug("Executing command: %s", command)
98
+ self.routes[command](*prompt_parts[1:])
99
+ logger.debug("Command '%s' completed successfully", command)
100
+ except TypeError as e:
101
+ msg = str(e)
102
+ logger.warning("TypeError in command '%s': %s", command, msg)
103
+ if re.match(r".*missing.*required.*argument.*", msg):
104
+ print(f"'{command}' " + msg[msg.find("missing"):])
105
+ elif re.match(r".*takes.*arguments but.*given", msg):
106
+ print(f"'{command}' " + msg[msg.find("takes"):])
107
+ else:
108
+ logger.error("Unhandled TypeError in command '%s': %s", command, e)
109
+ raise e
110
+ except Exception as e:
111
+ logger.error("Unexpected error in command '%s': %s: %s", command, type(e).__name__, e)
112
+ raise e
113
+ else:
114
+ logger.warning("Invalid command '%s' received", command)
115
+ print(
116
+ "Invalid command. for help type 'help'.\nOr additionally you may try a command and then 'help'")
117
+
118
+ logger.info("REPL main loop ended")
119
+
120
+
121
+ __all__ = [
122
+ "REPL",
123
+ "Command",
124
+ "Argument"
125
+ ]
danielutils/aliases.py ADDED
@@ -0,0 +1,174 @@
1
+ # import inspect
2
+ # from abc import ABC, abstractmethod
3
+ # from typing import cast, Generic, ParamSpec, TypeVar, Callable
4
+ # from .functions import isoftype
5
+ # from .reflection.Function import get_caller_name, get_function_return_type
6
+ # P = ParamSpec("P")
7
+ # A = TypeVar("A")
8
+ # B = TypeVar("B")
9
+
10
+
11
+ # class SubscribableBase(ABC):
12
+ # """An abstract class that need __instanceCheck__ to be implemented
13
+ # will create default functions such that isinstance will work with the new type
14
+ # while it is used as all of the python's generic types
15
+ # """
16
+ # __slots__ = ("params",)
17
+
18
+ # def __init__(self, *params):
19
+ # if get_caller_name() != "__class_getitem__":
20
+ # raise ValueError(
21
+ # f"{self.__class__.__name__} is a type-hint class, Cant instantiate.")
22
+ # self.params = params[0] if type(params) in {list, tuple} else params
23
+
24
+ # @classmethod
25
+ # def __class_getitem__(cls, *params) -> type:
26
+ # res: type = cast(type, cls(*params))
27
+ # return res
28
+
29
+ # @abstractmethod
30
+ # def __instancecheck__(self, value) -> bool: ...
31
+
32
+
33
+ # class Supplier(SubscribableBase):
34
+ # """create a type for functions to be a supplier function
35
+ # """
36
+
37
+ # def __instancecheck__(self, func) -> bool:
38
+ # if not callable(func):
39
+ # return False
40
+ # signature = inspect.signature(func)
41
+ # if len(signature.parameters) != 0:
42
+ # return False
43
+
44
+ # return_type = get_function_return_type(func)
45
+ # if return_type is not None:
46
+ # try:
47
+ # return isoftype(return_type(), self.params)
48
+ # except:
49
+ # return False
50
+ # return self.params is None
51
+
52
+
53
+ # class Consumer(SubscribableBase):
54
+ # """create a type for functions to be a consumer function
55
+ # """
56
+
57
+ # def __instancecheck__(self, func) -> bool:
58
+ # if not callable(func):
59
+ # return False
60
+ # if get_function_return_type(func) is not type(None):
61
+ # return False
62
+ # signature = inspect.signature(func)
63
+ # if len(signature.parameters) != 1:
64
+ # return False
65
+ # param_type = next(iter(signature.parameters.values())).annotation
66
+ # try:
67
+ # return isoftype(param_type(), self.params)
68
+ # except:
69
+ # return False
70
+
71
+
72
+ # class BinaryConsumer(SubscribableBase, Generic[A, B]):
73
+ # """create a type for functions to be a binary consumer function
74
+ # """
75
+
76
+ # def __instancecheck__(self, func) -> bool:
77
+ # if self.params[-1] is not None:
78
+ # return False
79
+ # self.params = self.params[0]
80
+ # if not callable(func):
81
+ # return False
82
+ # if get_function_return_type(func) is not type(None):
83
+ # return False
84
+ # signature = inspect.signature(func)
85
+ # if len(signature.parameters) != 2:
86
+ # return False
87
+ # param_iter = iter(signature.parameters.values())
88
+ # param_types = (next(param_iter).annotation(),
89
+ # next(param_iter).annotation())
90
+ # try:
91
+ # for value, value_type in zip(param_types, self.params):
92
+ # if not isoftype(value, value_type):
93
+ # return False
94
+ # return True
95
+ # except:
96
+ # return False
97
+
98
+ # # class Comparer(SubscribableBase):
99
+ # # def __instancecheck__(self, func) -> bool:
100
+ # # if not callable(func):
101
+ # # return False
102
+ # # if get_function_return_type(func) not in {int, float, Union[int, float]}:
103
+ # # return False
104
+ # # signature = inspect.signature(func)
105
+ # # if len(signature.parameters) != 2:
106
+ # # return False
107
+ # # param_iter = iter(signature.parameters.values())
108
+ # # param1_type = next(param_iter).annotation
109
+ # # param2_type = next(param_iter).annotation
110
+ # # try:
111
+ # # return param1_type == param2_type == self.params[0] == self.params[1]
112
+ # # except:
113
+ # # return False
114
+ # # class Predicate(SubscribableBase):
115
+ # # """create a type for functions to be a consumer function
116
+ # # """
117
+
118
+ # # def __instancecheck__(self, func) -> bool:
119
+ # # if not callable(func):
120
+ # # return False
121
+ # # signature = inspect.signature(func)
122
+ # # if len(signature.parameters) != 0:
123
+ # # return False
124
+
125
+ # # return_type = get_function_return_type(func)
126
+ # # try:
127
+ # # return isoftype(return_type(), self.params)
128
+ # # except:
129
+ # # return False
130
+
131
+
132
+ # # ListTupleType: TypeAlias = Union[list, tuple]
133
+ # __all__ = [
134
+ # "SubscribableBase",
135
+ # "Supplier",
136
+ # "Consumer",
137
+ # "BinaryConsumer",
138
+ # # "Predicate"
139
+ # # "ListTupleType"
140
+ # ]
141
+ from typing import Callable, TypeVar, Union
142
+
143
+ Number = Union[int, float, complex]
144
+
145
+ A = TypeVar("A")
146
+ B = TypeVar("B")
147
+ U = TypeVar("U")
148
+ T = TypeVar("T")
149
+ # P = ParamSpec("P")
150
+
151
+ Supplier = Callable[[], T]
152
+ Runnable = Supplier[None]
153
+ Consumer = Callable[[T], None]
154
+ UnaryFunction = Callable[[U], T]
155
+ UnaryOperator = Callable[[T], T]
156
+ Predicate = Callable[[T], bool]
157
+
158
+ BinaryFunction = Callable[[A, B], T]
159
+ BinaryOperator = Callable[[T, T], T]
160
+ Comparator = Callable[[A, B], int]
161
+ BinaryConsumer = Callable[[T, T], None]
162
+
163
+ __all__ = [
164
+ "UnaryFunction",
165
+ "BinaryFunction",
166
+ "UnaryOperator",
167
+ "BinaryOperator",
168
+ "Supplier",
169
+ "Predicate",
170
+ "Comparator",
171
+ "Consumer",
172
+ "BinaryConsumer",
173
+ "Runnable",
174
+ ]
@@ -0,0 +1,7 @@
1
+ from .async_cmd import *
2
+ from .async_command import *
3
+ from .async_layered_command import *
4
+ from .async_retry_executor import *
5
+ from .async_worker_pool import *
6
+ from .time_strategy import *
7
+ from .utils import *
@@ -0,0 +1,28 @@
1
+ import asyncio
2
+ import logging
3
+ from typing import Tuple, Optional
4
+ from ..logging_.utils import get_logger
5
+ logger = get_logger(__name__)
6
+
7
+
8
+ async def async_cmd(
9
+ cmd: str,
10
+ *,
11
+ capture_stdout: bool = False,
12
+ capture_stderr: bool = False
13
+ ) -> Tuple[int, Optional[bytes], Optional[bytes]]:
14
+ logger.debug("Executing async command: %s, capture_stdout=%s, capture_stderr=%s", cmd, capture_stdout, capture_stderr)
15
+ kwargs = {}
16
+ if capture_stdout:
17
+ kwargs['stdout'] = asyncio.subprocess.PIPE
18
+ if capture_stderr:
19
+ kwargs['stderr'] = asyncio.subprocess.PIPE
20
+ process = await asyncio.create_subprocess_shell(cmd, **kwargs) # type:ignore
21
+ stdout, stderr = await process.communicate()
22
+ logger.debug("Command completed with returncode=%s", process.returncode)
23
+ return process.returncode or 0, stdout, stderr
24
+
25
+
26
+ __all__ = [
27
+ 'async_cmd',
28
+ ]