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
danielutils/random_.py ADDED
@@ -0,0 +1,47 @@
1
+ import random
2
+
3
+ LOWERCASE_LETTERS: str = "abcdefghijklmnopqrstuvwxyz"
4
+ UPPERCASE_LETTERS: str = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
5
+ LETTERS = LOWERCASE_LETTERS + UPPERCASE_LETTERS
6
+ DIGITS: str = "0123456789"
7
+ ALL = LETTERS + DIGITS
8
+
9
+
10
+ def builder(length: int, letters: str) -> str:
11
+ return "".join(random.choices(letters, k=length))
12
+
13
+
14
+ class RandomDataGenerator:
15
+
16
+ @staticmethod
17
+ def string(length: int) -> str:
18
+ return builder(length, ALL)
19
+
20
+ @staticmethod
21
+ def name(length: int) -> str:
22
+ return builder(length, LETTERS)
23
+
24
+ @staticmethod
25
+ def file_name(length: int) -> str:
26
+ return "file_" + RandomDataGenerator.name(length)
27
+
28
+ @staticmethod
29
+ def directory_name(length: int) -> str:
30
+ return "directory_" + RandomDataGenerator.name(length)
31
+
32
+ @staticmethod
33
+ def number(minimum: int, exclusive_maximum: int) -> int:
34
+ return random.randint(minimum, exclusive_maximum)
35
+
36
+ @staticmethod
37
+ def integer(minimum: int, exclusive_maximum: int) -> int:
38
+ return random.randint(minimum, exclusive_maximum)
39
+
40
+ @staticmethod
41
+ def float(minimum: float, exclusive_maximum: float) -> float:
42
+ return random.uniform(minimum, exclusive_maximum)
43
+
44
+
45
+ __all__ = [
46
+ "RandomDataGenerator",
47
+ ]
@@ -0,0 +1,6 @@
1
+ from .interpreter import * # this has to be first, the order matters!
2
+ from .file import *
3
+ from .function import *
4
+ from .info_classes import *
5
+ from .module import *
6
+ from .declarations import *
@@ -0,0 +1,288 @@
1
+ import inspect
2
+ import re
3
+ from dataclasses import dataclass
4
+ from typing import Any, List, Optional, Protocol, Tuple, Type, Union
5
+
6
+ from .interpreter import get_python_version
7
+
8
+ if get_python_version() >= (3, 9):
9
+ from builtins import list as List, tuple as Tuple # type:ignore
10
+
11
+ argument_kwargs = dict(frozen=True)
12
+ function_declaration_kwargs = dict(frozen=True)
13
+ if get_python_version() >= (3, 10):
14
+ argument_kwargs.update(slots=True)
15
+ function_declaration_kwargs.update(slots=True)
16
+
17
+
18
+ def get_explicitly_declared_functions(cls: type) -> List[str]:
19
+ return [func for func, _ in inspect.getmembers(cls, predicate=inspect.isfunction)]
20
+
21
+
22
+ def get_mro(obj: Any) -> List[type]:
23
+ if isinstance(obj, type):
24
+ return obj.mro()
25
+ return get_mro(obj.__class__)
26
+
27
+
28
+ @dataclass(**argument_kwargs)
29
+ class Argument:
30
+ name: str
31
+ type: Optional[str]
32
+ default: Optional[str]
33
+
34
+ def __hash__(self) -> int:
35
+ arg_type = self.type
36
+ if self.type is not None and "Union" in self.type:
37
+ arg_type = tuple(sorted(self.type[self.type.index("[") + 1:self.type.rindex("]")].split(",")))
38
+ return hash((self.name, arg_type, self.default))
39
+
40
+ def duplicate(self, **override_kwargs) -> 'Argument':
41
+ data = dict(name=self.name, type=self.type, default=self.default)
42
+ data.update(override_kwargs)
43
+ return Argument(**data)
44
+
45
+
46
+ func_pattern = re.compile(
47
+ r"(.*def[\s\\]*?(\w+)[\s\\]*?\(([\w\W]*?)\)(?:[\s\\]*?->[\s\\]*?([\w\(\)\[\]\,]+))?[\s\\]*?:)",
48
+ flags=re.MULTILINE)
49
+ not_whitespace_pattern = re.compile(r"\S+", flags=re.MULTILINE)
50
+ arg_pattern = re.compile(r"([\w\*\/]+)(?:\s*?:\s*?([\w\[\]\(\)\,]+))?(?:\s*?=\s*?(.+))?")
51
+ class_pattern = re.compile(r"\s*class\s+?\w+\s*?(?:\((.*)\))?\s*?:")
52
+
53
+
54
+ def split_args(args: str) -> List[str]:
55
+ from ..data_structures.stack import Stack
56
+
57
+ res = []
58
+ stack: Stack[str] = Stack()
59
+ start = 0
60
+ for i, char in enumerate(args):
61
+ if char not in {'(', ')', '[', ']', ','}:
62
+ continue
63
+ if char in {'(', "["}:
64
+ stack.push(char)
65
+ elif char in {')', ']'}:
66
+ stack.pop()
67
+ elif stack.is_empty():
68
+ res.append(args[start:i])
69
+ start = i + 1
70
+
71
+ res.append(args[start:len(args)])
72
+ return res
73
+
74
+
75
+ def _format_type_name(type_obj: Any) -> Optional[str]:
76
+ if type_obj is None or type_obj is inspect.Parameter.empty:
77
+ return None
78
+ origin = getattr(type_obj, "__origin__", None)
79
+ if origin is not None:
80
+ args = getattr(type_obj, "__args__", ())
81
+ formatted_args = ", ".join(_format_type_name(arg) or str(arg) for arg in args)
82
+ origin_name = getattr(origin, "__name__", str(origin).replace("typing.", ""))
83
+ return f"{origin_name}[{formatted_args}]"
84
+ if hasattr(type_obj, "__name__"):
85
+ return type_obj.__name__
86
+ return str(type_obj).replace("typing.", "")
87
+
88
+
89
+ def _extract_generics_from_cls(cls: type) -> List[str]:
90
+ parameters: List[str] = []
91
+ for param in getattr(cls, "__parameters__", ()):
92
+ parameters.append(param.__name__)
93
+ return parameters
94
+
95
+
96
+ def remove_whitespace(text: str) -> str:
97
+ return "".join(not_whitespace_pattern.findall(text.strip())).replace("\\", "")
98
+
99
+
100
+ def _should_use_runtime_introspection(cls: type) -> bool:
101
+ return "<locals>" in getattr(cls, "__qualname__", "")
102
+
103
+
104
+ def _source_matches_class(cls: type, src: str) -> bool:
105
+ if _should_use_runtime_introspection(cls):
106
+ return False
107
+ class_pattern_match = re.compile(
108
+ rf"^\s*class\s+{re.escape(cls.__name__)}\b",
109
+ re.MULTILINE,
110
+ )
111
+ return class_pattern_match.search(src) is not None
112
+
113
+
114
+ @dataclass(**function_declaration_kwargs)
115
+ class FunctionDeclaration:
116
+ name: str
117
+ arguments: Tuple[Argument, ...]
118
+ return_type: Optional[str]
119
+ decorators: Optional[List[str]] = None
120
+ generics: Optional[Tuple[str, ...]] = None
121
+
122
+ def duplicate(self, **override_kwargs) -> 'FunctionDeclaration':
123
+ data = dict(
124
+ name=self.name,
125
+ arguments=self.arguments,
126
+ return_type=self.return_type,
127
+ decorators=self.decorators,
128
+ generics=self.generics,
129
+ )
130
+ data.update(override_kwargs)
131
+ return FunctionDeclaration(**data)
132
+
133
+ @property
134
+ def has_generics(self) -> bool:
135
+ return self.generics is not None and len(self.generics) > 0
136
+
137
+ @staticmethod
138
+ def _from_runtime(cls: type) -> List['FunctionDeclaration']:
139
+ parameters = _extract_generics_from_cls(cls)
140
+ res: List[FunctionDeclaration] = []
141
+ expected_qualname_prefix = f"{cls.__qualname__}."
142
+ for name, obj in cls.__dict__.items():
143
+ if not inspect.isfunction(obj):
144
+ continue
145
+ if getattr(obj, "__qualname__", "") != expected_qualname_prefix + name:
146
+ continue
147
+ sig = inspect.signature(obj)
148
+ arguments: List[Argument] = []
149
+ for pname, param in sig.parameters.items():
150
+ if pname == "self":
151
+ continue
152
+ ptype = _format_type_name(
153
+ param.annotation if param.annotation is not inspect.Parameter.empty else None
154
+ )
155
+ default = None if param.default is inspect.Parameter.empty else repr(param.default)
156
+ arguments.append(Argument(pname, ptype, default))
157
+ return_type = _format_type_name(
158
+ sig.return_annotation if sig.return_annotation is not inspect.Signature.empty else None
159
+ )
160
+ res.append(FunctionDeclaration(
161
+ name,
162
+ tuple(arguments),
163
+ return_type,
164
+ decorators=None,
165
+ generics=tuple(parameters) or None,
166
+ ))
167
+ return res
168
+
169
+ @staticmethod
170
+ def get_declared_functions(cls) -> List['FunctionDeclaration']:
171
+ if not isinstance(cls, type):
172
+ raise TypeError('cls must be a Class')
173
+ if _should_use_runtime_introspection(cls):
174
+ return FunctionDeclaration._from_runtime(cls)
175
+ try:
176
+ src = inspect.getsource(cls)
177
+ if not _source_matches_class(cls, src):
178
+ return FunctionDeclaration._from_runtime(cls)
179
+ except (OSError, TypeError):
180
+ return FunctionDeclaration._from_runtime(cls)
181
+ bases = [item for item in map(remove_whitespace, class_pattern.findall(src)) if len(item) > 0]
182
+ parameters: List[str] = []
183
+ for base in bases:
184
+ if '[' in base and ']' in base:
185
+ generic_args = base[base.index('[') + 1:base.rindex(']')]
186
+ for arg in split_args(generic_args):
187
+ if len(arg) == 1:
188
+ parameters.append(arg)
189
+ res = []
190
+ for _, name, args, ret in func_pattern.findall(src):
191
+ name = name.strip()
192
+ if name not in cls.__dict__:
193
+ continue
194
+ args = remove_whitespace(args) if args is not None else None
195
+ arguments = []
196
+ if args is not None:
197
+ for arg in split_args(args):
198
+ match = arg_pattern.match(remove_whitespace(arg))
199
+ if match is not None:
200
+ arguments.append(Argument(*match.groups()))
201
+ return_type = ret.strip() if ret is not None and len(ret) != 0 else None
202
+ res.append(FunctionDeclaration(
203
+ name,
204
+ tuple(arguments),
205
+ return_type,
206
+ decorators=None,
207
+ generics=tuple(parameters) or None,
208
+ ))
209
+ return res
210
+
211
+ def __eq__(self, other) -> bool:
212
+ if not isinstance(other, FunctionDeclaration):
213
+ return False
214
+ return (
215
+ self.name == other.name
216
+ and self.arguments == other.arguments
217
+ and self.return_type == other.return_type
218
+ )
219
+
220
+ def __hash__(self) -> int:
221
+ return hash((self.name, self.arguments, self.return_type))
222
+
223
+
224
+ class _ProtocolMarker(Protocol):
225
+ ...
226
+
227
+
228
+ _ProtocolMeta = type(_ProtocolMarker)
229
+ del _ProtocolMarker
230
+
231
+
232
+ @dataclass
233
+ class ClassDeclaration:
234
+ cls: Type
235
+ name: str
236
+ module: str
237
+ bases: Tuple[Type, ...]
238
+ generics: Optional[Tuple[str, ...]]
239
+ functions: List[FunctionDeclaration]
240
+
241
+ @staticmethod
242
+ def from_cls(cls) -> 'ClassDeclaration':
243
+ if not isinstance(cls, type):
244
+ raise TypeError('obj must be a Class')
245
+
246
+ parameters: List[str] = []
247
+ if _should_use_runtime_introspection(cls):
248
+ parameters = _extract_generics_from_cls(cls)
249
+ else:
250
+ try:
251
+ src = "\n".join(
252
+ line for line in inspect.getsource(cls).splitlines()
253
+ if not remove_whitespace(line).startswith("@")
254
+ )
255
+ if not _source_matches_class(cls, src):
256
+ raise OSError("source does not match class")
257
+ match = class_pattern.match(src)
258
+ bases = match.group(1).split(",") if match and match.group(1) else []
259
+ for base in bases:
260
+ if '[' in base and ']' in base:
261
+ generic_args = base[base.index('[') + 1:base.rindex(']')]
262
+ for arg in split_args(generic_args):
263
+ if len(arg) == 1:
264
+ parameters.append(arg)
265
+ except (OSError, TypeError):
266
+ parameters = _extract_generics_from_cls(cls)
267
+
268
+ return ClassDeclaration(
269
+ cls,
270
+ cls.__name__,
271
+ cls.__module__,
272
+ getattr(cls, "__orig_bases__", getattr(cls, "__args__", ())),
273
+ tuple(parameters) or None,
274
+ FunctionDeclaration.get_declared_functions(cls),
275
+ )
276
+
277
+ @property
278
+ def is_generic(self) -> bool:
279
+ return self.generics is not None and len(self.generics) > 0
280
+
281
+
282
+ __all__ = [
283
+ "Argument",
284
+ "ClassDeclaration",
285
+ "FunctionDeclaration",
286
+ "get_explicitly_declared_functions",
287
+ "get_mro",
288
+ ]
@@ -0,0 +1 @@
1
+ from .file_reflection import *
@@ -0,0 +1,85 @@
1
+ import inspect
2
+ import logging
3
+ import os
4
+ from typing import Optional, cast
5
+ from types import FrameType
6
+ from ..interpreter.callstack import _get_prev_frame_from
7
+ from ...logging_.utils import get_logger
8
+
9
+ logger = get_logger(__name__)
10
+
11
+
12
+ def get_caller_file_name() -> Optional[str]:
13
+ """return the name of the file that the caller of the
14
+ function that's using this function is in
15
+
16
+ Returns:
17
+ Optional[str]: name of file
18
+ """
19
+ logger.debug("Getting caller file name")
20
+ frame = _get_prev_frame_from(_get_prev_frame_from(inspect.currentframe()))
21
+ if frame is None:
22
+ logger.debug("No caller frame found")
23
+ return None
24
+ frame = cast(FrameType, frame)
25
+ filename = frame.f_code.co_filename
26
+ logger.debug("Caller file name: %s", filename)
27
+ return filename
28
+
29
+
30
+ def get_current_file_path() -> Optional[str]:
31
+ """returns the name of the file that this functions is called from
32
+
33
+ Returns:
34
+ Optional[str]: name of file
35
+ """
36
+ logger.debug("Getting current file path")
37
+ return get_caller_file_name()
38
+
39
+
40
+ def get_current_file_name() -> Optional[str]:
41
+ logger.debug("Getting current file name")
42
+ if (filepath := get_caller_file_name()) is None:
43
+ logger.debug("No file path available")
44
+ return None
45
+ filename = filepath.split('\\')[-1]
46
+ logger.debug("Current file name: %s", filename)
47
+ return filename
48
+
49
+
50
+ def get_current_folder_path() -> Optional[str]:
51
+ logger.debug("Getting current folder path")
52
+ if (filepath := get_caller_file_name()) is None:
53
+ logger.debug("No file path available")
54
+ return None
55
+ folder_path = "\\".join(filepath.split("\\")[:-1])
56
+ logger.debug("Current folder path: %s", folder_path)
57
+ return folder_path
58
+
59
+
60
+ def get_current_folder_name() -> Optional[str]:
61
+ logger.debug("Getting current folder name")
62
+ if (filepath := get_caller_file_name()) is None:
63
+ logger.debug("No file path available")
64
+ return None
65
+ folder_name = filepath.split("\\")[-2]
66
+ logger.debug("Current folder name: %s", folder_name)
67
+ return folder_name
68
+
69
+
70
+ def get_current_directory() -> str:
71
+ """returns the name of the directory of main script"""
72
+ logger.debug("Getting current directory")
73
+ directory = os.path.dirname(os.path.abspath(get_caller_file_name())) # type:ignore # noqa
74
+ logger.debug("Current directory: %s", directory)
75
+ return directory or ""
76
+
77
+
78
+ __all__ = [
79
+ "get_current_file_path",
80
+ "get_current_file_name",
81
+ "get_current_folder_path",
82
+ "get_current_folder_name",
83
+ "get_caller_file_name",
84
+ 'get_current_directory',
85
+ ]
@@ -0,0 +1 @@
1
+ from .function_reflections import *
@@ -0,0 +1,152 @@
1
+ import inspect
2
+ import logging
3
+ from typing import Optional, Callable
4
+ from ..interpreter import get_python_version, get_prev_frame
5
+ from ...logging_.utils import get_logger
6
+
7
+ if get_python_version() < (3, 9):
8
+ from typing import Set as Set # pylint: disable=ungrouped-imports
9
+ else:
10
+ from builtins import set as Set
11
+
12
+ logger = get_logger(__name__)
13
+
14
+
15
+ def get_caller_name(steps_back: int) -> Optional[str]:
16
+ """returns the name caller of the function
17
+
18
+ Returns:
19
+ str: name of caller
20
+
21
+ USING THIS FUNCTION WHILE DEBUGGING WILL ADD ADDITIONAL FRAMES TO THE TRACEBACK
22
+ """
23
+ if not isinstance(steps_back, int):
24
+ logger.error("Invalid steps_back type: %s", type(steps_back))
25
+ raise TypeError("steps_back must be an int")
26
+ if steps_back < 0:
27
+ logger.error("Invalid steps_back value: %s", steps_back)
28
+ raise ValueError("steps_back must be a non-negative integer")
29
+ if (frame := get_prev_frame(2 + steps_back)) is None:
30
+ return None
31
+ caller_name = frame.f_code.co_name
32
+ return caller_name
33
+
34
+
35
+ def get_prev_func(steps_back: int) -> Optional[Callable]:
36
+ """Returns the current function"""
37
+ if steps_back < 0:
38
+ logger.error("Invalid steps_back value: %s", steps_back)
39
+ raise ValueError("n_step must be a non-negative integer")
40
+ if (caller_frame := get_prev_frame(2 + steps_back)) is None:
41
+ return None
42
+ caller_name = caller_frame.f_code.co_name
43
+ if 'self' in caller_frame.f_locals:
44
+ return getattr(caller_frame.f_locals['self'], caller_name)
45
+ return caller_frame.f_globals.get(caller_name, None)
46
+
47
+
48
+ def get_current_func() -> Optional[Callable]:
49
+ """returns the current func
50
+ Example:
51
+ >>> def foo():
52
+ >>> return get_current_func()
53
+
54
+ >>> foo is foo()
55
+ True
56
+ >>> foo is foo()()()
57
+ True
58
+ """
59
+ return get_prev_func(1)
60
+
61
+
62
+ def get_caller() -> Optional[Callable]:
63
+ """returns the caller to the current function
64
+
65
+ Example:
66
+ >>> def foo():
67
+ >>> print(get_caller())
68
+
69
+ >>> def bar():
70
+ >>> foo()
71
+
72
+ >>> bar()
73
+ "bar"
74
+ """
75
+ return get_prev_func(2)
76
+
77
+
78
+ def get_function_return_type(func: Callable, signature: Optional[inspect.Signature] = None) -> Optional[type]:
79
+ """returns the return type of a function
80
+
81
+ Args:
82
+ func (Callable): a function to inquire about
83
+
84
+ Returns:
85
+ Optional[type]: the return type of the function
86
+ """
87
+ if signature is None:
88
+ signature = inspect.signature(func)
89
+ if ("inspect._empty" in str(signature.return_annotation)) or (signature.return_annotation is None):
90
+ return type(None)
91
+ return signature.return_annotation
92
+
93
+
94
+ def is_function_annotated_properly(func: Callable, ignore: Optional[set] = None, check_return: bool = True) -> bool:
95
+ """checks whether a function is annotated properly
96
+
97
+ Args:
98
+ func (Callable): the function to check
99
+ ignore (set, optional): arguments to ignore when validating.
100
+ when 'None' Defaults to {"self", "cls", "args", "kwargs"}.
101
+ check_return (bool, optional): whether to also check that the return value is annotated. Defaults to True
102
+ Raises:
103
+ ValueError: if any of the parameters is of the wrong type
104
+
105
+ Returns:
106
+ bool: result of validation
107
+ """
108
+ from ...functions.isoftype import isoftype
109
+ if not inspect.isfunction(func):
110
+ logger.error("Invalid function type: %s", type(func))
111
+ raise ValueError("param should be a function")
112
+
113
+ if ignore is None:
114
+ ignore = {"self", "cls", "args", "kwargs"}
115
+ if not isoftype(ignore, Set[str]):
116
+ logger.error("Invalid ignore type: %s", type(ignore))
117
+ raise ValueError("ignore must be a set of str")
118
+
119
+ # get the signature of the function
120
+ signature = inspect.signature(func)
121
+ for arg_name, arg_param in signature.parameters.items():
122
+ if arg_name not in ignore:
123
+ arg_type = arg_param.annotation
124
+ # check if an annotation is missing
125
+ if arg_type == inspect.Parameter.empty:
126
+ return False
127
+ # check if the argument has a default value
128
+ default_value = signature.parameters[arg_name].default
129
+ if default_value != inspect.Parameter.empty:
130
+ # allow everything to be set to None as default
131
+ if default_value is None:
132
+ continue
133
+ # if it does, check the type of the default value
134
+ if not isoftype(default_value, arg_type):
135
+ return False
136
+
137
+ return True
138
+
139
+
140
+ def get_source_code(func: Callable):
141
+ return inspect.getsource(func)
142
+
143
+
144
+ __all__ = [
145
+ "get_caller_name",
146
+ 'get_prev_func',
147
+ "get_current_func",
148
+ 'get_caller',
149
+ "get_function_return_type",
150
+ "is_function_annotated_properly",
151
+ "get_source_code"
152
+ ]
@@ -0,0 +1,6 @@
1
+ from .argument_info import *
2
+ from .class_info import *
3
+ from .decorator_info import *
4
+ from .file_info import *
5
+ from .function_info import *
6
+ from .import_info import *