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,104 @@
1
+ """
2
+ Lazy module loading functionality.
3
+
4
+ This module provides transparent lazy loading of optional modules,
5
+ allowing applications to start even when optional dependencies are missing.
6
+ The modules are only imported when actually used, providing clear error messages.
7
+ """
8
+
9
+ import sys
10
+ from typing import Any, Optional
11
+
12
+
13
+ class LazyModule:
14
+ """
15
+ A proxy for modules that should only be imported when actually used.
16
+
17
+ This allows code to run even when optional dependencies are missing,
18
+ but provides clear error messages when the functionality is actually needed.
19
+ """
20
+
21
+ def __init__(self, module_name: str, error_message: str, install_hint: str):
22
+ self._module_name = module_name
23
+ self._module: Optional[Any] = None
24
+ self._imported = False
25
+
26
+ if not error_message:
27
+ error_message = f"Module '{module_name}' is not available"
28
+
29
+ if install_hint and install_hint.strip():
30
+ error_message += f". {install_hint}"
31
+
32
+ self._error_message = error_message
33
+
34
+ # Set essential module attributes to make it look like a real module
35
+ self.__name__ = module_name
36
+ self.__file__ = None
37
+ self.__package__ = None
38
+ self.__path__ = None
39
+ self.__spec__ = None
40
+
41
+ def _ensure_imported(self):
42
+ """Import the module if not already imported."""
43
+ if not self._imported:
44
+ # Check if there's already a real module in sys.modules
45
+ existing_module = sys.modules.get(self._module_name)
46
+ if existing_module is not self and existing_module is not None:
47
+ # There's already a real module, use it
48
+ self._module = existing_module
49
+ self._imported = True
50
+ else:
51
+ # Try to import the real module
52
+ try:
53
+ # Temporarily remove ourselves from sys.modules to avoid recursion
54
+ if existing_module is self:
55
+ del sys.modules[self._module_name]
56
+
57
+ self._module = __import__(self._module_name)
58
+ self._imported = True
59
+
60
+ # Restore ourselves to sys.modules
61
+ sys.modules[self._module_name] = self
62
+ except ImportError:
63
+ # If import fails, raise the custom error message
64
+ raise ImportError(self._error_message)
65
+ return self._module
66
+
67
+ def __getattr__(self, name):
68
+ """Delegate attribute access to the actual module."""
69
+ # Try to import the module first
70
+ try:
71
+ module = self._ensure_imported()
72
+ return getattr(module, name)
73
+ except ImportError:
74
+ # If import fails, raise the custom error message
75
+ # This is runtime execution, not runtime loading
76
+ raise ImportError(self._error_message)
77
+
78
+ def __dir__(self):
79
+ """Support dir() on the lazy module."""
80
+ if self._imported:
81
+ return dir(self._module)
82
+ return []
83
+
84
+ @property
85
+ def module(self):
86
+ """Access to the original module if it exists."""
87
+ if self._imported and self._module is not None:
88
+ return self._module
89
+ return None
90
+
91
+ def __repr__(self):
92
+ if self._imported:
93
+ return repr(self._module)
94
+ return f"<LazyModule '{self._module_name}' (not imported)>"
95
+
96
+
97
+ def lazy_import(module_name: str, error_message: str, install_hint: str):
98
+ """
99
+ Set up lazy loading for a module.
100
+ The module will be replaced in sys.modules with a LazyModule instance.
101
+ It will only be truly imported when an attribute is accessed.
102
+ """
103
+ sys.modules[module_name] = LazyModule(
104
+ module_name, error_message, install_hint)
@@ -0,0 +1,34 @@
1
+ import logging
2
+ from typing import Any
3
+ import importlib
4
+ from ...logging_.utils import get_logger
5
+
6
+ logger = get_logger(__name__)
7
+
8
+
9
+ def dynamically_load(module_name: str, obj_name: str) -> Any:
10
+ """dynamically loads the module and returns the object from this file
11
+
12
+ Args:
13
+ module_name (str): name of python module, (typically a file name without extension)
14
+ obj_name (str): the name of the wanted object
15
+
16
+ Returns:
17
+ Any: the object
18
+ """
19
+ try:
20
+ module = importlib.import_module(module_name)
21
+ obj = getattr(module, obj_name)
22
+ logger.info("Successfully loaded object '%s' from module '%s'", obj_name, module_name)
23
+ return obj
24
+ except ImportError as e:
25
+ logger.error("Failed to import module '%s': %s", module_name, e)
26
+ raise
27
+ except AttributeError as e:
28
+ logger.error("Object '%s' not found in module '%s': %s", obj_name, module_name, e)
29
+ raise
30
+
31
+
32
+ __all__ = [
33
+ "dynamically_load"
34
+ ]
@@ -0,0 +1,161 @@
1
+ import sys
2
+ import os
3
+ import pkgutil
4
+ from pathlib import Path
5
+ from collections import defaultdict
6
+ from typing import Set as Set, List as List, Dict as Dict
7
+ from ..interpreter import get_python_version
8
+
9
+ if get_python_version() >= (3, 9):
10
+ from builtins import list as List, set as Set, dict as Dict
11
+
12
+
13
+ def get_all_modules() -> Set[str]:
14
+ all_modules = set()
15
+
16
+ # Get built-in modules
17
+ builtin_modules = set(o.strip("_") for o in sys.builtin_module_names)
18
+
19
+ # Get modules from the Python Standard Library
20
+ stdlib_modules = set()
21
+ for module in pkgutil.iter_modules():
22
+ try:
23
+ # type:ignore
24
+ if not module.ispkg and hasattr(module.module_finder, 'path') and module.module_finder.path.startswith(sys.prefix):
25
+ stdlib_modules.add(module.name)
26
+ except AttributeError:
27
+ pass
28
+
29
+ # Combine built-in modules and modules from the Python Standard Library
30
+ all_modules.update(builtin_modules)
31
+ all_modules.update(stdlib_modules)
32
+
33
+ all_modules.update({
34
+ "platform", 'os', 'pathlib', 'subprocess', "inspect", "types", "dataclasses", "__future__", "ctypes", "pkgutil",
35
+ "logging", "importlib", "enum", "multiprocessing", "traceback", "re", "threading"
36
+ })
37
+ return all_modules
38
+
39
+
40
+ ALL_MODULES: set = get_all_modules()
41
+
42
+
43
+ def get_imports_helper(path: str):
44
+ from danielutils import file_exists
45
+ if not file_exists(path):
46
+ raise ValueError(f"Can't find file {path}")
47
+ is_in_multiline_comment = False
48
+ with open(path, "r", encoding="utf-8") as f:
49
+ for l in f.readlines():
50
+ l = l.strip()
51
+ if l.startswith("#"):
52
+ continue
53
+ if '"""' in l:
54
+ if l.count('"""') % 2 == 1:
55
+ is_in_multiline_comment = not is_in_multiline_comment
56
+
57
+ if is_in_multiline_comment:
58
+ continue
59
+
60
+ if any([l.startswith("import "), l.startswith("from ")]):
61
+ yield l
62
+
63
+
64
+ def resolve_relative(base_path: str, statement: str) -> str:
65
+ res = base_path
66
+ splits = statement.split(".")
67
+ for s in splits:
68
+ if s == "":
69
+ res = str(Path(res).parent)
70
+ else:
71
+ res = os.path.join(res, s)
72
+ break
73
+ from danielutils import is_directory
74
+ if not is_directory(res):
75
+ res = f"{res}.py"
76
+ return res
77
+
78
+
79
+ def resolve_absolute(statement: str) -> str:
80
+ if statement in ALL_MODULES:
81
+ return statement
82
+ if "." in statement:
83
+ statement = statement.split(".")[0]
84
+ return resolve_absolute(statement)
85
+ return statement
86
+
87
+
88
+ def resolve_path(base_path, statement: str) -> str:
89
+ og = statement
90
+ FROM = "from"
91
+ IMPORT = "import"
92
+ if FROM in statement:
93
+ statement = statement[statement.index(FROM) + len(FROM):].strip()
94
+ statement = statement[:statement.index(IMPORT)].strip()
95
+ if statement.startswith("."):
96
+ return resolve_relative(base_path, statement)
97
+ else:
98
+ statement = statement[statement.index(IMPORT) + len(IMPORT):].strip()
99
+ return resolve_absolute(statement)
100
+
101
+
102
+ def normalize_path(path: str) -> str:
103
+ from danielutils import is_directory
104
+ if is_directory(path):
105
+ path = os.path.join(path, '__init__.py')
106
+ return str(Path(path).absolute())
107
+
108
+
109
+ def get_imports(path: str) -> Dict[str, Set[str]]:
110
+ res: Dict[str, Set[str]] = defaultdict(set)
111
+ i = 0
112
+ path = normalize_path(path)
113
+ queue: List[str] = [path]
114
+ while i < len(queue):
115
+ cur = queue[i]
116
+ imports = list(get_imports_helper(cur))
117
+ paths = [resolve_path(cur, imp) for imp in imports]
118
+ for sub_path in paths:
119
+ if sub_path in res:
120
+ continue
121
+ from danielutils import file_exists, is_directory
122
+ if not (file_exists(sub_path) or is_directory(sub_path)):
123
+ res[cur].add(sub_path)
124
+ else:
125
+ if sub_path not in res:
126
+ sub_path = normalize_path(sub_path)
127
+ queue.append(sub_path)
128
+ res[cur].add(sub_path)
129
+ i += 1
130
+ return res
131
+
132
+
133
+ def create_dependency_graph(path: str) -> 'Graph[str]':
134
+ from danielutils import Graph, MultiNode
135
+ res = dict(get_imports(path))
136
+ g: Graph[str] = Graph()
137
+ dct: Dict[str, MultiNode[str]] = {}
138
+ for k, v in res.items():
139
+ for o in v:
140
+ dct[o] = dct.get(o, MultiNode(o))
141
+ n = MultiNode(k, [dct[o] for o in v])
142
+ g.add_node(n)
143
+
144
+ return g
145
+
146
+
147
+ def get_dependencies(path: str, topological_sort: bool = True) -> List[str]:
148
+ from danielutils import Graph
149
+ g: Graph[str] = create_dependency_graph(path)
150
+
151
+ if topological_sort:
152
+ return [n.data for n in g.topological_sort()]
153
+
154
+ return [n.data for n in g]
155
+
156
+
157
+ __all__ = [
158
+ "get_dependencies",
159
+ "create_dependency_graph",
160
+ 'ALL_MODULES'
161
+ ]
@@ -0,0 +1,3 @@
1
+ from .backoff_strategies import *
2
+ from .backoff_strategy import *
3
+ from .retry_executor import *
@@ -0,0 +1,6 @@
1
+ from .no_backoff import *
2
+ from .constant_backoff import *
3
+ from .exponential_backoff import *
4
+ from .linear_backoff import *
5
+ from .multiplicative_backoff import *
6
+ from .functional_backoff import *
@@ -0,0 +1,25 @@
1
+ import logging
2
+ from ..backoff_strategy import BackOffStrategy
3
+ from ...logging_.utils import get_logger
4
+
5
+ logger = get_logger(__name__)
6
+
7
+
8
+ class ConstantBackOffStrategy(BackOffStrategy):
9
+ """
10
+ will always back off exactly the same amount of time
11
+
12
+ :param delay: The amount of milliseconds to sleep
13
+ """
14
+
15
+ def __init__(self, delay: float) -> None:
16
+ if not delay >= 0:
17
+ logger.error("Invalid delay value: %s - must be positive", delay)
18
+ raise ValueError("delay must be positive")
19
+ delay = float(delay)
20
+ super().__init__(lambda: delay)
21
+
22
+
23
+ __all__ = [
24
+ "ConstantBackOffStrategy"
25
+ ]
@@ -0,0 +1,26 @@
1
+ import logging
2
+ from ..backoff_strategy import BackOffStrategy
3
+ from ...logging_.utils import get_logger
4
+
5
+ logger = get_logger(__name__)
6
+
7
+
8
+ class ExponentialBackOffStrategy(BackOffStrategy):
9
+ def __init__(self, initial: float) -> None:
10
+ if not initial >= 0:
11
+ logger.error("Invalid initial value: %s - must be positive", initial)
12
+ raise ValueError("initial must be positive")
13
+ attempt: int = 1
14
+
15
+ def inner() -> float:
16
+ nonlocal attempt
17
+ attempt += 1
18
+ backoff_time = initial ** (attempt - 1)
19
+ return backoff_time
20
+
21
+ super().__init__(inner)
22
+
23
+
24
+ __all__ = [
25
+ "ExponentialBackOffStrategy"
26
+ ]
@@ -0,0 +1,29 @@
1
+ import logging
2
+ from typing import Callable
3
+
4
+ from ..backoff_strategy import BackOffStrategy
5
+ from ...logging_.utils import get_logger
6
+
7
+ logger = get_logger(__name__)
8
+
9
+
10
+ class FunctionalBackoffStrategy(BackOffStrategy):
11
+ def __init__(self, func: Callable[[int], float]) -> None:
12
+ logger.debug("Initializing FunctionalBackoffStrategy with function: %s", func.__name__ if hasattr(func, '__name__') else type(func).__name__)
13
+ attempt = 1
14
+ logger.debug("Setting up functional backoff with custom function")
15
+
16
+ def inner() -> float:
17
+ nonlocal attempt
18
+ attempt += 1
19
+ backoff_time = func(attempt - 1)
20
+ logger.debug("Functional backoff attempt %s: %sms (func(%s))", attempt, backoff_time, attempt - 1)
21
+ return backoff_time
22
+
23
+ super().__init__(inner)
24
+ logger.debug("FunctionalBackoffStrategy initialized successfully")
25
+
26
+
27
+ __all__ = [
28
+ "FunctionalBackoffStrategy"
29
+ ]
@@ -0,0 +1,23 @@
1
+ import logging
2
+ from ..backoff_strategy import BackOffStrategy
3
+ from ...logging_.utils import get_logger
4
+
5
+ logger = get_logger(__name__)
6
+
7
+
8
+ class LinerBackoffStrategy(BackOffStrategy):
9
+ def __init__(self, initial: float, additive_term: float) -> None:
10
+ attempt = 1
11
+
12
+ def inner() -> float:
13
+ nonlocal attempt
14
+ attempt += 1
15
+ backoff_time = initial + additive_term * (attempt - 1)
16
+ return backoff_time
17
+
18
+ super().__init__(inner)
19
+
20
+
21
+ __all__ = [
22
+ 'LinerBackoffStrategy'
23
+ ]
@@ -0,0 +1,23 @@
1
+ import logging
2
+ from ..backoff_strategy import BackOffStrategy
3
+ from ...logging_.utils import get_logger
4
+
5
+ logger = get_logger(__name__)
6
+
7
+
8
+ class MultiplicativeBackoff(BackOffStrategy):
9
+ def __init__(self, initial_backoff: float) -> None:
10
+ attempt = 1
11
+
12
+ def inner() -> float:
13
+ nonlocal attempt
14
+ attempt += 1
15
+ backoff_time = initial_backoff * (attempt - 1)
16
+ return backoff_time
17
+
18
+ super().__init__(inner)
19
+
20
+
21
+ __all__ = [
22
+ 'MultiplicativeBackoff'
23
+ ]
@@ -0,0 +1,17 @@
1
+ import logging
2
+ from ..backoff_strategy import BackOffStrategy
3
+ from ...logging_.utils import get_logger
4
+
5
+ logger = get_logger(__name__)
6
+
7
+
8
+ class NoBackOffStrategy(BackOffStrategy):
9
+
10
+ def __init__(self) -> None:
11
+ logger.debug("Initializing NoBackOffStrategy (no delay)")
12
+ super().__init__(lambda: 0.0)
13
+
14
+
15
+ __all__ = [
16
+ 'NoBackOffStrategy',
17
+ ]
@@ -0,0 +1,28 @@
1
+ import logging
2
+ from ..aliases import Supplier
3
+ from ..logging_.utils import get_logger
4
+
5
+ logger = get_logger(__name__)
6
+
7
+
8
+ class BackOffStrategy:
9
+ """
10
+ A class to create a common abstraction for backoff strategies
11
+ """
12
+
13
+ def __init__(self, supp: Supplier[float]) -> None:
14
+ self._supp = supp
15
+
16
+ def get_backoff(self) -> float:
17
+ """
18
+ Get the backoff time in milliseconds.
19
+
20
+ :return: amount of milliseconds to sleep
21
+ """
22
+ backoff_time = self._supp()
23
+ return backoff_time
24
+
25
+
26
+ __all__ = [
27
+ "BackOffStrategy"
28
+ ]
@@ -0,0 +1,55 @@
1
+ import logging
2
+ import time
3
+ from typing import Generic, TypeVar, Optional
4
+
5
+ from ..aliases import Supplier, Consumer
6
+ from .backoff_strategies import ConstantBackOffStrategy
7
+
8
+ from .backoff_strategy import BackOffStrategy
9
+ from ..logging_.utils import get_logger
10
+
11
+ T = TypeVar("T")
12
+
13
+ logger = get_logger(__name__)
14
+
15
+
16
+ class RetryExecutor(Generic[T]):
17
+ def __init__(self, backoff_strategy: BackOffStrategy = ConstantBackOffStrategy(200)) -> None:
18
+ self._backoff_strategy = backoff_strategy
19
+ logger.info("RetryExecutor initialized with backoff strategy: %s", type(backoff_strategy).__name__)
20
+
21
+ def __enter__(self):
22
+ return self
23
+
24
+ def __exit__(self, exc_type, exc_val, exc_tb):
25
+ if exc_type is not None:
26
+ logger.warning("RetryExecutor context exited with exception: %s: %s", exc_type.__name__, exc_val)
27
+
28
+ def execute(self, supp: Supplier[T], max_retries: int = 5,
29
+ exception_callback: Optional[Consumer[Exception]] = None) -> Optional[T]:
30
+ logger.info("Starting retry execution with max_retries=%s", max_retries)
31
+
32
+ for i in range(max_retries):
33
+ try:
34
+ result = supp()
35
+ logger.info("Execution succeeded on attempt %s", i + 1)
36
+ return result
37
+ except Exception as e:
38
+ logger.warning("Attempt %s failed with %s: %s", i + 1, type(e).__name__, e)
39
+ if exception_callback:
40
+ exception_callback(e)
41
+
42
+ if i != max_retries - 1:
43
+ self._sleep()
44
+
45
+ logger.error("All %s attempts failed", max_retries)
46
+ return None
47
+
48
+ def _sleep(self) -> None:
49
+ backoff_time = self._backoff_strategy.get_backoff() / 1000
50
+ time.sleep(backoff_time)
51
+
52
+
53
+ __all__ = [
54
+ "RetryExecutor",
55
+ ]
@@ -0,0 +1 @@
1
+ from .try_get import *
@@ -0,0 +1,28 @@
1
+ import logging
2
+ from typing import Callable, Any, Optional
3
+ from ..logging_.utils import get_logger
4
+
5
+ logger = get_logger(__name__)
6
+
7
+
8
+ def try_get(supplier: Callable[[], Any]) -> Optional[Any]:
9
+ """try to get a value from a function and return the value or return None on fail
10
+
11
+ Args:
12
+ supplier (Callable[[], Any]): supplier function
13
+
14
+ Returns:
15
+ Optional[Any]: return value
16
+ """
17
+ try:
18
+ result = supplier()
19
+ logger.debug("try_get succeeded for supplier: %s", supplier.__name__)
20
+ return result
21
+ except Exception as e:
22
+ logger.debug("try_get failed for supplier: %s with %s: %s", supplier.__name__, type(e).__name__, e)
23
+ return None
24
+
25
+
26
+ __all__ = [
27
+ "try_get"
28
+ ]
@@ -0,0 +1,3 @@
1
+ from .independent import *
2
+ from .windows import *
3
+ from .layered_command import *