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
|
@@ -0,0 +1,227 @@
|
|
|
1
|
+
import functools
|
|
2
|
+
import inspect
|
|
3
|
+
import logging
|
|
4
|
+
from typing import Callable, get_type_hints, cast, TypeVar, Union
|
|
5
|
+
from ..functions.isoftype import isoftype
|
|
6
|
+
from ..reflection import get_function_return_type
|
|
7
|
+
from ..exceptions import EmptyAnnotationException, \
|
|
8
|
+
InvalidDefaultValueException, ValidationException, InvalidReturnValueException
|
|
9
|
+
from ..versioned_imports import ParamSpec
|
|
10
|
+
from ..logging_.utils import get_logger
|
|
11
|
+
|
|
12
|
+
logger = get_logger(__name__)
|
|
13
|
+
|
|
14
|
+
T = TypeVar("T")
|
|
15
|
+
P = ParamSpec("P")
|
|
16
|
+
FuncT = Callable[P, T] # type:ignore
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def validate(strict: Union[FuncT, bool] = True) -> FuncT:
|
|
20
|
+
"""A decorator that validates the annotations and types of the arguments and return
|
|
21
|
+
value of a function.
|
|
22
|
+
|
|
23
|
+
* 'None' is allowed as default value for everything
|
|
24
|
+
* Because of their wide known use, generally accepted keywords 'self', 'cls', 'args', 'kwargs'
|
|
25
|
+
are not validated.
|
|
26
|
+
|
|
27
|
+
Args:
|
|
28
|
+
func (Callable): The function to be decorated.
|
|
29
|
+
|
|
30
|
+
Raises:
|
|
31
|
+
TypeError: if the decorated object is nto a Callable
|
|
32
|
+
EmptyAnnotationException: If an argument is not annotated.
|
|
33
|
+
InvalidDefaultValueException: If an argument's default value is not of the annotated type.
|
|
34
|
+
ValidationException: If an argument's value is not of the expected type.
|
|
35
|
+
InvalidReturnValueException: If the return value is not of the expected type.
|
|
36
|
+
|
|
37
|
+
Returns:
|
|
38
|
+
Callable: A wrapper function that performs the validation and calls the original function.
|
|
39
|
+
"""
|
|
40
|
+
logger.debug("Creating validate decorator with strict=%s", strict)
|
|
41
|
+
if not isoftype(strict, Union[bool, Callable]):
|
|
42
|
+
logger.error("Invalid strict parameter type: %s", type(strict))
|
|
43
|
+
raise TypeError(
|
|
44
|
+
"the argument for validate must be a Callable or a boolean to mark strict use")
|
|
45
|
+
|
|
46
|
+
def deco(func: FuncT) -> FuncT:
|
|
47
|
+
logger.debug("Applying validate decorator to function %s", func.__name__)
|
|
48
|
+
SKIP_SET = {"self", "cls", "args", "kwargs"}
|
|
49
|
+
if not callable(func):
|
|
50
|
+
logger.error("Object %s is not callable", func)
|
|
51
|
+
raise TypeError(
|
|
52
|
+
"The validate decorator must only decorate a function")
|
|
53
|
+
func_name = f"{func.__module__}.{func.__qualname__}"
|
|
54
|
+
logger.debug("Validating function: %s", func_name)
|
|
55
|
+
# get the signature of the function
|
|
56
|
+
signature = inspect.signature(func)
|
|
57
|
+
for arg_name, arg_param in signature.parameters.items():
|
|
58
|
+
if arg_name not in SKIP_SET:
|
|
59
|
+
arg_type = arg_param.annotation
|
|
60
|
+
# check if an annotation is missing
|
|
61
|
+
if arg_type == inspect.Parameter.empty:
|
|
62
|
+
raise EmptyAnnotationException(
|
|
63
|
+
f"In {func_name}, argument '{arg_name}' is not annotated")
|
|
64
|
+
|
|
65
|
+
# check if the argument has a default value
|
|
66
|
+
default_value = signature.parameters[arg_name].default
|
|
67
|
+
if default_value != inspect.Parameter.empty:
|
|
68
|
+
# allow everything to be set to None as default
|
|
69
|
+
if default_value is None:
|
|
70
|
+
continue
|
|
71
|
+
# if it does, check the type of the default value
|
|
72
|
+
if not isoftype(default_value, arg_type):
|
|
73
|
+
raise InvalidDefaultValueException(
|
|
74
|
+
f"In {func_name}, argument '{arg_name}'s default value is annotated \
|
|
75
|
+
as {arg_type} but got '{default_value}' which is {type(default_value)}")
|
|
76
|
+
|
|
77
|
+
@functools.wraps(func)
|
|
78
|
+
def wrapper(*args, **kwargs):
|
|
79
|
+
"""wrapper function for the type validating - will run on each call independently
|
|
80
|
+
"""
|
|
81
|
+
nonlocal strict
|
|
82
|
+
strict = cast(bool, strict)
|
|
83
|
+
hints = None
|
|
84
|
+
# check all arguments
|
|
85
|
+
bound = signature.bind(*args, **kwargs)
|
|
86
|
+
for variable_name, variable_value in bound.arguments.items():
|
|
87
|
+
if variable_name in SKIP_SET:
|
|
88
|
+
continue
|
|
89
|
+
expected_type = func.__annotations__[variable_name]
|
|
90
|
+
|
|
91
|
+
if isinstance(expected_type, str):
|
|
92
|
+
# why does this even happen?
|
|
93
|
+
if hints is None:
|
|
94
|
+
hints = get_type_hints(func)
|
|
95
|
+
expected_type = hints[variable_name]
|
|
96
|
+
|
|
97
|
+
if not isoftype(variable_value, expected_type, strict=strict):
|
|
98
|
+
raise ValidationException(
|
|
99
|
+
f"In {func_name}, argument '{variable_name}' is annotated as "
|
|
100
|
+
f"{expected_type} but got '{variable_value}' which is {type(variable_value)}")
|
|
101
|
+
|
|
102
|
+
# call the function
|
|
103
|
+
result = func(*args, **kwargs)
|
|
104
|
+
|
|
105
|
+
# check the return type
|
|
106
|
+
return_type = get_function_return_type(func, signature)
|
|
107
|
+
if isinstance(return_type, str):
|
|
108
|
+
# why does this even happen?
|
|
109
|
+
return_type = get_type_hints(func)["return"]
|
|
110
|
+
if return_type is not type(None) and not isoftype(result, return_type):
|
|
111
|
+
raise InvalidReturnValueException(
|
|
112
|
+
f"In function {func_name}, the return type is annotated as "
|
|
113
|
+
f"{return_type} but got '{result}' which is {type(result)}")
|
|
114
|
+
return result
|
|
115
|
+
|
|
116
|
+
return wrapper
|
|
117
|
+
|
|
118
|
+
if callable(strict):
|
|
119
|
+
func = strict
|
|
120
|
+
strict = True
|
|
121
|
+
return deco(func)
|
|
122
|
+
return deco
|
|
123
|
+
|
|
124
|
+
|
|
125
|
+
# def validate(func: Callable) -> Callable:
|
|
126
|
+
# """A decorator that validates the annotations and types of the arguments and return
|
|
127
|
+
# value of a function.
|
|
128
|
+
|
|
129
|
+
# * 'None' is allowed as default value for everything
|
|
130
|
+
# * Because of their use in better_builtins, the generally accepted keywords 'self' and 'cls'
|
|
131
|
+
# are not validated to not break intellisense when using 'Any'
|
|
132
|
+
|
|
133
|
+
# Args:
|
|
134
|
+
# func (Callable): The function to be decorated.
|
|
135
|
+
|
|
136
|
+
# Raises:
|
|
137
|
+
# TypeError: if the decorated object is nto a Callable
|
|
138
|
+
# EmptyAnnotationException: If an argument is not annotated.
|
|
139
|
+
# InvalidDefaultValueException: If an argument's default value is not of the annotated type.
|
|
140
|
+
# ValidationException: If an argument's value is not of the expected type.
|
|
141
|
+
# InvalidReturnValueException: If the return value is not of the expected type.
|
|
142
|
+
|
|
143
|
+
# Returns:
|
|
144
|
+
# Callable: A wrapper function that performs the validation and calls the original function.
|
|
145
|
+
# """
|
|
146
|
+
# SKIP_SET = {"self", "cls"}
|
|
147
|
+
# if not isinstance(func, Callable):
|
|
148
|
+
# raise TypeError("The validate decorator must only decorate a function")
|
|
149
|
+
# func_name = f"{func.__module__}.{func.__qualname__}"
|
|
150
|
+
# # get the signature of the function
|
|
151
|
+
# signature = inspect.signature(func)
|
|
152
|
+
# for arg_name, arg_param in signature.parameters.items():
|
|
153
|
+
# if arg_name not in SKIP_SET:
|
|
154
|
+
# arg_type = arg_param.annotation
|
|
155
|
+
# # check if an annotation is missing
|
|
156
|
+
# if arg_type == inspect.Parameter.empty:
|
|
157
|
+
# raise EmptyAnnotationException(
|
|
158
|
+
# f"In {func_name}, argument '{arg_name}' is not annotated")
|
|
159
|
+
|
|
160
|
+
# # check if the argument has a default value
|
|
161
|
+
# default_value = signature.parameters[arg_name].default
|
|
162
|
+
# if default_value != inspect.Parameter.empty:
|
|
163
|
+
# # allow everything to be set to None as default
|
|
164
|
+
# if default_value is None:
|
|
165
|
+
# continue
|
|
166
|
+
# # if it does, check the type of the default value
|
|
167
|
+
# if not isoftype(default_value, arg_type):
|
|
168
|
+
# raise InvalidDefaultValueException(
|
|
169
|
+
# f"In {func_name}, argument '{arg_name}'s default value is annotated \
|
|
170
|
+
# as {arg_type} but got '{default_value}' which is {type(default_value)}")
|
|
171
|
+
|
|
172
|
+
# @functools.wraps(func)
|
|
173
|
+
# def wrapper(*args, **kwargs):
|
|
174
|
+
# """wrapper function for the type validating - will run on each call independently
|
|
175
|
+
# """
|
|
176
|
+
# hints = None
|
|
177
|
+
# # check all arguments
|
|
178
|
+
# bound = signature.bind(*args, **kwargs)
|
|
179
|
+
# for variable_name, variable_value in bound.arguments.items():
|
|
180
|
+
# if variable_name in SKIP_SET:
|
|
181
|
+
# continue
|
|
182
|
+
# expected_type = func.__annotations__[variable_name]
|
|
183
|
+
|
|
184
|
+
# if isinstance(expected_type, str):
|
|
185
|
+
# # why does this even happen?
|
|
186
|
+
# if hints is None:
|
|
187
|
+
# hints = get_type_hints(func)
|
|
188
|
+
# expected_type = hints[variable_name]
|
|
189
|
+
|
|
190
|
+
# if not isoftype(variable_value, expected_type):
|
|
191
|
+
# raise ValidationException(
|
|
192
|
+
# f"In {func_name}, argument '{variable_name}' is annotated as \
|
|
193
|
+
# {expected_type} but got '{variable_value}' which is {type(variable_value)}")
|
|
194
|
+
|
|
195
|
+
# # call the function
|
|
196
|
+
# result = func(*args, **kwargs)
|
|
197
|
+
|
|
198
|
+
# # check the return type
|
|
199
|
+
# return_type = type(None) if ("inspect._empty" in str(signature.return_annotation)
|
|
200
|
+
# or signature.return_annotation is None) else signature.return_annotation
|
|
201
|
+
# if isinstance(return_type, str):
|
|
202
|
+
# # why does this even happen?
|
|
203
|
+
# return_type = get_type_hints(func)["return"]
|
|
204
|
+
# if return_type is not type(None) and not isoftype(result, return_type):
|
|
205
|
+
# raise InvalidReturnValueException(
|
|
206
|
+
# f"In function {func_name}, the return type is annotated as "
|
|
207
|
+
# f"{return_type} but got '{result}' which is {type(result)}")
|
|
208
|
+
# return result
|
|
209
|
+
# return wrapper
|
|
210
|
+
|
|
211
|
+
# @validate # type:ignore
|
|
212
|
+
# def NotImplemented(func: Callable) -> Callable:
|
|
213
|
+
# """decorator to mark function as not implemented for development purposes
|
|
214
|
+
|
|
215
|
+
# Args:
|
|
216
|
+
# func (Callable): the function to decorate
|
|
217
|
+
# """
|
|
218
|
+
# @ functools.wraps(func)
|
|
219
|
+
# def wrapper(*args, **kwargs) -> Any:
|
|
220
|
+
# raise NotImplementedError(
|
|
221
|
+
# f"As marked by the developer {func.__module__}.{func.__qualname__} is not implemented yet..")
|
|
222
|
+
# return wrapper
|
|
223
|
+
|
|
224
|
+
|
|
225
|
+
__all__ = [
|
|
226
|
+
"validate"
|
|
227
|
+
]
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
|
|
2
|
+
import logging
|
|
3
|
+
|
|
4
|
+
from .logging_.utils import get_logger
|
|
5
|
+
|
|
6
|
+
logger = get_logger(__name__)
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class OverloadException(Exception):
|
|
10
|
+
"""Base exception for overload decorator
|
|
11
|
+
"""
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class OverloadNotFound(OverloadException):
|
|
15
|
+
"""Exception to raise if a function is called with certain
|
|
16
|
+
argument types but this function hasn't been overloaded with those types
|
|
17
|
+
"""
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
class OverloadDuplication(OverloadException):
|
|
21
|
+
"""
|
|
22
|
+
Exception to raise if a function is overloaded twice with same argument types
|
|
23
|
+
"""
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
# class ValidationError(Exception):
|
|
27
|
+
# """_summary_
|
|
28
|
+
|
|
29
|
+
# Args:
|
|
30
|
+
# Exception (_type_): _description_
|
|
31
|
+
|
|
32
|
+
# Returns:
|
|
33
|
+
# _type_: _description_
|
|
34
|
+
# """
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
# class ValidationTypeError(ValidationError, TypeError):
|
|
38
|
+
# pass
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
# class ValidationReturnTypeError(ValidationError, TypeError):
|
|
42
|
+
# pass
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
# class ValidationValueError(ValidationError, ValueError):
|
|
46
|
+
# pass
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
# class ValidationDuplicationError(ValidationError):
|
|
50
|
+
# pass
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
class ValidationException(Exception):
|
|
54
|
+
"""generic validation exception for validate decorator
|
|
55
|
+
"""
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
class EmptyAnnotationException(ValidationException):
|
|
59
|
+
"""exception for validate decorator to be used if a function needs to
|
|
60
|
+
be validated but has an empty annotation for one of the arguments
|
|
61
|
+
"""
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
class InvalidDefaultValueException(ValidationException):
|
|
65
|
+
"""exception for validate decorator to be used if a function has
|
|
66
|
+
a default value that doesn't conform to the annotated type
|
|
67
|
+
"""
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
class InvalidReturnValueException(ValidationException):
|
|
71
|
+
"""exception for validate decorator to be used if a function has
|
|
72
|
+
an annotated return value but the actual return value doesn't conform to it
|
|
73
|
+
"""
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
class PrintCatchOne:
|
|
77
|
+
"""a utility class to be used with a "with" block to print
|
|
78
|
+
any exception that happens inside of it rather than exiting the program
|
|
79
|
+
"""
|
|
80
|
+
|
|
81
|
+
def __enter__(self):
|
|
82
|
+
return self
|
|
83
|
+
|
|
84
|
+
def __exit__(self, cls, instance, traceback):
|
|
85
|
+
if instance:
|
|
86
|
+
logger.error("Exception caught in PrintCatchOne: %s %s", cls, instance)
|
|
87
|
+
from .colors import error # pylint: disable=cyclic-import
|
|
88
|
+
error(f"{cls} {instance}")
|
|
89
|
+
return isinstance(instance, cls)
|
|
90
|
+
return False
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
from .isoftype import *
|
|
2
|
+
from .areoneof import *
|
|
3
|
+
from .check_foreach import *
|
|
4
|
+
from .types_subseteq import *
|
|
5
|
+
from .isoneof import *
|
|
6
|
+
from .powerset import *
|
|
7
|
+
from .multiloop import *
|
|
8
|
+
from .flatten import *
|
|
9
|
+
from .subseteq import *
|
|
10
|
+
from .parallel_for import *
|
|
11
|
+
from .foreach import *
|
|
12
|
+
from .factorial import *
|
|
13
|
+
from .partition import *
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import logging
|
|
2
|
+
from typing import Sequence, Any, Union
|
|
3
|
+
from .isoneof import isoneof
|
|
4
|
+
from ..reflection import get_python_version
|
|
5
|
+
from ..logging_.utils import get_logger
|
|
6
|
+
|
|
7
|
+
logger = get_logger(__name__)
|
|
8
|
+
if get_python_version() < (3, 9):
|
|
9
|
+
from typing import List as List, Tuple as Tuple
|
|
10
|
+
else:
|
|
11
|
+
from builtins import list as List, tuple as Tuple # type:ignore
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def areoneof(values: Sequence[Any], types: Union[List[type], Tuple[type]]) -> bool:
|
|
15
|
+
"""performs 'isoneof(values[0],types) and ... and isoneof(values[...],types)'
|
|
16
|
+
|
|
17
|
+
Args:
|
|
18
|
+
values (Sequence[Any]): Sequence of values
|
|
19
|
+
types (Sequence[Type]): Sequence of types
|
|
20
|
+
|
|
21
|
+
Raises:
|
|
22
|
+
TypeError: if types is not a Sequence
|
|
23
|
+
TypeError: if values is not a Sequence
|
|
24
|
+
|
|
25
|
+
Returns:
|
|
26
|
+
bool: the result of the check
|
|
27
|
+
"""
|
|
28
|
+
logger.debug("Checking if %s values are one of types: %s", len(values), types)
|
|
29
|
+
if not isinstance(types, Sequence):
|
|
30
|
+
logger.error("'types' parameter is not a Sequence")
|
|
31
|
+
raise TypeError("'types' must be of type Sequence")
|
|
32
|
+
if not isinstance(values, Sequence):
|
|
33
|
+
logger.error("'values' parameter is not a Sequence")
|
|
34
|
+
raise TypeError("'values' must be of type Sequence")
|
|
35
|
+
|
|
36
|
+
for i, v in enumerate(values):
|
|
37
|
+
if not isoneof(v, types):
|
|
38
|
+
logger.debug("Value at index %s (%s) is not one of the specified types", i, v)
|
|
39
|
+
return False
|
|
40
|
+
|
|
41
|
+
logger.debug("All values match one of the specified types")
|
|
42
|
+
return True
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
__all__ = [
|
|
46
|
+
"areoneof"
|
|
47
|
+
]
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import logging
|
|
2
|
+
from typing import Sequence, Any, Callable
|
|
3
|
+
from ..logging_.utils import get_logger
|
|
4
|
+
|
|
5
|
+
logger = get_logger(__name__)
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def check_foreach(values: Sequence[Any], condition: Callable[[Any], bool]) -> bool:
|
|
9
|
+
"""
|
|
10
|
+
Check if a condition is true for all values in a sequence.
|
|
11
|
+
|
|
12
|
+
Args:
|
|
13
|
+
values (Sequence[Any]): Values to perform check on
|
|
14
|
+
condition (Callable[[Any], bool]): Condition to check on all values
|
|
15
|
+
|
|
16
|
+
Returns:
|
|
17
|
+
bool: returns True iff condition return True for all values individually
|
|
18
|
+
"""
|
|
19
|
+
logger.debug("Checking condition on %s values", len(values))
|
|
20
|
+
|
|
21
|
+
if not isinstance(values, Sequence):
|
|
22
|
+
logger.warning("Values parameter is not a Sequence")
|
|
23
|
+
return False
|
|
24
|
+
if not callable(condition):
|
|
25
|
+
logger.warning("Condition parameter is not callable")
|
|
26
|
+
return False
|
|
27
|
+
|
|
28
|
+
for i, v in enumerate(values):
|
|
29
|
+
if not condition(v):
|
|
30
|
+
logger.debug("Condition failed at index %s with value: %s", i, v)
|
|
31
|
+
return False
|
|
32
|
+
|
|
33
|
+
logger.debug("All values passed the condition check")
|
|
34
|
+
return True
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
__all__ = [
|
|
38
|
+
"check_foreach"
|
|
39
|
+
]
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import logging
|
|
2
|
+
from ..logging_.utils import get_logger
|
|
3
|
+
|
|
4
|
+
logger = get_logger(__name__)
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
def factorial(n: int) -> int:
|
|
8
|
+
logger.info("Computing factorial of %s", n)
|
|
9
|
+
if n < 0:
|
|
10
|
+
logger.error("Factorial not defined for negative numbers: %s", n)
|
|
11
|
+
raise ValueError("Factorial not defined for negative numbers")
|
|
12
|
+
if n == 0 or n == 1:
|
|
13
|
+
return 1
|
|
14
|
+
|
|
15
|
+
res = 1
|
|
16
|
+
for i in range(2, n + 1):
|
|
17
|
+
res *= i
|
|
18
|
+
|
|
19
|
+
logger.info("Factorial of %s computed: %s", n, res)
|
|
20
|
+
return res
|
|
21
|
+
|
|
22
|
+
__all__ = [
|
|
23
|
+
"factorial"
|
|
24
|
+
]
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import logging
|
|
2
|
+
from typing import Iterable
|
|
3
|
+
from ..logging_.utils import get_logger
|
|
4
|
+
|
|
5
|
+
logger = get_logger(__name__)
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def flatten(iterable: Iterable) -> list:
|
|
9
|
+
"""
|
|
10
|
+
Flattens a given iterable into a list.
|
|
11
|
+
|
|
12
|
+
This function takes as input an iterable that may contain nested iterables (like lists or tuples),
|
|
13
|
+
and returns a flat list where all elements of the input are expanded.
|
|
14
|
+
Non-iterable elements in the input iterable are appended as they are.
|
|
15
|
+
|
|
16
|
+
Args:
|
|
17
|
+
iterable (Iterable): The iterable to flatten. Can contain nested iterables.
|
|
18
|
+
|
|
19
|
+
Returns:
|
|
20
|
+
list: A flat list containing all elements of the input iterable.
|
|
21
|
+
"""
|
|
22
|
+
result = []
|
|
23
|
+
for i in iterable:
|
|
24
|
+
if isinstance(i, Iterable):
|
|
25
|
+
result.extend(flatten(i))
|
|
26
|
+
else:
|
|
27
|
+
result.append(i)
|
|
28
|
+
|
|
29
|
+
return result
|
|
30
|
+
|
|
31
|
+
__all__=[
|
|
32
|
+
"flatten"
|
|
33
|
+
]
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import logging
|
|
2
|
+
from typing import TypeVar, Iterable
|
|
3
|
+
from ..custom_types import Consumer
|
|
4
|
+
from ..logging_.utils import get_logger
|
|
5
|
+
|
|
6
|
+
logger = get_logger(__name__)
|
|
7
|
+
|
|
8
|
+
T = TypeVar('T')
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def foreach(iterable: Iterable[T], consumer: Consumer[T]) -> None:
|
|
12
|
+
logger.info("Applying consumer to iterable")
|
|
13
|
+
for v in iterable:
|
|
14
|
+
consumer(v)
|
|
15
|
+
logger.info("Foreach operation completed")
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
__all__ = [
|
|
19
|
+
'foreach'
|
|
20
|
+
]
|