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,114 @@
|
|
|
1
|
+
import asyncio
|
|
2
|
+
import functools
|
|
3
|
+
import logging
|
|
4
|
+
import os
|
|
5
|
+
import unittest
|
|
6
|
+
from inspect import iscoroutinefunction
|
|
7
|
+
from typing import Callable, Type, Coroutine, Union, Any
|
|
8
|
+
|
|
9
|
+
from ...random_ import RandomDataGenerator
|
|
10
|
+
from ...io_ import create_directory, delete_directory, directory_exists
|
|
11
|
+
from ...path import get_current_working_directory, set_current_working_directory
|
|
12
|
+
from ...logging_.utils import get_logger
|
|
13
|
+
|
|
14
|
+
logger = get_logger(__name__)
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def dispatch_function(func: Union[Callable, Coroutine], *args, **kwargs) -> Any:
|
|
18
|
+
if asyncio.iscoroutinefunction(func):
|
|
19
|
+
return asyncio.run(func(*args, **kwargs))
|
|
20
|
+
elif asyncio.iscoroutine(func):
|
|
21
|
+
return asyncio.run(func)
|
|
22
|
+
else:
|
|
23
|
+
return func(*args, **kwargs)
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def get_available_folder_name(prefix_path: str, random_suffix_length: int = 15) -> str:
|
|
27
|
+
res = f"{prefix_path}_{RandomDataGenerator.name(random_suffix_length)}"
|
|
28
|
+
while directory_exists(res):
|
|
29
|
+
res = f"{prefix_path}_{RandomDataGenerator.name(random_suffix_length)}"
|
|
30
|
+
return res
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def improved_setup(func: Callable) -> Callable:
|
|
34
|
+
@functools.wraps(func)
|
|
35
|
+
def wrapper(self):
|
|
36
|
+
self.cwd = get_available_folder_name(f"./{self.__class__.__name__}_test_folder")
|
|
37
|
+
logger.debug("Creating test directory: %s", self.cwd)
|
|
38
|
+
create_directory(self.cwd)
|
|
39
|
+
self.prev_cwd = get_current_working_directory()
|
|
40
|
+
set_current_working_directory(os.path.join(self.prev_cwd, self.cwd))
|
|
41
|
+
logger.debug("Changed working directory to: %s", self.cwd)
|
|
42
|
+
if func is not None:
|
|
43
|
+
func(self)
|
|
44
|
+
|
|
45
|
+
@functools.wraps(func)
|
|
46
|
+
async def async_wrapper(self):
|
|
47
|
+
self.cwd = get_available_folder_name(f"./{self.__class__.__name__}_test_folder")
|
|
48
|
+
logger.debug("Creating async test directory: %s", self.cwd)
|
|
49
|
+
create_directory(self.cwd)
|
|
50
|
+
self.prev_cwd = get_current_working_directory()
|
|
51
|
+
set_current_working_directory(os.path.join(self.prev_cwd, self.cwd))
|
|
52
|
+
logger.debug("Changed working directory to: %s", self.cwd)
|
|
53
|
+
if func is not None:
|
|
54
|
+
await func(self)
|
|
55
|
+
|
|
56
|
+
return async_wrapper if iscoroutinefunction(func) else wrapper
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
def improved_teardown(func: Callable) -> Callable:
|
|
60
|
+
@functools.wraps(func)
|
|
61
|
+
def wrapper(self):
|
|
62
|
+
if func is not None:
|
|
63
|
+
func(self)
|
|
64
|
+
logger.debug("Restoring working directory to: %s", self.prev_cwd)
|
|
65
|
+
set_current_working_directory(self.prev_cwd)
|
|
66
|
+
logger.debug("Cleaning up test directory: %s", self.cwd)
|
|
67
|
+
delete_directory(self.cwd)
|
|
68
|
+
|
|
69
|
+
@functools.wraps(func)
|
|
70
|
+
async def async_wrapper(self):
|
|
71
|
+
if func is not None:
|
|
72
|
+
await func(self)
|
|
73
|
+
logger.debug("Restoring working directory to: %s", self.prev_cwd)
|
|
74
|
+
set_current_working_directory(self.prev_cwd)
|
|
75
|
+
logger.debug("Cleaning up async test directory: %s", self.cwd)
|
|
76
|
+
delete_directory(self.cwd)
|
|
77
|
+
|
|
78
|
+
return async_wrapper if iscoroutinefunction(func) else wrapper
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
class AutoCWDTestCase(unittest.TestCase):
|
|
82
|
+
@staticmethod
|
|
83
|
+
def _dummy(*args, **kwargs) -> None:
|
|
84
|
+
pass
|
|
85
|
+
|
|
86
|
+
@classmethod
|
|
87
|
+
def __init_subclass__(cls: Type, **kwargs) -> None:
|
|
88
|
+
logger.debug("Initializing AutoCWDTestCase subclass: %s", cls.__name__)
|
|
89
|
+
dct = dict(cls.__dict__)
|
|
90
|
+
impl_setUp = dct.get("setUp", cls._dummy)
|
|
91
|
+
impl_tearDown = dct.get("tearDown", cls._dummy)
|
|
92
|
+
setattr(cls, "setUp", improved_setup(impl_setUp))
|
|
93
|
+
setattr(cls, "tearDown", improved_teardown(impl_tearDown))
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
class AsyncAutoCWDTestCase(unittest.IsolatedAsyncioTestCase):
|
|
97
|
+
@staticmethod
|
|
98
|
+
async def _async_dummy(*args, **kwargs) -> None:
|
|
99
|
+
pass
|
|
100
|
+
|
|
101
|
+
@classmethod
|
|
102
|
+
def __init_subclass__(cls: Type, **kwargs) -> None:
|
|
103
|
+
logger.debug("Initializing AsyncAutoCWDTestCase subclass: %s", cls.__name__)
|
|
104
|
+
dct = dict(cls.__dict__)
|
|
105
|
+
async_impl_setUp = dct.get("asyncSetUp", cls._async_dummy)
|
|
106
|
+
async_impl_tearDown = dct.get("asyncTearDown", cls._async_dummy)
|
|
107
|
+
setattr(cls, "asyncSetUp", improved_setup(async_impl_setUp))
|
|
108
|
+
setattr(cls, "asyncTearDown", improved_teardown(async_impl_tearDown))
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
__all__ = [
|
|
112
|
+
'AutoCWDTestCase',
|
|
113
|
+
"AsyncAutoCWDTestCase"
|
|
114
|
+
]
|
danielutils/text.py
ADDED
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
import logging
|
|
3
|
+
from typing import Union
|
|
4
|
+
from .decorators.validate import validate
|
|
5
|
+
from .functions.check_foreach import check_foreach
|
|
6
|
+
from .logging_.utils import get_logger
|
|
7
|
+
|
|
8
|
+
logger = get_logger(__name__)
|
|
9
|
+
HEBREW_LETTERS = ['\u05D0', '\u2135', '\uFB21', '\uFB2E', '\uFB2F',
|
|
10
|
+
'\uFB30', '\uFB4F', '\u05D1', '\u2136', '\uFB31',
|
|
11
|
+
'\uFB4C', '\u05D2', '\u2137', '\uFB32', '\u05D3',
|
|
12
|
+
'\u2138', '\uFB22', '\uFB33', '\u05D4', '\uFB23',
|
|
13
|
+
'\uFB34', '\u05D5', '\uFB4B', '\uFB35', '\u05F0',
|
|
14
|
+
'\u05F1', '\u05D6', '\uFB36', '\u05D7', '\u05D8',
|
|
15
|
+
'\uFB38', '\u05D9', '\uFB1D', '\uFB39', '\u05EF',
|
|
16
|
+
'\u05F2', '\uFB1F', '\u05DB', '\uFB24', '\u05DA',
|
|
17
|
+
'\uFB3B', '\uFB3A', '\uFB4D', '\u05DC', '\uFB25',
|
|
18
|
+
'\uFB3C', '\u05DE', '\uFB26', '\u05DD', '\uFB3E',
|
|
19
|
+
'\u05E0', '\u05DF', '\uFB40', '\u05E1', '\uFB41',
|
|
20
|
+
'\u05E2', '\uFB20', '\u05E4', '\u05E3', '\uFB44',
|
|
21
|
+
'\uFB43', '\uFB4E', '\u05E6', '\u05E5', '\uFB46',
|
|
22
|
+
'\u05E7', '\uFB47', '\u05E8', '\uFB27', '\uFB48',
|
|
23
|
+
'\u05E9', '\uFB2B', '\uFB2A', '\uFB49', '\uFB2D',
|
|
24
|
+
'\uFB2C', '\u05EA', '\uFB28', '\uFB4A']
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
HEBREW_LETTERS_DEC = [ord(v) for v in HEBREW_LETTERS]
|
|
28
|
+
HEBREW_LETTERS_HEX = [hex(v) for v in HEBREW_LETTERS_DEC]
|
|
29
|
+
ENGLISH_LETTERS = [chr(v) for v in range(65, 91)]+[chr(v)
|
|
30
|
+
for v in range(97, 123)]
|
|
31
|
+
ENGLISH_LETTERS_DEC = [ord(v) for v in ENGLISH_LETTERS]
|
|
32
|
+
ENGLISH_LETTERS_HEX = [hex(v) for v in ENGLISH_LETTERS_DEC]
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
@validate # type:ignore
|
|
36
|
+
def is_english(s: str) -> bool:
|
|
37
|
+
"""returns whether the specified string is in the english language
|
|
38
|
+
|
|
39
|
+
Args:
|
|
40
|
+
s (str): the string to check
|
|
41
|
+
|
|
42
|
+
Returns:
|
|
43
|
+
bool: returns true if the string is in english
|
|
44
|
+
"""
|
|
45
|
+
return check_foreach(s, lambda c: c in ENGLISH_LETTERS)
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
@validate # type:ignore
|
|
49
|
+
def is_number(s: str) -> bool:
|
|
50
|
+
"""checks if a string is a number
|
|
51
|
+
|
|
52
|
+
Args:
|
|
53
|
+
text (str): string to check
|
|
54
|
+
|
|
55
|
+
Returns:
|
|
56
|
+
bool: true if string is a number
|
|
57
|
+
"""
|
|
58
|
+
return s.isnumeric()
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
@validate # type:ignore
|
|
62
|
+
def is_int(num: Union[int, float]) -> bool:
|
|
63
|
+
"""_summary_
|
|
64
|
+
|
|
65
|
+
Args:
|
|
66
|
+
num (Union[int, float]): is a number an int
|
|
67
|
+
|
|
68
|
+
Returns:
|
|
69
|
+
bool: return true if num is a while number
|
|
70
|
+
"""
|
|
71
|
+
if isinstance(num, int):
|
|
72
|
+
return True
|
|
73
|
+
|
|
74
|
+
return int(num) == num
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
@validate # type:ignore
|
|
78
|
+
def is_float(s: str) -> bool:
|
|
79
|
+
"""checks whether a string has a float value
|
|
80
|
+
|
|
81
|
+
Args:
|
|
82
|
+
s (str): string to check
|
|
83
|
+
|
|
84
|
+
Returns:
|
|
85
|
+
bool: result
|
|
86
|
+
"""
|
|
87
|
+
try:
|
|
88
|
+
float(s)
|
|
89
|
+
logger.debug("String '%s' is a valid float", s)
|
|
90
|
+
return True
|
|
91
|
+
except ValueError:
|
|
92
|
+
logger.debug("String '%s' is not a valid float", s)
|
|
93
|
+
return False
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
@validate # type:ignore
|
|
97
|
+
def is_hebrew(s: str) -> bool:
|
|
98
|
+
"""checks if a string is in hebrew
|
|
99
|
+
|
|
100
|
+
Args:
|
|
101
|
+
text (str): string to check
|
|
102
|
+
|
|
103
|
+
Returns:
|
|
104
|
+
bool: true iff all chars are hebrew
|
|
105
|
+
"""
|
|
106
|
+
return check_foreach(s, lambda c: c in HEBREW_LETTERS)
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
@validate # type:ignore
|
|
110
|
+
def is_binary(s: str) -> bool:
|
|
111
|
+
"""checks if s string has a binary value
|
|
112
|
+
|
|
113
|
+
Args:
|
|
114
|
+
s (str): string to check
|
|
115
|
+
|
|
116
|
+
Returns:
|
|
117
|
+
bool: result
|
|
118
|
+
"""
|
|
119
|
+
return check_foreach(s, lambda c: c in {0, 1})
|
|
120
|
+
|
|
121
|
+
|
|
122
|
+
@validate # type:ignore
|
|
123
|
+
def is_decimal(s: str) -> bool:
|
|
124
|
+
"""checks if a string has a decimal number
|
|
125
|
+
|
|
126
|
+
Args:
|
|
127
|
+
s (str): string to check
|
|
128
|
+
|
|
129
|
+
Returns:
|
|
130
|
+
bool: result
|
|
131
|
+
"""
|
|
132
|
+
return check_foreach(s, lambda c: c in range(10))
|
|
133
|
+
|
|
134
|
+
|
|
135
|
+
@validate # type:ignore
|
|
136
|
+
def is_hex(s: str) -> bool:
|
|
137
|
+
"""checks if a string has a hexadecimal value
|
|
138
|
+
|
|
139
|
+
Args:
|
|
140
|
+
s (str): string to check
|
|
141
|
+
|
|
142
|
+
Returns:
|
|
143
|
+
bool: result
|
|
144
|
+
"""
|
|
145
|
+
try:
|
|
146
|
+
int(s, 16)
|
|
147
|
+
logger.debug("String '%s' is a valid hexadecimal", s)
|
|
148
|
+
return True
|
|
149
|
+
except ValueError:
|
|
150
|
+
logger.debug("String '%s' is not a valid hexadecimal", s)
|
|
151
|
+
return False
|
|
152
|
+
|
|
153
|
+
|
|
154
|
+
__all__ = [
|
|
155
|
+
"HEBREW_LETTERS",
|
|
156
|
+
"HEBREW_LETTERS_DEC",
|
|
157
|
+
"HEBREW_LETTERS_HEX",
|
|
158
|
+
"ENGLISH_LETTERS",
|
|
159
|
+
"ENGLISH_LETTERS_DEC",
|
|
160
|
+
"ENGLISH_LETTERS_HEX",
|
|
161
|
+
"is_english",
|
|
162
|
+
"is_number",
|
|
163
|
+
"is_int",
|
|
164
|
+
"is_float",
|
|
165
|
+
"is_hebrew",
|
|
166
|
+
"is_binary",
|
|
167
|
+
"is_decimal",
|
|
168
|
+
"is_hex"
|
|
169
|
+
]
|
danielutils/time.py
ADDED
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import logging
|
|
2
|
+
import time
|
|
3
|
+
from datetime import datetime
|
|
4
|
+
from typing import Callable, TypeVar
|
|
5
|
+
from .versioned_imports import ParamSpec
|
|
6
|
+
from .logging_.utils import get_logger
|
|
7
|
+
|
|
8
|
+
logger = get_logger(__name__)
|
|
9
|
+
|
|
10
|
+
T = TypeVar("T")
|
|
11
|
+
P = ParamSpec("P")
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def measure(func: Callable[P, T]) -> Callable[P, float]:
|
|
15
|
+
"""A function to measure the execution time of a function.
|
|
16
|
+
|
|
17
|
+
Args:
|
|
18
|
+
func (function): The function to be measured.
|
|
19
|
+
|
|
20
|
+
Returns:
|
|
21
|
+
float: The time taken in seconds to execute the given function.
|
|
22
|
+
"""
|
|
23
|
+
|
|
24
|
+
def wrapper(*args, **kwargs) -> float:
|
|
25
|
+
logger.debug("Measuring execution time of function: %s", func.__name__)
|
|
26
|
+
start = time.time()
|
|
27
|
+
func(*args, **kwargs)
|
|
28
|
+
end = time.time()
|
|
29
|
+
execution_time = end - start
|
|
30
|
+
logger.debug("Function %s executed in %.4f seconds", func.__name__, execution_time)
|
|
31
|
+
return execution_time
|
|
32
|
+
|
|
33
|
+
return wrapper
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
def epoch_to_datetime(epoch: float) -> datetime:
|
|
37
|
+
"""Converts a POSIX timestamp to datetime
|
|
38
|
+
|
|
39
|
+
Args:
|
|
40
|
+
epoch (int): time from epoch
|
|
41
|
+
|
|
42
|
+
Returns:
|
|
43
|
+
datetime: resulting conversion
|
|
44
|
+
"""
|
|
45
|
+
return datetime.fromtimestamp(epoch)
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
def datetime_to_epoch(dt: datetime) -> float:
|
|
49
|
+
"""Converts a datetime to a POSIX timestamp
|
|
50
|
+
|
|
51
|
+
Args:
|
|
52
|
+
dt (datetime): datetime object
|
|
53
|
+
|
|
54
|
+
Returns:
|
|
55
|
+
int: resulting conversion
|
|
56
|
+
"""
|
|
57
|
+
return dt.timestamp()
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
__all__ = [
|
|
61
|
+
"measure",
|
|
62
|
+
'epoch_to_datetime',
|
|
63
|
+
'datetime_to_epoch'
|
|
64
|
+
]
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
from .unittest_test_runner import *
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Command-line interface for the test runner using Google Fire.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
import logging
|
|
6
|
+
import fire
|
|
7
|
+
from typing import Optional, Literal
|
|
8
|
+
from .core import UnittestRunner, VerboseLevel
|
|
9
|
+
|
|
10
|
+
logger = logging.getLogger(__name__)
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def run_tests(python_path: Optional[str] = None,
|
|
14
|
+
results_file: str = "test_results.json",
|
|
15
|
+
skip_threshold: int = 24,
|
|
16
|
+
force: bool = False,
|
|
17
|
+
test_name: Optional[str] = None,
|
|
18
|
+
target: Optional[str] = None,
|
|
19
|
+
verbose: VerboseLevel = "class",
|
|
20
|
+
show_function_results: bool = False):
|
|
21
|
+
"""
|
|
22
|
+
Run tests with smart skipping and detailed reporting.
|
|
23
|
+
|
|
24
|
+
Args:
|
|
25
|
+
python_path: Path to Python executable to use for running tests (defaults to current Python)
|
|
26
|
+
results_file: JSON file to store/load results (default: test_results.json)
|
|
27
|
+
skip_threshold: Hours after which to re-run previously passing tests (default: 24)
|
|
28
|
+
force: Force run all tests, ignoring previous results
|
|
29
|
+
test_name: Run only a specific test module (e.g., tests.abstractions.db.test_in_memory_database)
|
|
30
|
+
target: unittest dot-notation target (e.g., tests.module.class.function)
|
|
31
|
+
verbose: Verbose level - module/file/class/function (default: class)
|
|
32
|
+
show_function_results: Show pass/fail status for each individual test function within modules
|
|
33
|
+
"""
|
|
34
|
+
logger.info("Starting test run with parameters: python_path=%s, results_file=%s, skip_threshold=%d, force=%s, target=%s, verbose=%s",
|
|
35
|
+
python_path, results_file, skip_threshold, force, target, verbose)
|
|
36
|
+
|
|
37
|
+
runner = UnittestRunner(
|
|
38
|
+
python_path=python_path,
|
|
39
|
+
results_file=results_file,
|
|
40
|
+
skip_threshold_hours=skip_threshold,
|
|
41
|
+
force_run=force,
|
|
42
|
+
test_name=test_name,
|
|
43
|
+
target=target,
|
|
44
|
+
verbose=verbose,
|
|
45
|
+
show_function_results=show_function_results
|
|
46
|
+
)
|
|
47
|
+
|
|
48
|
+
try:
|
|
49
|
+
logger.info("Executing all tests")
|
|
50
|
+
runner.run_all_tests()
|
|
51
|
+
logger.info("Saving results to JSON file: %s", results_file)
|
|
52
|
+
runner.save_results_json()
|
|
53
|
+
logger.info("Test run completed successfully")
|
|
54
|
+
except KeyboardInterrupt:
|
|
55
|
+
logger.warning("Test run interrupted by user")
|
|
56
|
+
print("\nTest run interrupted by user.")
|
|
57
|
+
except Exception as e:
|
|
58
|
+
logger.error("Error during test run: %s", e)
|
|
59
|
+
print(f"Error during test run: {e}")
|
|
60
|
+
raise
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
def main():
|
|
64
|
+
"""Main entry point for the CLI."""
|
|
65
|
+
logger.info("Starting CLI main entry point")
|
|
66
|
+
fire.Fire(run_tests)
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
if __name__ == "__main__":
|
|
70
|
+
main()
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Core test runner modules.
|
|
3
|
+
|
|
4
|
+
This package contains the core functionality split by responsibility:
|
|
5
|
+
- discovery: Test discovery and structure analysis
|
|
6
|
+
- execution: Test execution hierarchy
|
|
7
|
+
- parser: Output parsing and result processing
|
|
8
|
+
- runner: Main UnittestRunner orchestration
|
|
9
|
+
- types: Type definitions and data structures
|
|
10
|
+
"""
|
|
11
|
+
|
|
12
|
+
from .runner import UnittestRunner
|
|
13
|
+
|
|
14
|
+
# Define VerboseLevel here since it's a simple type alias
|
|
15
|
+
from typing import Literal
|
|
16
|
+
VerboseLevel = Literal["module", "file", "class", "function"]
|
|
17
|
+
|