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,23 @@
1
+ from datetime import datetime, timezone
2
+
3
+
4
+ def get_datetime() -> datetime:
5
+ """return the current datetime
6
+
7
+ Returns:
8
+ datetime: current datetime
9
+ """
10
+ return datetime.now()
11
+
12
+ def utc_now() -> datetime:
13
+ """return the current datetime in UTC
14
+
15
+ Returns:
16
+ datetime: current datetime in UTC
17
+ """
18
+ return datetime.now(timezone.utc)
19
+
20
+ __all__ = [
21
+ "get_datetime",
22
+ "utc_now"
23
+ ]
@@ -0,0 +1,18 @@
1
+ from .validate import *
2
+ from .atomic import *
3
+ from .attach import *
4
+ from .decorate_conditionally import *
5
+ from .limit_recursion import *
6
+ from .memo import *
7
+ from .overload import *
8
+ from .partially_implemented import *
9
+ from .timeout import *
10
+ from .chain_decorators import *
11
+ from .threadify import *
12
+ from .delay_call import *
13
+ from .deprecate import *
14
+ from .processify import *
15
+ from .singleton import *
16
+ from .total_ordering import *
17
+ from .final import *
18
+ from .normalize_decorator import *
@@ -0,0 +1,45 @@
1
+ import functools
2
+ import logging
3
+ from typing import Callable, Any, TypeVar
4
+ import threading
5
+ from .validate import validate
6
+ from ..logging_.utils import get_logger
7
+ logger = get_logger(__name__)
8
+
9
+ from ..versioned_imports import ParamSpec
10
+
11
+ T = TypeVar("T")
12
+ P = ParamSpec("P")
13
+ FuncT = Callable[P, T] # type:ignore
14
+
15
+
16
+ @validate # type:ignore
17
+ def atomic(func: FuncT) -> FuncT:
18
+ """will make function thread safe by making it
19
+ accessible for only one thread at one time
20
+
21
+ Args:
22
+ func (Callable): function to make thread safe
23
+
24
+ Returns:
25
+ Callable: the thread safe function
26
+ """
27
+ logger.debug("Making function %s atomic (thread-safe)", func.__name__)
28
+ lock = threading.Lock()
29
+
30
+ @functools.wraps(func)
31
+ def wrapper(*args, **kwargs) -> Any:
32
+ logger.debug("Acquiring lock for atomic function %s", func.__name__)
33
+ with lock:
34
+ logger.debug("Executing atomic function %s", func.__name__)
35
+ result = func(*args, **kwargs)
36
+ logger.debug("Atomic function %s completed", func.__name__)
37
+ return result
38
+
39
+ logger.debug("Atomic decorator applied to %s", func.__name__)
40
+ return wrapper
41
+
42
+
43
+ __all__ = [
44
+ "atomic"
45
+ ]
@@ -0,0 +1,61 @@
1
+ import functools
2
+ import logging
3
+ from typing import Callable, Optional, TypeVar
4
+ from .validate import validate
5
+ from ..versioned_imports import ParamSpec
6
+ from ..logging_.utils import get_logger
7
+ logger = get_logger(__name__)
8
+
9
+ T = TypeVar("T")
10
+ P = ParamSpec("P")
11
+ FuncT = Callable[P, T] # type:ignore
12
+
13
+
14
+ @validate(strict=False) # type:ignore
15
+ def attach(before: Optional[Callable] = None, after: Optional[Callable] = None) -> Callable[[FuncT], FuncT]:
16
+ """attaching functions to a function
17
+
18
+ Args:
19
+ before (Callable, optional): function to call before. Defaults to None.
20
+ after (Callable, optional): function to call after. Defaults to None.
21
+
22
+ Raises:
23
+ ValueError: if both before and after are none
24
+ ValueError: if the decorated object is not a Callable
25
+
26
+ Returns:
27
+ Callable: the decorated result
28
+ """
29
+ logger.debug("Creating attach decorator with before=%s, after=%s", before, after)
30
+ if before is None and after is None:
31
+ logger.error("Both before and after functions are None")
32
+ raise ValueError("You must supply at least one function")
33
+
34
+ def attach_deco(func: FuncT) -> FuncT:
35
+ logger.debug("Applying attach decorator to function %s", func.__name__)
36
+ if not callable(func):
37
+ logger.error("Object %s is not callable", func)
38
+ raise ValueError("attach must decorate a function")
39
+
40
+ @functools.wraps(func)
41
+ def wrapper(*args, **kwargs):
42
+ logger.debug("Executing attached function %s", func.__name__)
43
+ if before is not None:
44
+ logger.debug("Calling before function: %s", before.__name__)
45
+ before()
46
+ res = func(*args, **kwargs)
47
+ if after is not None:
48
+ logger.debug("Calling after function: %s", after.__name__)
49
+ after()
50
+ logger.debug("Attached function %s completed", func.__name__)
51
+ return res
52
+
53
+ logger.debug("Attach decorator applied to %s", func.__name__)
54
+ return wrapper
55
+
56
+ return attach_deco
57
+
58
+
59
+ __all__ = [
60
+ "attach"
61
+ ]
@@ -0,0 +1,39 @@
1
+ import functools
2
+ import logging
3
+ from typing import Callable
4
+ from ..logging_.utils import get_logger
5
+
6
+ logger = get_logger(__name__)
7
+
8
+
9
+ def chain_decorators(*decorators, reverse_order: bool = False) -> Callable:
10
+ """will chain the given decorators in the order they appear
11
+
12
+ Args:
13
+ reverse_order (bool, optional): whether to reverse the order of decoration. Defaults to False.
14
+
15
+ Returns:
16
+ Callable: resulting multi-decorated function
17
+ """
18
+ logger.debug("Creating chain decorator with %s decorators, reverse_order=%s", len(decorators), reverse_order)
19
+ def decorators_deco(func):
20
+ logger.debug("Applying chain decorators to function %s", func.__name__)
21
+ @functools.wraps(func)
22
+ def wrapper(*args, **kwargs):
23
+ logger.debug("Executing chained function %s", func.__name__)
24
+ return func(*args, **kwargs)
25
+
26
+ decorator_order = decorators[::1 if reverse_order else -1]
27
+ logger.debug("Applying decorators in order: %s", [d.__name__ if hasattr(d, '__name__') else str(d) for d in decorator_order])
28
+ for i, deco in enumerate(decorator_order):
29
+ logger.debug("Applying decorator %s/%s: %s", i+1, len(decorator_order), deco.__name__ if hasattr(deco, '__name__') else str(deco))
30
+ wrapper = deco(wrapper)
31
+
32
+ logger.debug("Chain decorators applied to %s", func.__name__)
33
+ return wrapper
34
+ return decorators_deco
35
+
36
+
37
+ __all__ = [
38
+ "chain_decorators"
39
+ ]
@@ -0,0 +1,32 @@
1
+ import functools
2
+ from typing import Callable, Optional, Union
3
+ from .validate import validate
4
+
5
+
6
+ @validate(strict=False) # type:ignore
7
+ def decorate_conditionally(decorator: Callable, predicate: Union[bool, Callable[[], bool]], *,
8
+ decorator_args: Optional[list] = None, decorator_kwargs: Optional[dict] = None):
9
+ """will decorate a function iff the predicate is True or returns True
10
+
11
+ Args:
12
+ decorator (Callable): the decorator to use
13
+ predicate (bool | Callable[[], bool]): the predicate
14
+ """
15
+
16
+ def deco(func):
17
+ if (predicate() if callable(predicate) else predicate):
18
+ nonlocal decorator_args, decorator_kwargs, decorator
19
+ if decorator_args is None:
20
+ decorator_args = []
21
+ if decorator_kwargs is None:
22
+ decorator_kwargs = {}
23
+ decorator = functools.wraps(func)(decorator)
24
+ return decorator(*decorator_args, **decorator_kwargs)(func)
25
+ return func
26
+
27
+ return deco
28
+
29
+
30
+ __all__ = [
31
+ "decorate_conditionally"
32
+ ]
@@ -0,0 +1,47 @@
1
+ import logging
2
+ from typing import Callable, TypeVar, Union
3
+ import time
4
+ import functools
5
+ from .decorate_conditionally import decorate_conditionally
6
+ from .threadify import threadify
7
+ from ..versioned_imports import ParamSpec
8
+ from ..logging_.utils import get_logger
9
+
10
+ logger = get_logger(__name__)
11
+
12
+ T = TypeVar("T")
13
+ P = ParamSpec("P")
14
+ FuncT = Callable[P, T] # type:ignore
15
+
16
+
17
+ def delay_call(seconds: Union[float, int], blocking: bool = True) -> Callable[[FuncT], FuncT]:
18
+ """will delay the call to a function by the given amount of seconds
19
+
20
+ Args:
21
+ seconds (float | int): the amount of time to wait
22
+ blocking (bool, optional): whether to block the main thread
23
+ when waiting or to wait in a different thread. Defaults to True.
24
+ """
25
+ logger.debug("Creating delay_call decorator with %ss delay, blocking=%s", seconds, blocking)
26
+
27
+ def deco(func: FuncT) -> FuncT:
28
+ logger.debug("Applying delay_call decorator to function %s", func.__name__)
29
+ @decorate_conditionally(threadify, not blocking)
30
+ @functools.wraps(func)
31
+ def wrapper(*args, **kwargs):
32
+ logger.debug("Delaying call to %s by %s seconds", func.__name__, seconds)
33
+ time.sleep(seconds)
34
+ logger.debug("Delay completed, calling %s", func.__name__)
35
+ result = func(*args, **kwargs)
36
+ logger.debug("Delayed function %s completed", func.__name__)
37
+ return result
38
+
39
+ logger.debug("Delay_call decorator applied to %s", func.__name__)
40
+ return wrapper
41
+
42
+ return deco
43
+
44
+
45
+ __all__ = [
46
+ "delay_call"
47
+ ]
@@ -0,0 +1,46 @@
1
+ from typing import Callable, TypeVar
2
+ from ..colors import warning, ColoredText
3
+ from ..versioned_imports import ParamSpec
4
+
5
+ T = TypeVar("T")
6
+ P = ParamSpec("P")
7
+ FuncT = Callable[P, T] # type:ignore
8
+
9
+
10
+ def deprecate_with(replacement_func) -> Callable[[FuncT], FuncT]:
11
+ """will replace a deprecated function with the replacement func and will print a warning"""
12
+
13
+ def deco(func: FuncT) -> FuncT:
14
+ warning(f"{func.__module__}.{func.__qualname__} is deprecated,"
15
+ f" using {replacement_func.__module__}.{replacement_func.__qualname__} instead")
16
+
17
+ def wrapper(*args, **kwargs):
18
+ return replacement_func(*args, **kwargs)
19
+
20
+ return wrapper
21
+
22
+ return deco
23
+
24
+
25
+ def deprecate(deprecation_message: str) -> Callable[[FuncT], FuncT]:
26
+ """A decorator to print a deprecation message when using a deprecated function
27
+
28
+ Args:
29
+ deprecation_message (str): deprecation message
30
+ """
31
+
32
+ def deco(func: FuncT) -> FuncT:
33
+ def wrapper(*args, **kwargs):
34
+ print(ColoredText.orange("Deprecation Warning") +
35
+ ":", deprecation_message)
36
+ return func(*args, **kwargs)
37
+
38
+ return wrapper
39
+
40
+ return deco
41
+
42
+
43
+ __all__ = [
44
+ "deprecate_with",
45
+ "deprecate"
46
+ ]
@@ -0,0 +1,39 @@
1
+ def final(cls: type) -> type:
2
+ """
3
+ A class Decorator to mark a class as final and add expected behavior
4
+ Args:
5
+ cls: the class to mark
6
+
7
+ Returns:
8
+ marked class
9
+ """
10
+
11
+ def __init__subclass__(*args, **kwargs):
12
+ raise TypeError(f"'{cls.__qualname__}' is final. Can't create subclasses")
13
+
14
+ setattr(cls, "__init_subclass__", __init__subclass__)
15
+ return cls
16
+
17
+
18
+ class Final:
19
+ """
20
+ A parent class to make direct child a Final class. will add expected behaviour.
21
+ """
22
+
23
+ def __new__(cls, *args, **kwargs):
24
+ if cls is Final:
25
+ raise TypeError("Can't instantiate 'Final'")
26
+ return super().__new__(cls, *args, **kwargs)
27
+
28
+ @classmethod
29
+ def __init_subclass__(cls, **kwargs):
30
+ def __init__subclass__(*args, **kwargs):
31
+ raise TypeError(f"'{cls.__qualname__}' is final. Can't create subclasses")
32
+
33
+ setattr(cls, "__init_subclass__", __init__subclass__)
34
+
35
+
36
+ __all__ = [
37
+ "final",
38
+ "Final"
39
+ ]
@@ -0,0 +1,63 @@
1
+ import functools
2
+ import re
3
+ import traceback
4
+ import logging
5
+ from typing import Any, Callable, TypeVar
6
+ from .validate import validate
7
+ from ..colors import warning
8
+ from ..versioned_imports import ParamSpec
9
+ from ..logging_.utils import get_logger
10
+
11
+ logger = get_logger(__name__)
12
+
13
+ T = TypeVar("T")
14
+ P = ParamSpec("P")
15
+ FuncT = Callable[P, T] # type:ignore
16
+
17
+
18
+ @validate # type:ignore
19
+ def limit_recursion(max_depth: int, return_value: Any = None, quiet: bool = True) -> Callable[[FuncT], FuncT]:
20
+ """decorator to limit recursion of functions
21
+
22
+ Args:
23
+ max_depth (int): max recursion depth which is allowed for this function
24
+ return_value (Any, optional): The value to return when the limit is reached. Defaults to None.
25
+ if is None, will return the last a tuple for the last args, kwargs given
26
+ quiet (bool, optional): whether to print a warning message. Defaults to True.
27
+ """
28
+ logger.debug("Creating limit_recursion decorator with max_depth=%s, quiet=%s", max_depth, quiet)
29
+
30
+ def deco(func: FuncT) -> FuncT:
31
+ logger.debug("Applying limit_recursion decorator to function %s", func.__name__)
32
+ @functools.wraps(func)
33
+ def wrapper(*args, **kwargs):
34
+ depth = functools.reduce(
35
+ lambda count, line:
36
+ count + 1 if re.search(rf"{func.__name__}\(.*\)$", line)
37
+ else count,
38
+ traceback.format_stack(), 0
39
+ )
40
+ logger.debug("Function %s called at recursion depth %s/%s", func.__name__, depth, max_depth)
41
+ if depth >= max_depth:
42
+ logger.warning("Recursion limit reached for %s at depth %s", func.__name__, depth)
43
+ if not quiet:
44
+ warning(
45
+ "limit_recursion has limited the number of calls for "
46
+ f"{func.__module__}.{func.__qualname__} to {max_depth}")
47
+ if return_value:
48
+ logger.debug("Returning specified return_value: %s", return_value)
49
+ return return_value
50
+ logger.debug("Returning args and kwargs: %s, %s", args, kwargs)
51
+ return args, kwargs
52
+ logger.debug("Recursion depth %s is within limit, calling function", depth)
53
+ return func(*args, **kwargs)
54
+
55
+ logger.debug("Limit_recursion decorator applied to %s", func.__name__)
56
+ return wrapper
57
+
58
+ return deco
59
+
60
+
61
+ __all__ = [
62
+ "limit_recursion"
63
+ ]
@@ -0,0 +1,68 @@
1
+ import functools
2
+ import logging
3
+ from typing import Callable, Any, TypeVar, Dict, Generator, List, Set, Optional
4
+ from copy import deepcopy
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 memo(func: FuncT) -> FuncT:
18
+ """decorator to memorize function calls in order to improve performance by using more memory
19
+
20
+ Args:
21
+ func (Callable): function to memorize
22
+ """
23
+ logger.debug("Creating memo decorator for function %s", func.__name__)
24
+ cache: Dict[tuple, Any] = {}
25
+
26
+ @functools.wraps(func)
27
+ def wrapper(*args, **kwargs):
28
+ cache_key = (args, *kwargs.items())
29
+ if cache_key not in cache:
30
+ logger.debug("Cache miss for %s, computing result", func.__name__)
31
+ cache[cache_key] = func(*args, **kwargs)
32
+ logger.debug("Result cached for %s", func.__name__)
33
+ else:
34
+ logger.debug("Cache hit for %s, returning cached result", func.__name__)
35
+ return deepcopy(cache[cache_key])
36
+
37
+ logger.debug("Memo decorator applied to %s", func.__name__)
38
+ return wrapper
39
+
40
+
41
+ def memo_generator(func: Callable[P, Generator]) -> Callable[P, Generator]:
42
+ logger.debug("Creating memo_generator decorator for function %s", func.__name__)
43
+ cache: Dict[tuple, Any] = {}
44
+
45
+ @functools.wraps(func)
46
+ def wrapper(*args: P.args, **kwargs: P.kwargs) -> Generator:
47
+ args = tuple(args)
48
+ cache_key = (args, *kwargs.items())
49
+ if cache_key not in cache:
50
+ logger.debug("Cache miss for generator %s, computing and caching result", func.__name__)
51
+ lst = []
52
+ for v in func(*args, **kwargs):
53
+ lst.append(v)
54
+ yield v
55
+ cache[cache_key] = lst
56
+ logger.debug("Generator result cached for %s", func.__name__)
57
+ else:
58
+ logger.debug("Cache hit for generator %s, yielding from cache", func.__name__)
59
+ yield from cache[cache_key]
60
+
61
+ logger.debug("Memo_generator decorator applied to %s", func.__name__)
62
+ return wrapper
63
+
64
+
65
+ __all__ = [
66
+ "memo",
67
+ "memo_generator"
68
+ ]
@@ -0,0 +1,55 @@
1
+ import functools
2
+ from typing import Callable, Any
3
+
4
+
5
+ def normalize_decorator(decorator: Callable[..., Any]) -> Callable[..., Any]:
6
+ """
7
+
8
+ Args:
9
+ decorator: a function that is used as a decorator and you want to be used with "normalized" arguments
10
+
11
+ Returns:
12
+ object:
13
+ The normalized version of the decorator
14
+
15
+ Example:
16
+
17
+ Do
18
+ ```python
19
+ @normalize_decorator
20
+ def validate(func, strict:bool = False):
21
+ @functools.wraps(func)
22
+ def wrapper(*args, **kwargs):
23
+ # do stuff
24
+ return func(*args, **kwargs)
25
+ return wrapper
26
+ ```
27
+
28
+ instead of
29
+ ```python
30
+ def validate(strict_or_func: Optional[Union[bool,Callable]] = None):
31
+ strict = False # default value
32
+ def deco(func):
33
+ @functools.wraps(func)
34
+ def wrapper(*args, **kwargs):
35
+ # do stuff
36
+ return func(*args, **kwargs)
37
+ return wrapper
38
+ if isinstance(strict_or_func, bool):
39
+ strict = strict_or_func
40
+ return deco
41
+ return deco(strict_or_func)
42
+ """
43
+ @functools.wraps(decorator)
44
+ def wrapper(*args, **kwargs):
45
+ if args and callable(args[0]):
46
+ return decorator(args[0], *args[1:], **kwargs)
47
+ else:
48
+ return lambda func: decorator(func, *args, **kwargs)
49
+
50
+ return wrapper
51
+
52
+
53
+ __all__ = [
54
+ "normalize_decorator",
55
+ ]