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