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.
- danielutils/__init__.py +50 -0
- danielutils/abstractions/__init__.py +3 -0
- danielutils/abstractions/db/__init__.py +10 -0
- danielutils/abstractions/db/database.py +206 -0
- danielutils/abstractions/db/database_definitions.py +219 -0
- danielutils/abstractions/db/database_exceptions.py +34 -0
- danielutils/abstractions/db/database_factory.py +70 -0
- danielutils/abstractions/db/database_initializer.py +261 -0
- danielutils/abstractions/db/database_models.py +213 -0
- danielutils/abstractions/db/dependencies.py +6 -0
- danielutils/abstractions/db/implementations/__init__.py +4 -0
- danielutils/abstractions/db/implementations/in_memory_database.py +365 -0
- danielutils/abstractions/db/implementations/persistent_in_memory_database.py +174 -0
- danielutils/abstractions/db/implementations/redis_database.py +403 -0
- danielutils/abstractions/db/implementations/sqlite_database.py +491 -0
- danielutils/abstractions/multiprogramming/__init__.py +3 -0
- danielutils/abstractions/multiprogramming/multi_id.py +26 -0
- danielutils/abstractions/multiprogramming/worker.py +104 -0
- danielutils/abstractions/multiprogramming/worker_pool.py +98 -0
- danielutils/abstractions/repl.py +125 -0
- danielutils/aliases.py +174 -0
- danielutils/async_/__init__.py +7 -0
- danielutils/async_/async_cmd.py +28 -0
- danielutils/async_/async_command.py +879 -0
- danielutils/async_/async_layered_command.py +125 -0
- danielutils/async_/async_retry_executor.py +86 -0
- danielutils/async_/async_worker_pool.py +110 -0
- danielutils/async_/time_strategy.py +73 -0
- danielutils/async_/utils.py +57 -0
- danielutils/better_builtins/__init__.py +3 -0
- danielutils/better_builtins/counter.py +48 -0
- danielutils/better_builtins/frange.py +266 -0
- danielutils/better_builtins/typed_builtins/__init__.py +4 -0
- danielutils/better_builtins/typed_builtins/factory.py +126 -0
- danielutils/better_builtins/typed_builtins/tdict.py +36 -0
- danielutils/better_builtins/typed_builtins/tlist.py +223 -0
- danielutils/better_builtins/typed_builtins/tset.py +67 -0
- danielutils/better_builtins/typed_builtins/ttuple.py +15 -0
- danielutils/colors.py +203 -0
- danielutils/context_managers/__init__.py +5 -0
- danielutils/context_managers/attr_context.py +40 -0
- danielutils/context_managers/multi_context.py +34 -0
- danielutils/context_managers/optional_context.py +33 -0
- danielutils/context_managers/state_context.py +25 -0
- danielutils/context_managers/temporary_file.py +108 -0
- danielutils/convenience.py +12 -0
- danielutils/conversions/__init__.py +2 -0
- danielutils/conversions/main_conversions.py +131 -0
- danielutils/conversions/specialized_conversions/__init__.py +2 -0
- danielutils/conversions/specialized_conversions/to_hex.py +25 -0
- danielutils/conversions/specialized_conversions/to_int.py +19 -0
- danielutils/custom_types.py +18 -0
- danielutils/data_structures/__init__.py +8 -0
- danielutils/data_structures/algorithms.py +52 -0
- danielutils/data_structures/comparer.py +47 -0
- danielutils/data_structures/default_dict.py +23 -0
- danielutils/data_structures/functions.py +35 -0
- danielutils/data_structures/graph/__init__.py +4 -0
- danielutils/data_structures/graph/binary_node.py +71 -0
- danielutils/data_structures/graph/graph.py +208 -0
- danielutils/data_structures/graph/multinode.py +101 -0
- danielutils/data_structures/graph/node.py +60 -0
- danielutils/data_structures/heap/__init__.py +3 -0
- danielutils/data_structures/heap/heap.py +119 -0
- danielutils/data_structures/heap/max_heap.py +18 -0
- danielutils/data_structures/heap/min_heap.py +18 -0
- danielutils/data_structures/queue/__init__.py +3 -0
- danielutils/data_structures/queue/atomic_queue.py +15 -0
- danielutils/data_structures/queue/priority_queue.py +110 -0
- danielutils/data_structures/queue/queue.py +92 -0
- danielutils/data_structures/stack.py +102 -0
- danielutils/data_structures/trees/__init__.py +2 -0
- danielutils/data_structures/trees/binary_syntax_tree.py +84 -0
- danielutils/data_structures/trees/binary_tree.py +63 -0
- danielutils/date.py +43 -0
- danielutils/date_time.py +23 -0
- danielutils/decorators/__init__.py +18 -0
- danielutils/decorators/atomic.py +45 -0
- danielutils/decorators/attach.py +61 -0
- danielutils/decorators/chain_decorators.py +39 -0
- danielutils/decorators/decorate_conditionally.py +32 -0
- danielutils/decorators/delay_call.py +47 -0
- danielutils/decorators/deprecate.py +46 -0
- danielutils/decorators/final.py +39 -0
- danielutils/decorators/limit_recursion.py +63 -0
- danielutils/decorators/memo.py +68 -0
- danielutils/decorators/normalize_decorator.py +55 -0
- danielutils/decorators/overload.py +204 -0
- danielutils/decorators/partially_implemented.py +32 -0
- danielutils/decorators/processify.py +74 -0
- danielutils/decorators/property.py +42 -0
- danielutils/decorators/singleton.py +59 -0
- danielutils/decorators/threadify.py +40 -0
- danielutils/decorators/timeout.py +82 -0
- danielutils/decorators/total_ordering.py +82 -0
- danielutils/decorators/validate.py +227 -0
- danielutils/exceptions.py +90 -0
- danielutils/functions/__init__.py +13 -0
- danielutils/functions/areoneof.py +47 -0
- danielutils/functions/check_foreach.py +39 -0
- danielutils/functions/factorial.py +24 -0
- danielutils/functions/flatten.py +33 -0
- danielutils/functions/foreach.py +20 -0
- danielutils/functions/isoftype.py +475 -0
- danielutils/functions/isoneof.py +71 -0
- danielutils/functions/multiloop.py +51 -0
- danielutils/functions/parallel_for.py +47 -0
- danielutils/functions/partition.py +45 -0
- danielutils/functions/powerset.py +31 -0
- danielutils/functions/subseteq.py +25 -0
- danielutils/functions/types_subseteq.py +51 -0
- danielutils/generators/__init__.py +3 -0
- danielutils/generators/conditional_generator.py +59 -0
- danielutils/generators/generator_from_stream.py +31 -0
- danielutils/generators/join_generators.py +109 -0
- danielutils/internet.py +92 -0
- danielutils/io_.py +451 -0
- danielutils/java/__init__.py +18 -0
- danielutils/java/interfaces/__init__.py +1 -0
- danielutils/java/interfaces/comparable.py +31 -0
- danielutils/java/java_interface.py +59 -0
- danielutils/logging_/__init__.py +2 -0
- danielutils/logging_/_impl/__init__.py +4 -0
- danielutils/logging_/_impl/builtin_impls/__init__.py +2 -0
- danielutils/logging_/_impl/builtin_impls/file_logger.py +35 -0
- danielutils/logging_/_impl/builtin_impls/print_logger.py +21 -0
- danielutils/logging_/_impl/log_level.py +13 -0
- danielutils/logging_/_impl/logger.py +109 -0
- danielutils/logging_/_impl/logger_strategy_impl_base.py +37 -0
- danielutils/logging_/utils.py +199 -0
- danielutils/lombok/__init__.py +1 -0
- danielutils/lombok/builder.py +68 -0
- danielutils/math_/__init__.py +2 -0
- danielutils/math_/constants.py +103 -0
- danielutils/math_/functions.py +34 -0
- danielutils/math_/math_print.py +52 -0
- danielutils/math_/math_symbols.py +125 -0
- danielutils/metaclasses/__init__.py +5 -0
- danielutils/metaclasses/atomic_class_meta.py +36 -0
- danielutils/metaclasses/implicit_data_deleter_meta.py +87 -0
- danielutils/metaclasses/instance_cache_meta.py +66 -0
- danielutils/metaclasses/interface.py +328 -0
- danielutils/metaclasses/overload_meta.py +48 -0
- danielutils/mock_/__init__.py +1 -0
- danielutils/mock_/mock_module.py +27 -0
- danielutils/path.py +55 -0
- danielutils/print_.py +123 -0
- danielutils/progress_bar/__init__.py +3 -0
- danielutils/progress_bar/ascii_progress_bar.py +161 -0
- danielutils/progress_bar/progress_bar.py +75 -0
- danielutils/progress_bar/progress_bar_pool.py +72 -0
- danielutils/protocols/__init__.py +3 -0
- danielutils/protocols/dictable.py +38 -0
- danielutils/protocols/evaluable.py +26 -0
- danielutils/protocols/serializable.py +51 -0
- danielutils/py.typed +0 -0
- danielutils/random_.py +47 -0
- danielutils/reflection/__init__.py +6 -0
- danielutils/reflection/declarations.py +288 -0
- danielutils/reflection/file/__init__.py +1 -0
- danielutils/reflection/file/file_reflection.py +85 -0
- danielutils/reflection/function/__init__.py +1 -0
- danielutils/reflection/function/function_reflections.py +152 -0
- danielutils/reflection/info_classes/__init__.py +6 -0
- danielutils/reflection/info_classes/argument_info.py +278 -0
- danielutils/reflection/info_classes/class_info.py +204 -0
- danielutils/reflection/info_classes/decorator_info.py +54 -0
- danielutils/reflection/info_classes/file_info.py +1180 -0
- danielutils/reflection/info_classes/folder_info.py +592 -0
- danielutils/reflection/info_classes/function_info.py +551 -0
- danielutils/reflection/info_classes/import_info.py +207 -0
- danielutils/reflection/interpreter/__init__.py +9 -0
- danielutils/reflection/interpreter/callstack.py +57 -0
- danielutils/reflection/interpreter/get_traceback.py +27 -0
- danielutils/reflection/interpreter/interpreter.py +41 -0
- danielutils/reflection/interpreter/is_debugging.py +13 -0
- danielutils/reflection/interpreter/os_.py +33 -0
- danielutils/reflection/interpreter/packages.py +33 -0
- danielutils/reflection/interpreter/python_version.py +33 -0
- danielutils/reflection/interpreter/signals.py +18 -0
- danielutils/reflection/interpreter/tracer.py +175 -0
- danielutils/reflection/module/__init__.py +3 -0
- danielutils/reflection/module/lazy_module.py +104 -0
- danielutils/reflection/module/module_reflections.py +34 -0
- danielutils/reflection/module/package_reflection.py +161 -0
- danielutils/retry_executor/__init__.py +3 -0
- danielutils/retry_executor/backoff_strategies/__init__.py +6 -0
- danielutils/retry_executor/backoff_strategies/constant_backoff.py +25 -0
- danielutils/retry_executor/backoff_strategies/exponential_backoff.py +26 -0
- danielutils/retry_executor/backoff_strategies/functional_backoff.py +29 -0
- danielutils/retry_executor/backoff_strategies/linear_backoff.py +23 -0
- danielutils/retry_executor/backoff_strategies/multiplicative_backoff.py +23 -0
- danielutils/retry_executor/backoff_strategies/no_backoff.py +17 -0
- danielutils/retry_executor/backoff_strategy.py +28 -0
- danielutils/retry_executor/retry_executor.py +55 -0
- danielutils/snippets/__init__.py +1 -0
- danielutils/snippets/try_get.py +28 -0
- danielutils/system/__init__.py +3 -0
- danielutils/system/independent.py +197 -0
- danielutils/system/layered_command.py +130 -0
- danielutils/system/windows/__init__.py +2 -0
- danielutils/system/windows/utils/__init__.py +1 -0
- danielutils/system/windows/utils/filetime.py +19 -0
- danielutils/system/windows/win32_ctime.py +183 -0
- danielutils/system/windows/windows.py +78 -0
- danielutils/testing/__init__.py +1 -0
- danielutils/testing/unittest_/__init__.py +2 -0
- danielutils/testing/unittest_/always_teardown_testcase.py +75 -0
- danielutils/testing/unittest_/auto_cwd_testcase.py +114 -0
- danielutils/text.py +169 -0
- danielutils/time.py +64 -0
- danielutils/tools/__init__.py +1 -0
- danielutils/tools/unittest_test_runner/__init__.py +9 -0
- danielutils/tools/unittest_test_runner/cli.py +70 -0
- danielutils/tools/unittest_test_runner/core/__init__.py +17 -0
- danielutils/tools/unittest_test_runner/core/discovery.py +322 -0
- danielutils/tools/unittest_test_runner/core/execution.py +244 -0
- danielutils/tools/unittest_test_runner/core/parser.py +163 -0
- danielutils/tools/unittest_test_runner/core/runner.py +561 -0
- danielutils/tools/unittest_test_runner/models.py +82 -0
- danielutils/university/__init__.py +7 -0
- danielutils/university/computability_and_complexity/__init__.py +2 -0
- danielutils/university/computability_and_complexity/discreate_finite_automaton.py +89 -0
- danielutils/university/computability_and_complexity/languages/__init__.py +0 -0
- danielutils/university/computability_and_complexity/languages/language.py +17 -0
- danielutils/university/computability_and_complexity/languages/sat.py +71 -0
- danielutils/university/computability_and_complexity/turing_machine.py +47 -0
- danielutils/university/databases/__init__.py +1 -0
- danielutils/university/databases/all.py +756 -0
- danielutils/university/image_proccesing/__init__.py +1 -0
- danielutils/university/image_proccesing/encoding/__init__.py +3 -0
- danielutils/university/image_proccesing/encoding/encoding.py +35 -0
- danielutils/university/image_proccesing/encoding/lossless/__init__.py +4 -0
- danielutils/university/image_proccesing/encoding/lossless/huffman.py +0 -0
- danielutils/university/image_proccesing/encoding/lossless/lossless_encoding.py +10 -0
- danielutils/university/image_proccesing/encoding/lossless/lzw.py +0 -0
- danielutils/university/image_proccesing/encoding/lossless/run_length.py +22 -0
- danielutils/university/image_proccesing/encoding/lossy/__init__.py +1 -0
- danielutils/university/image_proccesing/encoding/lossy/lossy_encoding.py +10 -0
- danielutils/university/image_proccesing/tansformations/__init__.py +0 -0
- danielutils/university/image_proccesing/tansformations/gaussian.py +0 -0
- danielutils/university/image_proccesing/tansformations/gradient.py +0 -0
- danielutils/university/image_proccesing/tansformations/hough.py +0 -0
- danielutils/university/image_proccesing/tansformations/laplacian.py +0 -0
- danielutils/university/image_proccesing/tansformations/transformation.py +11 -0
- danielutils/university/linear_algebra/__init__.py +1 -0
- danielutils/university/linear_algebra/matrix.py +404 -0
- danielutils/university/machine_learning/__init__.py +0 -0
- danielutils/university/machine_learning/activation_functions/__init__.py +2 -0
- danielutils/university/machine_learning/activation_functions/activation_function.py +11 -0
- danielutils/university/machine_learning/activation_functions/relu.py +11 -0
- danielutils/university/machine_learning/neuron.py +15 -0
- danielutils/university/oop/__init__.py +1 -0
- danielutils/university/oop/observer.py +31 -0
- danielutils/university/oop/strategy.py +9 -0
- danielutils/university/probability/__init__.py +7 -0
- danielutils/university/probability/conditional_variable/__init__.py +3 -0
- danielutils/university/probability/conditional_variable/conditional_variable.py +138 -0
- danielutils/university/probability/conditional_variable/continuous/__init__.py +0 -0
- danielutils/university/probability/conditional_variable/discrete/__init__.py +8 -0
- danielutils/university/probability/conditional_variable/discrete/bernoulli.py +38 -0
- danielutils/university/probability/conditional_variable/discrete/bernoulli_sum.py +31 -0
- danielutils/university/probability/conditional_variable/discrete/binomial.py +40 -0
- danielutils/university/probability/conditional_variable/discrete/conditional_from_discrete_probability_func.py +36 -0
- danielutils/university/probability/conditional_variable/discrete/discrete.py +42 -0
- danielutils/university/probability/conditional_variable/discrete/geometric.py +43 -0
- danielutils/university/probability/conditional_variable/discrete/poisson.py +32 -0
- danielutils/university/probability/conditional_variable/discrete/uniform.py +33 -0
- danielutils/university/probability/distributions.py +35 -0
- danielutils/university/probability/expressions/__init__.py +2 -0
- danielutils/university/probability/expressions/accumulation_expression.py +291 -0
- danielutils/university/probability/expressions/probability_expression.py +110 -0
- danielutils/university/probability/funcs/__init__.py +4 -0
- danielutils/university/probability/funcs/covariance.py +12 -0
- danielutils/university/probability/funcs/expected_value.py +41 -0
- danielutils/university/probability/funcs/probability_function.py +14 -0
- danielutils/university/probability/funcs/variance.py +17 -0
- danielutils/university/probability/operator.py +79 -0
- danielutils/university/probability/protocols.py +32 -0
- danielutils/university/probability/supp.py +101 -0
- danielutils/university/probability/transformation.py +44 -0
- danielutils/versioned_imports.py +31 -0
- danielutils-1.1.28.dist-info/METADATA +390 -0
- danielutils-1.1.28.dist-info/RECORD +287 -0
- danielutils-1.1.28.dist-info/WHEEL +5 -0
- danielutils-1.1.28.dist-info/licenses/LICENSE +21 -0
- 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,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
|
+
]
|