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,204 @@
1
+ import logging
2
+ from typing import Callable, cast, Any, TypeVar, Dict, List
3
+ import inspect
4
+ import functools
5
+ from ..reflection import is_function_annotated_properly
6
+ from ..functions import isoftype, isoneof, isoneof_strict
7
+ from ..exceptions import OverloadDuplication, OverloadNotFound
8
+ from .deprecate import deprecate
9
+ from ..versioned_imports import ParamSpec
10
+ from ..logging_.utils import get_logger
11
+
12
+ logger = get_logger(__name__)
13
+
14
+ T = TypeVar("T")
15
+ P = ParamSpec("P")
16
+ FuncT = Callable[P, T] # type:ignore
17
+ T2 = TypeVar("T2")
18
+ P2 = ParamSpec("P2")
19
+ FuncT2 = Callable[P2, T2] # type:ignore
20
+
21
+ __overload_dict: Dict[str, Dict[tuple, Callable]] = {}
22
+
23
+
24
+ @deprecate("'explicit_global_overload' is a legacy decorator please use 'overload' instead")
25
+ def explicit_global_overload(*types) -> Callable:
26
+ """decorator for overloading functions\n
27
+ Usage\n-------\n
28
+ @overload(str,str)\n
29
+ def print_info(name,color):
30
+ ...\n\n
31
+ @overload(str,[int,float]))\n
32
+ def print_info(name,age):
33
+ ...\n\n
34
+
35
+ * use None to skip argument
36
+ * use no arguments to mark as default function
37
+ * you should overload in decreasing order of specificity! e.g
38
+ @overload(int) should appear in the code before @overload(Any)
39
+
40
+ \n\n\n
41
+ \nRaises:
42
+ OverloadDuplication: if a functions is overloaded twice (or more)
43
+ with same argument types
44
+ OverloadNotFound: if an overloaded function is called with
45
+ types that has no variant of the function
46
+
47
+ \nNotice:
48
+ The function's __doc__ will hold the value of the last variant only
49
+ """
50
+ # make sure to use unique global dictionary
51
+ if len(types) == 1 and type(types[0]).__name__ == "function":
52
+ raise ValueError("can't create an overload without defining types")
53
+ global __overload_dict
54
+ types = cast(tuple, types)
55
+ # allow input of both tuples and lists for flexibly
56
+ if len(types) > 0:
57
+ types_as_list = list(types)
58
+ for i, maybe_list_of_types in enumerate(types):
59
+ if isoneof(maybe_list_of_types, [list, tuple]):
60
+ types_as_list[i] = tuple(sorted(list(maybe_list_of_types),
61
+ key=lambda sub_type: sub_type.__name__))
62
+ types = tuple(types_as_list)
63
+
64
+ def deco(func: Callable) -> Callable:
65
+ if not callable(func):
66
+ raise TypeError("overload decorator must be used on a callable")
67
+
68
+ # assign current overload to overload dictionary
69
+ name = f"{func.__module__}.{func.__qualname__}"
70
+
71
+ if name not in __overload_dict:
72
+ __overload_dict[name] = {}
73
+
74
+ if types in __overload_dict[name]:
75
+ # raise if current overload already exists for current function
76
+ raise OverloadDuplication(
77
+ f"{name} has duplicate overloading for type(s): {types}")
78
+
79
+ __overload_dict[name][types] = func
80
+
81
+ @functools.wraps(func)
82
+ def wrapper(*args, **kwargs) -> Any:
83
+ default_func = None
84
+ # select correct overload
85
+ for variable_types, curr_func in __overload_dict[f"{func.__module__}.{func.__qualname__}"].items():
86
+ if len(variable_types) == 0:
87
+ if default_func is None:
88
+ default_func = curr_func
89
+ continue
90
+ # will not reach here because of duplicate overloading so this is redundant
91
+ raise ValueError("Can't have two default functions")
92
+
93
+ if len(variable_types) != len(args):
94
+ continue
95
+
96
+ for i, variable_type in enumerate(variable_types):
97
+ if variable_type is not None:
98
+ if isoneof(variable_type, [list, tuple]):
99
+ if not isoneof_strict(args[i], variable_type):
100
+ break
101
+ else:
102
+ if not isoftype(args[i], variable_type):
103
+ break
104
+ else:
105
+ return curr_func(*args, **kwargs)
106
+
107
+ if default_func is not None:
108
+ return default_func(*args, **kwargs)
109
+ # or raise exception if no overload exists for current arguments
110
+ raise OverloadNotFound(
111
+ f"function {func.__module__}.{func.__qualname__} is not overloaded with {[type(v) for v in args]}")
112
+
113
+ return wrapper
114
+
115
+ return deco
116
+
117
+
118
+ class overload:
119
+ """this class create an object to manage the overloads for a given function.\n
120
+ will only match a specific resolution and won't infer best guess for types
121
+ """
122
+
123
+ __SKIP_SET = {"self", "cls", "args", "kwargs"}
124
+
125
+ def __init__(self, func: FuncT):
126
+ overload._validate(func)
127
+ self._qualname = func.__qualname__
128
+ self._moudle = func.__module__
129
+ self._functions: Dict[int, List[Callable]] = {}
130
+ self._functions[overload._get_key(func)] = [func]
131
+ functools.wraps(func)(self)
132
+
133
+ @staticmethod
134
+ def _get_key(func: Callable):
135
+ return len(inspect.signature(func).parameters)
136
+
137
+ @staticmethod
138
+ def _validate(func: Callable):
139
+ if not callable(func):
140
+ raise ValueError("Can only overload functions")
141
+ if not is_function_annotated_properly(func):
142
+ raise ValueError(
143
+ f"{func.__module__}.{func.__qualname__} is not properly annotated."
144
+ "\nFunction must be fully annotated to be overloaded")
145
+
146
+ def overload(self, func: FuncT2) -> "overload":
147
+ """will add another function to the list of available options
148
+
149
+ Args:
150
+ func (Callable): a new alternative function
151
+
152
+ Returns:
153
+ overload2: returns the overload object
154
+ """
155
+ self._validate(func)
156
+ k = overload._get_key(func)
157
+ if k not in self._functions:
158
+ self._functions[k] = []
159
+ self._functions[k].append(func)
160
+ return self
161
+
162
+ def __call__(self, *args, **kwargs):
163
+ num_args = len(args) + len(kwargs.keys())
164
+ if num_args not in self._functions:
165
+ raise AttributeError(
166
+ f"No overload with {num_args} argument found for {self._moudle}.{self._qualname}")
167
+
168
+ if num_args == 0:
169
+ return self._functions[num_args][0](*args, **kwargs)
170
+
171
+ max_score = 0
172
+ winner = self._functions[num_args][0]
173
+ EXACT_MATCH = 1 / num_args
174
+ SUBCLASS = 1 / num_args
175
+ for func in self._functions[num_args]:
176
+ score = 0
177
+ signature = inspect.signature(func)
178
+ for i, tup in enumerate(signature.parameters.items()):
179
+ param_name, param_type = tup
180
+ if param_name in overload.__SKIP_SET:
181
+ continue
182
+
183
+ if type(args[i]) == param_type.annotation: # pylint :disable=unidiomatic-typecheck
184
+ score += EXACT_MATCH # type:ignore
185
+
186
+ elif isoftype(args[i], param_type.annotation):
187
+ score += SUBCLASS # type:ignore
188
+ else:
189
+ break
190
+
191
+ else:
192
+ # reaching here means current function matches perfectly the annotation
193
+ if score > max_score:
194
+ max_score = score
195
+ winner = func
196
+ # raise AttributeError("No overload found")
197
+
198
+ return winner(*args, **kwargs)
199
+
200
+
201
+ __all__ = [
202
+ "explicit_global_overload",
203
+ "overload"
204
+ ]
@@ -0,0 +1,32 @@
1
+ from typing import Callable, Any, TypeVar
2
+ import functools
3
+ from .validate import validate
4
+ from ..colors import warning
5
+ from ..versioned_imports import ParamSpec
6
+
7
+ T = TypeVar("T")
8
+ P = ParamSpec("P")
9
+ FuncT = Callable[P, T] # type:ignore
10
+
11
+
12
+ @validate # type:ignore
13
+ def PartiallyImplemented(func: FuncT) -> FuncT:
14
+ """decorator to mark function as not fully implemented for development purposes
15
+
16
+ Args:
17
+ func (Callable): the function to decorate
18
+ """
19
+
20
+ @functools.wraps(func)
21
+ def wrapper(*args, **kwargs) -> Any:
22
+ warning(
23
+ f"As marked by the developer, {func.__module__}.{func.__qualname__} "
24
+ "may not be fully implemented and might not work properly.")
25
+ return func(*args, **kwargs)
26
+
27
+ return wrapper
28
+
29
+
30
+ __all__ = [
31
+ "PartiallyImplemented"
32
+ ]
@@ -0,0 +1,74 @@
1
+ import inspect
2
+ import functools
3
+ import multiprocessing
4
+ import logging
5
+ from typing import Any, Dict
6
+
7
+ from ..reflection import get_prev_frame
8
+ from ..abstractions.multiprogramming import process_id
9
+ from ..logging_.utils import get_logger
10
+
11
+ logger = get_logger(__name__)
12
+
13
+
14
+ def processify(func):
15
+ """Modifies the function so that when calling it, a new process
16
+ will start to run it with provided arguments. Note that no return
17
+ value will be given.
18
+
19
+ Args:
20
+ func (Callable): the function to make a process
21
+
22
+ Returns:
23
+ Callable: the modified function
24
+ """
25
+ logger.debug("Creating processify decorator for function %s", func.__name__)
26
+ multiprocessing.freeze_support()
27
+
28
+ @functools.wraps(func)
29
+ def wrapper(*args, **kwargs):
30
+ main_pid = kwargs.get("__main_pid", process_id())
31
+ current_pid = process_id()
32
+ logger.debug("Processify wrapper called for %s, main_pid=%s, current_pid=%s", func.__name__, main_pid, current_pid)
33
+
34
+ if current_pid == main_pid:
35
+ logger.info("Starting new process for function %s", func.__name__)
36
+ frame = get_prev_frame(2) # type:ignore
37
+ dct = {k: v for k, v in frame.f_globals.items() if type(v) != type(inspect)} # type:ignore
38
+ logger.debug("Process context prepared with %s global variables", len(dct))
39
+ p = multiprocessing.Process(target=_run_func, args=(main_pid, dct, func.__name__, args, kwargs))
40
+ p.start()
41
+ logger.debug("Process started for %s, PID: %s", func.__name__, p.pid)
42
+ p.join() # Optionally wait for the process to finish
43
+ logger.info("Process completed for %s", func.__name__)
44
+ else:
45
+ logger.debug("Executing %s in child process", func.__name__)
46
+ del kwargs["__main_pid"]
47
+ return func(*args, **kwargs)
48
+
49
+ logger.debug("Processify decorator applied to %s", func.__name__)
50
+ return wrapper
51
+
52
+
53
+ def _run_func(main_pid: int, dct: dict, func_name: str, args, kwargs) -> None:
54
+ logger.debug("Running function %s in child process", func_name)
55
+ return dct[func_name](*args, __main_pid=main_pid, **kwargs)
56
+
57
+
58
+ def debug_info(include_builtins: bool = False) -> Dict[str, Any]:
59
+ f = get_prev_frame(2)
60
+ if f is None:
61
+ raise RuntimeError("Failed to get frame")
62
+ g = {k: v for k, v in f.f_globals.items() if k != "__builtins__"} if not include_builtins else dict(f.f_globals)
63
+ return {
64
+ "file": f.f_code.co_filename,
65
+ "function": f.f_code.co_qualname, # type:ignore
66
+ "line": f.f_lineno,
67
+ "globals": g,
68
+ "locals": dict(f.f_locals)
69
+ }
70
+
71
+
72
+ __all__ = [
73
+ "processify"
74
+ ]
@@ -0,0 +1,42 @@
1
+ from typing import Any, Union
2
+
3
+
4
+ class property2:
5
+ """my partial implementation to @property
6
+ """
7
+
8
+ def __init__(self, func):
9
+ self.gfunc = func
10
+ self.sfunc = None
11
+ self.dfunc = None
12
+
13
+ def setter(self, func):
14
+ """setter function
15
+ """
16
+ self.sfunc = func
17
+ return self
18
+
19
+ def deleter(self, func):
20
+ """deleter function"""
21
+ self.dfunc = func
22
+ return self
23
+
24
+ def __get__(self, instance: Any, owner: Union[type, None] = None) -> Any:
25
+ if callable(self.gfunc):
26
+ return self.gfunc(instance)
27
+ raise ValueError("Can't use unset getter function")
28
+
29
+ def __set__(self, instance: Any, value: Any) -> None:
30
+ if callable(self.sfunc):
31
+ return self.sfunc(instance)
32
+ raise ValueError("Can't use unset getter function")
33
+
34
+ def __delete__(self, instance: Any) -> None:
35
+ if callable(self.dfunc):
36
+ return self.dfunc(instance)
37
+ raise ValueError("Can't use unset getter function")
38
+
39
+
40
+ __all__ = [
41
+ "property2"
42
+ ]
@@ -0,0 +1,59 @@
1
+ import logging
2
+ from ..logging_.utils import get_logger
3
+
4
+ logger = get_logger(__name__)
5
+
6
+
7
+ def singleton(og_class):
8
+ """Decorator that ensures a class has only one instance (Singleton pattern)."""
9
+ instance = None
10
+ original_new = getattr(og_class, '__new__')
11
+ original_init = getattr(og_class, '__init__')
12
+
13
+ def __new__(cls, *args, **kwargs):
14
+ nonlocal instance
15
+ if instance is None:
16
+ logger.debug("Creating singleton instance for %s", og_class.__name__)
17
+ # index 0 is the current class.
18
+ # in the minimal case index 1 has 'object' class
19
+ # otherwise the immediate parent of current class
20
+ cls_index, og_index = 0, list(cls.__mro__).index(og_class)
21
+ blacklist = {*cls.__mro__[:og_index + 1]}
22
+ for candidate in cls.__mro__[og_index + 1:]:
23
+ if candidate not in blacklist:
24
+ try:
25
+ instance = candidate.__new__(cls, *args, **kwargs)
26
+ logger.debug("Successfully created singleton instance using %s", candidate.__name__)
27
+ break
28
+ except Exception as e:
29
+ logger.debug("Failed to create instance using %s: %s", candidate.__name__, e)
30
+ pass
31
+ else:
32
+ instance = object.__new__(cls)
33
+ logger.debug("Created singleton instance using object.__new__")
34
+ else:
35
+ logger.debug("Returning existing singleton instance for %s", og_class.__name__)
36
+ return instance
37
+
38
+ is_init: bool = False
39
+
40
+ def __init__(self, *args, **kwargs) -> None:
41
+ nonlocal is_init
42
+ if not is_init:
43
+ logger.debug("Initializing singleton instance for %s", og_class.__name__)
44
+ original_init(self, *args, **kwargs)
45
+ is_init = True
46
+ logger.info("Singleton instance initialized for %s", og_class.__name__)
47
+ else:
48
+ logger.debug("Singleton instance already initialized for %s", og_class.__name__)
49
+
50
+ setattr(og_class, "__new__", __new__)
51
+ setattr(og_class, "__init__", __init__)
52
+ setattr(og_class, "instance", lambda: instance)
53
+ logger.debug("Applied singleton decorator to %s", og_class.__name__)
54
+ return og_class
55
+
56
+
57
+ __all__ = [
58
+ "singleton"
59
+ ]
@@ -0,0 +1,40 @@
1
+ import logging
2
+ from typing import Callable, TypeVar
3
+ import functools
4
+ import threading
5
+ from ..versioned_imports import ParamSpec
6
+ from ..logging_.utils import get_logger
7
+
8
+ logger = get_logger(__name__)
9
+
10
+ T = TypeVar("T")
11
+ P = ParamSpec("P")
12
+ FuncT = Callable[P, T] # type:ignore
13
+
14
+
15
+ def threadify(func: FuncT) -> FuncT:
16
+ """will modify the function that when calling it a new thread
17
+ will start to run it with provided arguments.\nnote that no return value will be given
18
+
19
+ Args:
20
+ func (Callable): the function to make a thread
21
+
22
+ Returns:
23
+ Callable: the modified function
24
+ """
25
+ logger.debug("Creating threadify decorator for function %s", func.__name__)
26
+
27
+ @functools.wraps(func)
28
+ def wrapper(*args, **kwargs):
29
+ logger.debug("Starting thread for function %s", func.__name__)
30
+ thread = threading.Thread(target=func, args=args, kwargs=kwargs)
31
+ thread.start()
32
+ logger.debug("Thread started for %s, thread_id=%s", func.__name__, thread.ident)
33
+
34
+ logger.debug("Threadify decorator applied to %s", func.__name__)
35
+ return wrapper
36
+
37
+
38
+ __all__ = [
39
+ "threadify"
40
+ ]
@@ -0,0 +1,82 @@
1
+ import threading
2
+ import functools
3
+ import logging
4
+ from typing import Callable, TypeVar, Union
5
+ from .validate import validate
6
+ from ..versioned_imports import ParamSpec
7
+ from ..logging_.utils import get_logger
8
+
9
+ logger = get_logger(__name__)
10
+
11
+ T = TypeVar("T")
12
+ P = ParamSpec("P")
13
+ FuncT = Callable[P, T] # type:ignore
14
+
15
+
16
+ @validate # type:ignore
17
+ def timeout(duration: Union[int, float], silent: bool = False) -> Callable[[FuncT], FuncT]:
18
+ """A decorator to limit runtime for a function
19
+
20
+ Args:
21
+ duration (Union[int, float]): allowed runtime duration
22
+ silent (bool, optional): keyword only argument whether
23
+ to pass the exception up the call stack. Defaults to False.
24
+
25
+ Raises:
26
+ ValueError: if a function is not provided to be decorated
27
+ Exception: any exception from within the function
28
+
29
+ Returns:
30
+ Callable: the result decorated function
31
+ """
32
+ logger.debug("Creating timeout decorator with duration=%ss, silent=%s", duration, silent)
33
+
34
+ # https://stackoverflow.com/a/21861599/6416556
35
+ def timeout_deco(func: FuncT) -> FuncT:
36
+ logger.debug("Applying timeout decorator to function %s", func.__name__)
37
+ if not callable(func):
38
+ logger.error("Object %s is not callable", func)
39
+ raise ValueError("timeout must decorate a function")
40
+
41
+ @functools.wraps(func)
42
+ def wrapper(*args, **kwargs):
43
+ logger.debug("Executing function %s with timeout %ss", func.__name__, duration)
44
+ res: list = [
45
+ TimeoutError(f'{func.__module__}.{func.__qualname__} timed out after {duration} seconds!')]
46
+
47
+ def timeout_wrapper() -> None:
48
+ try:
49
+ logger.debug("Starting timeout thread for %s", func.__name__)
50
+ res[0] = func(*args, **kwargs)
51
+ logger.debug("Function %s completed successfully in timeout thread", func.__name__)
52
+ except Exception as function_error: # pylint : disable=broad-exception-caught
53
+ logger.warning("Function %s raised exception in timeout thread: %s: %s", func.__name__, type(function_error).__name__, function_error)
54
+ res[0] = function_error
55
+
56
+ t = threading.Thread(target=timeout_wrapper, daemon=True)
57
+ try:
58
+ logger.debug("Starting timeout thread for %s", func.__name__)
59
+ t.start()
60
+ t.join(duration)
61
+ logger.debug("Timeout thread for %s completed or timed out", func.__name__)
62
+ except Exception as thread_error:
63
+ logger.error("Thread error for %s: %s: %s", func.__name__, type(thread_error).__name__, thread_error)
64
+ raise thread_error
65
+ if isinstance(res[0], BaseException):
66
+ if not silent:
67
+ logger.warning("Function %s timed out or raised exception: %s", func.__name__, type(res[0]).__name__)
68
+ raise res[0]
69
+ logger.debug("Function %s timed out but silent mode enabled", func.__name__)
70
+ return None
71
+ logger.debug("Function %s completed successfully within timeout", func.__name__)
72
+ return res[0]
73
+
74
+ logger.debug("Timeout decorator applied to %s", func.__name__)
75
+ return wrapper
76
+
77
+ return timeout_deco
78
+
79
+
80
+ __all__ = [
81
+ "timeout"
82
+ ]
@@ -0,0 +1,82 @@
1
+ from typing import Callable, Any, Dict, List, Tuple
2
+
3
+
4
+ def is_default_implementation(func: Callable) -> bool:
5
+ return type(func).__qualname__ == "wrapper_descriptor"
6
+
7
+
8
+ def usable(func: Callable) -> bool:
9
+ return func is not None and not is_default_implementation(func)
10
+
11
+
12
+ def total_ordering(cls: type) -> type:
13
+ funcs: Dict[str, Callable[[Any, Any], Any]] = {
14
+ '__eq__': getattr(cls, '__eq__'),
15
+ '__ne__': getattr(cls, '__ne__'),
16
+ '__gt__': getattr(cls, '__gt__'),
17
+ '__ge__': getattr(cls, '__ge__'),
18
+ '__lt__': getattr(cls, '__lt__'),
19
+ '__le__': getattr(cls, '__le__'),
20
+ }
21
+ g1: List[Callable[[Any, Any], Any]] = [funcs["__eq__"], funcs["__ne__"]]
22
+ u1 = any(map(usable, g1))
23
+ g2: List[Callable[[Any, Any], Any]] = [funcs["__ge__"], funcs["__gt__"]]
24
+ u2 = any(map(usable, g2))
25
+ g3: List[Callable[[Any, Any], Any]] = [funcs["__lt__"], funcs["__le__"]]
26
+ u3 = any(map(usable, g3))
27
+ if sum(map(int, [u1, u2, u3])) < 2:
28
+ raise ValueError("There are not enough functions from different groups implemented")
29
+ mapping: Dict[str, Dict[Tuple, Callable]] = {
30
+ '__eq__': {
31
+ # a==b <=> not a!=b
32
+ ('__ne__',): lambda self, other: not funcs['__eq__'](self, other),
33
+ # a==b <=> a<=b and b<=a
34
+ ('__le__',): lambda self, other: funcs['__le__'](self, other) and funcs['__le__'](other, self),
35
+ # a==b <=> a>=b and b>=a
36
+ # a==b <=> not a<b and not b<a
37
+ # a==b <=> not a>b and not b>a
38
+ # a==b <=> a<=b and not a<b
39
+ ('__le__', '__lt__'): lambda self, other: funcs['__le__'](self, other) and not funcs['__lt__'](self, other),
40
+ # a==b <=> a>=b and not a>b
41
+ ('__ge__', '__ge__'): lambda self, other: funcs['__ge__'](self, other) and not funcs['__gt__'](self, other),
42
+ # a==b <=> a<=b and not a>=b
43
+ ('__le__', '__ge__'): lambda self, other: funcs['__ge__'](self, other) and funcs['__le__'](self, other),
44
+ # not a<b and not b>a
45
+ ('__lt__', '__gt__'): lambda self, other: not funcs['__lt__'](self, other) and not funcs['__gt__'](other,
46
+ self),
47
+ },
48
+ '__ne__': {
49
+ ('__eq__',): lambda self, other: not funcs['__eq__'](self, other),
50
+ },
51
+ '__lt__': {
52
+ ('__ge__',): lambda self, other: not funcs['__ge__'](self, other),
53
+ ('__eq__', '__le__'): lambda self, other: funcs['__le__'](self, other) and not funcs['__eq__'](self, other),
54
+ },
55
+ '__le__': {
56
+ ('__gt__',): lambda self, other: not funcs['__gt__'](self, other),
57
+ ('__eq__', '__lt__'): lambda self, other: funcs['__lt__'](self, other) or funcs['__eq__'](self, other),
58
+ },
59
+ '__gt__': {
60
+ ('__le__',): lambda self, other: not funcs['__le__'](self, other),
61
+ ('__eq__', '__ge__'): lambda self, other: funcs['__ge__'](self, other) and not funcs['__eq__'](self, other),
62
+ },
63
+ '__ge__': {
64
+ ('__lt__',): lambda self, other: not funcs['__lt__'](self, other),
65
+ ('__eq__', '__gt__'): lambda self, other: funcs['__gt__'](self, other) or funcs['__eq__'](self, other),
66
+ },
67
+ }
68
+ for name, f in funcs.items():
69
+ if usable(f):
70
+ continue
71
+ for option, implementation in mapping[name].items():
72
+ if all(map(usable, (funcs[dep] for dep in option))):
73
+ funcs[name] = implementation
74
+ break
75
+ for name, f in funcs.items():
76
+ setattr(cls, name, f)
77
+ return cls
78
+
79
+
80
+ __all__ = [
81
+ "total_ordering"
82
+ ]