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,98 @@
|
|
|
1
|
+
from queue import Queue
|
|
2
|
+
import logging
|
|
3
|
+
from typing import Optional, Any, Type as t_type, Tuple as Tuple, List as List
|
|
4
|
+
from threading import Semaphore
|
|
5
|
+
from .worker import Worker
|
|
6
|
+
from ...reflection import get_python_version
|
|
7
|
+
from ...logging_.utils import get_logger
|
|
8
|
+
|
|
9
|
+
if get_python_version() >= (3, 9):
|
|
10
|
+
from builtins import type as t_type, tuple as Tuple, list as List # type:ignore
|
|
11
|
+
logger = get_logger(__name__)
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class WorkerPool:
|
|
15
|
+
"""A worker pool class
|
|
16
|
+
"""
|
|
17
|
+
|
|
18
|
+
def __init__(self, num_workers: int, worker_class: t_type[Worker], w_kwargs: dict, global_variables: dict) -> None:
|
|
19
|
+
logger.info("Initializing WorkerPool with %s workers, worker_class=%s", num_workers, worker_class.__name__)
|
|
20
|
+
self.num_workers = num_workers
|
|
21
|
+
self.global_variables: dict = global_variables
|
|
22
|
+
self.q: Queue[Tuple[Any]] = Queue()
|
|
23
|
+
self.worker_class = worker_class
|
|
24
|
+
self.workers: List[Worker] = []
|
|
25
|
+
self.sem = Semaphore(0)
|
|
26
|
+
self.w_kwargs = w_kwargs
|
|
27
|
+
logger.debug("WorkerPool initialized: num_workers=%s, global_vars_count=%s, worker_kwargs_count=%s", num_workers, len(global_variables), len(w_kwargs))
|
|
28
|
+
|
|
29
|
+
def submit(self, job: Any) -> None:
|
|
30
|
+
"""submit a job to the pool
|
|
31
|
+
the object can be anything you want as long as you use it
|
|
32
|
+
correctly in your implemented worker class
|
|
33
|
+
|
|
34
|
+
Args:
|
|
35
|
+
job (Any): job object
|
|
36
|
+
"""
|
|
37
|
+
logger.debug("Submitting job to pool: job_type=%s", type(job).__name__)
|
|
38
|
+
# we create a tuple to signal that it is indeed a job object and we haven't just got None
|
|
39
|
+
# see Worker._loop
|
|
40
|
+
self.q.put((job,))
|
|
41
|
+
self.sem.release()
|
|
42
|
+
logger.debug("Job submitted successfully, queue_size=%s, semaphore_value=%s", self.q.qsize(), self.sem._value)
|
|
43
|
+
|
|
44
|
+
def _acquire(self) -> Optional[Tuple[Any]]:
|
|
45
|
+
"""acquire a new job from the pool
|
|
46
|
+
|
|
47
|
+
Returns:
|
|
48
|
+
Optional[tuple[Any]]: optional tuple of job object
|
|
49
|
+
"""
|
|
50
|
+
logger.debug("Acquiring job from pool")
|
|
51
|
+
self.sem.acquire()
|
|
52
|
+
if self.q.unfinished_tasks > 0:
|
|
53
|
+
job = self.q.get()
|
|
54
|
+
logger.debug("Job acquired successfully, remaining_tasks=%s", self.q.unfinished_tasks)
|
|
55
|
+
return job
|
|
56
|
+
logger.debug("No jobs available in pool")
|
|
57
|
+
return None
|
|
58
|
+
|
|
59
|
+
def start(self) -> None:
|
|
60
|
+
"""starts running the pool of workers
|
|
61
|
+
"""
|
|
62
|
+
logger.info("Starting worker pool with %s workers", self.num_workers)
|
|
63
|
+
for i in range(self.num_workers):
|
|
64
|
+
logger.debug("Creating worker %s/%s", i+1, self.num_workers)
|
|
65
|
+
w = self.worker_class(i, self, **self.w_kwargs)
|
|
66
|
+
w.run()
|
|
67
|
+
self.workers.append(w)
|
|
68
|
+
logger.debug("Worker %s started successfully", i+1)
|
|
69
|
+
logger.info("Worker pool started with %s workers", len(self.workers))
|
|
70
|
+
|
|
71
|
+
def _notify(self) -> None:
|
|
72
|
+
"""a function that the worker calls after finishing processing a job object (Any)
|
|
73
|
+
this function is called automatically from Worker.work()
|
|
74
|
+
"""
|
|
75
|
+
logger.debug("Worker notification received, unfinished_tasks=%s", self.q.unfinished_tasks)
|
|
76
|
+
self.q.task_done()
|
|
77
|
+
if self.q.unfinished_tasks <= 0:
|
|
78
|
+
logger.debug("All tasks completed, releasing all workers")
|
|
79
|
+
self.sem.release(self.num_workers)
|
|
80
|
+
logger.debug("Notification processed, remaining_tasks=%s", self.q.unfinished_tasks)
|
|
81
|
+
|
|
82
|
+
def join(self) -> None:
|
|
83
|
+
"""
|
|
84
|
+
waits for all the workers to finish and will return afterwards
|
|
85
|
+
Returns:
|
|
86
|
+
None
|
|
87
|
+
"""
|
|
88
|
+
logger.info("Joining worker pool with %s workers", len(self.workers))
|
|
89
|
+
for i, w in enumerate(self.workers):
|
|
90
|
+
logger.debug("Joining worker %s/%s", i+1, len(self.workers))
|
|
91
|
+
w.thread.join()
|
|
92
|
+
logger.debug("Worker %s joined successfully", i+1)
|
|
93
|
+
logger.info("All workers joined successfully")
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
__all__ = [
|
|
97
|
+
"WorkerPool"
|
|
98
|
+
]
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
import copy, re
|
|
2
|
+
import logging
|
|
3
|
+
from typing import Any, Callable, Union, Tuple as Tuple, List as List, Dict as Dict
|
|
4
|
+
from ..reflection import get_python_version # pylint :disable=relative-beyond-top-level
|
|
5
|
+
from ..logging_.utils import get_logger
|
|
6
|
+
|
|
7
|
+
if get_python_version() >= (3, 9):
|
|
8
|
+
from builtins import tuple as Tuple, list as List, dict as Dict # type:ignore
|
|
9
|
+
logger = get_logger(__name__)
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class Argument:
|
|
13
|
+
"""a class to wrap an argument"""
|
|
14
|
+
|
|
15
|
+
def __init__(self, name: str, optional: bool = False, flag: bool = False) -> None:
|
|
16
|
+
logger.debug("Creating Argument: name=%s, optional=%s, flag=%s", name, optional, flag)
|
|
17
|
+
self.name = name
|
|
18
|
+
self.optional = optional
|
|
19
|
+
self.flag = flag
|
|
20
|
+
logger.debug("Argument created successfully: %s", name)
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
class Command:
|
|
24
|
+
"""a class to wrap a command
|
|
25
|
+
"""
|
|
26
|
+
|
|
27
|
+
def __init__(self, command: Union[Argument, str], callback: Callable,
|
|
28
|
+
explanation: str = "", *, options: Tuple[Argument, ...] = tuple()) -> None:
|
|
29
|
+
logger.debug("Creating Command: command=%s, explanation_length=%d, options_count=%d", command if isinstance(command, str) else command.name, len(explanation), len(options))
|
|
30
|
+
self.command = command if isinstance(
|
|
31
|
+
command, Argument) else Argument(command)
|
|
32
|
+
self.callback = callback
|
|
33
|
+
self.explanation = explanation
|
|
34
|
+
self.options = options
|
|
35
|
+
logger.debug("Command created successfully: %s", self.command.name)
|
|
36
|
+
|
|
37
|
+
def __call__(self, *args: Any, **kwargs: Any) -> Any:
|
|
38
|
+
logger.debug("Command '%s' called with args_count=%d, kwargs_count=%d", self.command.name, len(args), len(kwargs))
|
|
39
|
+
if len(args) > 0:
|
|
40
|
+
if args[0] == "help":
|
|
41
|
+
logger.info("Help requested for command: %s", self.command.name)
|
|
42
|
+
if self.explanation != "":
|
|
43
|
+
print(self.explanation)
|
|
44
|
+
logger.debug("Help explanation displayed for command: %s", self.command.name)
|
|
45
|
+
return None
|
|
46
|
+
try:
|
|
47
|
+
result = self.callback(*args, **kwargs)
|
|
48
|
+
logger.debug("Command '%s' executed successfully", self.command.name)
|
|
49
|
+
return result
|
|
50
|
+
except Exception as e:
|
|
51
|
+
logger.error("Command '%s' failed with %s: %s", self.command.name, type(e).__name__, e)
|
|
52
|
+
raise
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
class REPL:
|
|
56
|
+
"""a class to easily create a shell application and get functionality for free
|
|
57
|
+
"""
|
|
58
|
+
|
|
59
|
+
# pylint: disable=dangerous-default-value
|
|
60
|
+
def __init__(self, routes: List[Command], *, prompt_symbol: str = ">>> ", exit_keywords: set = {"exit", "quit"}):
|
|
61
|
+
logger.info("Initializing REPL with %d commands, prompt='%s', exit_keywords=%s", len(routes), prompt_symbol, exit_keywords)
|
|
62
|
+
self.prompt_symbol = prompt_symbol
|
|
63
|
+
self.exit_keywords = copy.copy(exit_keywords)
|
|
64
|
+
self.routes: Dict[str, Command] = {
|
|
65
|
+
com.command.name: com for com in routes}
|
|
66
|
+
logger.debug("REPL initialized successfully with commands: %s", list(self.routes.keys()))
|
|
67
|
+
|
|
68
|
+
def run(self) -> None:
|
|
69
|
+
"""runs the main loop for the shell
|
|
70
|
+
|
|
71
|
+
Raises:
|
|
72
|
+
e: any error if there is any
|
|
73
|
+
"""
|
|
74
|
+
logger.info("Starting REPL main loop")
|
|
75
|
+
while True:
|
|
76
|
+
prompt = input(self.prompt_symbol)
|
|
77
|
+
logger.debug("User input received: '%s'", prompt)
|
|
78
|
+
|
|
79
|
+
if prompt in self.exit_keywords:
|
|
80
|
+
logger.info("Exit keyword '%s' received, stopping REPL", prompt)
|
|
81
|
+
break
|
|
82
|
+
|
|
83
|
+
if prompt == "help":
|
|
84
|
+
logger.info("Help command requested")
|
|
85
|
+
print("Available commands:")
|
|
86
|
+
for com in list(self.routes.keys()) + list(self.exit_keywords):
|
|
87
|
+
print(f"\t{com}")
|
|
88
|
+
logger.debug("Help displayed with %d commands", len(self.routes) + len(self.exit_keywords))
|
|
89
|
+
continue
|
|
90
|
+
|
|
91
|
+
prompt_parts = prompt.split()
|
|
92
|
+
command = prompt_parts[0]
|
|
93
|
+
logger.debug("Processing command: '%s' with %d arguments", command, len(prompt_parts)-1)
|
|
94
|
+
|
|
95
|
+
if command in self.routes:
|
|
96
|
+
try:
|
|
97
|
+
logger.debug("Executing command: %s", command)
|
|
98
|
+
self.routes[command](*prompt_parts[1:])
|
|
99
|
+
logger.debug("Command '%s' completed successfully", command)
|
|
100
|
+
except TypeError as e:
|
|
101
|
+
msg = str(e)
|
|
102
|
+
logger.warning("TypeError in command '%s': %s", command, msg)
|
|
103
|
+
if re.match(r".*missing.*required.*argument.*", msg):
|
|
104
|
+
print(f"'{command}' " + msg[msg.find("missing"):])
|
|
105
|
+
elif re.match(r".*takes.*arguments but.*given", msg):
|
|
106
|
+
print(f"'{command}' " + msg[msg.find("takes"):])
|
|
107
|
+
else:
|
|
108
|
+
logger.error("Unhandled TypeError in command '%s': %s", command, e)
|
|
109
|
+
raise e
|
|
110
|
+
except Exception as e:
|
|
111
|
+
logger.error("Unexpected error in command '%s': %s: %s", command, type(e).__name__, e)
|
|
112
|
+
raise e
|
|
113
|
+
else:
|
|
114
|
+
logger.warning("Invalid command '%s' received", command)
|
|
115
|
+
print(
|
|
116
|
+
"Invalid command. for help type 'help'.\nOr additionally you may try a command and then 'help'")
|
|
117
|
+
|
|
118
|
+
logger.info("REPL main loop ended")
|
|
119
|
+
|
|
120
|
+
|
|
121
|
+
__all__ = [
|
|
122
|
+
"REPL",
|
|
123
|
+
"Command",
|
|
124
|
+
"Argument"
|
|
125
|
+
]
|
danielutils/aliases.py
ADDED
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
# import inspect
|
|
2
|
+
# from abc import ABC, abstractmethod
|
|
3
|
+
# from typing import cast, Generic, ParamSpec, TypeVar, Callable
|
|
4
|
+
# from .functions import isoftype
|
|
5
|
+
# from .reflection.Function import get_caller_name, get_function_return_type
|
|
6
|
+
# P = ParamSpec("P")
|
|
7
|
+
# A = TypeVar("A")
|
|
8
|
+
# B = TypeVar("B")
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
# class SubscribableBase(ABC):
|
|
12
|
+
# """An abstract class that need __instanceCheck__ to be implemented
|
|
13
|
+
# will create default functions such that isinstance will work with the new type
|
|
14
|
+
# while it is used as all of the python's generic types
|
|
15
|
+
# """
|
|
16
|
+
# __slots__ = ("params",)
|
|
17
|
+
|
|
18
|
+
# def __init__(self, *params):
|
|
19
|
+
# if get_caller_name() != "__class_getitem__":
|
|
20
|
+
# raise ValueError(
|
|
21
|
+
# f"{self.__class__.__name__} is a type-hint class, Cant instantiate.")
|
|
22
|
+
# self.params = params[0] if type(params) in {list, tuple} else params
|
|
23
|
+
|
|
24
|
+
# @classmethod
|
|
25
|
+
# def __class_getitem__(cls, *params) -> type:
|
|
26
|
+
# res: type = cast(type, cls(*params))
|
|
27
|
+
# return res
|
|
28
|
+
|
|
29
|
+
# @abstractmethod
|
|
30
|
+
# def __instancecheck__(self, value) -> bool: ...
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
# class Supplier(SubscribableBase):
|
|
34
|
+
# """create a type for functions to be a supplier function
|
|
35
|
+
# """
|
|
36
|
+
|
|
37
|
+
# def __instancecheck__(self, func) -> bool:
|
|
38
|
+
# if not callable(func):
|
|
39
|
+
# return False
|
|
40
|
+
# signature = inspect.signature(func)
|
|
41
|
+
# if len(signature.parameters) != 0:
|
|
42
|
+
# return False
|
|
43
|
+
|
|
44
|
+
# return_type = get_function_return_type(func)
|
|
45
|
+
# if return_type is not None:
|
|
46
|
+
# try:
|
|
47
|
+
# return isoftype(return_type(), self.params)
|
|
48
|
+
# except:
|
|
49
|
+
# return False
|
|
50
|
+
# return self.params is None
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
# class Consumer(SubscribableBase):
|
|
54
|
+
# """create a type for functions to be a consumer function
|
|
55
|
+
# """
|
|
56
|
+
|
|
57
|
+
# def __instancecheck__(self, func) -> bool:
|
|
58
|
+
# if not callable(func):
|
|
59
|
+
# return False
|
|
60
|
+
# if get_function_return_type(func) is not type(None):
|
|
61
|
+
# return False
|
|
62
|
+
# signature = inspect.signature(func)
|
|
63
|
+
# if len(signature.parameters) != 1:
|
|
64
|
+
# return False
|
|
65
|
+
# param_type = next(iter(signature.parameters.values())).annotation
|
|
66
|
+
# try:
|
|
67
|
+
# return isoftype(param_type(), self.params)
|
|
68
|
+
# except:
|
|
69
|
+
# return False
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
# class BinaryConsumer(SubscribableBase, Generic[A, B]):
|
|
73
|
+
# """create a type for functions to be a binary consumer function
|
|
74
|
+
# """
|
|
75
|
+
|
|
76
|
+
# def __instancecheck__(self, func) -> bool:
|
|
77
|
+
# if self.params[-1] is not None:
|
|
78
|
+
# return False
|
|
79
|
+
# self.params = self.params[0]
|
|
80
|
+
# if not callable(func):
|
|
81
|
+
# return False
|
|
82
|
+
# if get_function_return_type(func) is not type(None):
|
|
83
|
+
# return False
|
|
84
|
+
# signature = inspect.signature(func)
|
|
85
|
+
# if len(signature.parameters) != 2:
|
|
86
|
+
# return False
|
|
87
|
+
# param_iter = iter(signature.parameters.values())
|
|
88
|
+
# param_types = (next(param_iter).annotation(),
|
|
89
|
+
# next(param_iter).annotation())
|
|
90
|
+
# try:
|
|
91
|
+
# for value, value_type in zip(param_types, self.params):
|
|
92
|
+
# if not isoftype(value, value_type):
|
|
93
|
+
# return False
|
|
94
|
+
# return True
|
|
95
|
+
# except:
|
|
96
|
+
# return False
|
|
97
|
+
|
|
98
|
+
# # class Comparer(SubscribableBase):
|
|
99
|
+
# # def __instancecheck__(self, func) -> bool:
|
|
100
|
+
# # if not callable(func):
|
|
101
|
+
# # return False
|
|
102
|
+
# # if get_function_return_type(func) not in {int, float, Union[int, float]}:
|
|
103
|
+
# # return False
|
|
104
|
+
# # signature = inspect.signature(func)
|
|
105
|
+
# # if len(signature.parameters) != 2:
|
|
106
|
+
# # return False
|
|
107
|
+
# # param_iter = iter(signature.parameters.values())
|
|
108
|
+
# # param1_type = next(param_iter).annotation
|
|
109
|
+
# # param2_type = next(param_iter).annotation
|
|
110
|
+
# # try:
|
|
111
|
+
# # return param1_type == param2_type == self.params[0] == self.params[1]
|
|
112
|
+
# # except:
|
|
113
|
+
# # return False
|
|
114
|
+
# # class Predicate(SubscribableBase):
|
|
115
|
+
# # """create a type for functions to be a consumer function
|
|
116
|
+
# # """
|
|
117
|
+
|
|
118
|
+
# # def __instancecheck__(self, func) -> bool:
|
|
119
|
+
# # if not callable(func):
|
|
120
|
+
# # return False
|
|
121
|
+
# # signature = inspect.signature(func)
|
|
122
|
+
# # if len(signature.parameters) != 0:
|
|
123
|
+
# # return False
|
|
124
|
+
|
|
125
|
+
# # return_type = get_function_return_type(func)
|
|
126
|
+
# # try:
|
|
127
|
+
# # return isoftype(return_type(), self.params)
|
|
128
|
+
# # except:
|
|
129
|
+
# # return False
|
|
130
|
+
|
|
131
|
+
|
|
132
|
+
# # ListTupleType: TypeAlias = Union[list, tuple]
|
|
133
|
+
# __all__ = [
|
|
134
|
+
# "SubscribableBase",
|
|
135
|
+
# "Supplier",
|
|
136
|
+
# "Consumer",
|
|
137
|
+
# "BinaryConsumer",
|
|
138
|
+
# # "Predicate"
|
|
139
|
+
# # "ListTupleType"
|
|
140
|
+
# ]
|
|
141
|
+
from typing import Callable, TypeVar, Union
|
|
142
|
+
|
|
143
|
+
Number = Union[int, float, complex]
|
|
144
|
+
|
|
145
|
+
A = TypeVar("A")
|
|
146
|
+
B = TypeVar("B")
|
|
147
|
+
U = TypeVar("U")
|
|
148
|
+
T = TypeVar("T")
|
|
149
|
+
# P = ParamSpec("P")
|
|
150
|
+
|
|
151
|
+
Supplier = Callable[[], T]
|
|
152
|
+
Runnable = Supplier[None]
|
|
153
|
+
Consumer = Callable[[T], None]
|
|
154
|
+
UnaryFunction = Callable[[U], T]
|
|
155
|
+
UnaryOperator = Callable[[T], T]
|
|
156
|
+
Predicate = Callable[[T], bool]
|
|
157
|
+
|
|
158
|
+
BinaryFunction = Callable[[A, B], T]
|
|
159
|
+
BinaryOperator = Callable[[T, T], T]
|
|
160
|
+
Comparator = Callable[[A, B], int]
|
|
161
|
+
BinaryConsumer = Callable[[T, T], None]
|
|
162
|
+
|
|
163
|
+
__all__ = [
|
|
164
|
+
"UnaryFunction",
|
|
165
|
+
"BinaryFunction",
|
|
166
|
+
"UnaryOperator",
|
|
167
|
+
"BinaryOperator",
|
|
168
|
+
"Supplier",
|
|
169
|
+
"Predicate",
|
|
170
|
+
"Comparator",
|
|
171
|
+
"Consumer",
|
|
172
|
+
"BinaryConsumer",
|
|
173
|
+
"Runnable",
|
|
174
|
+
]
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import asyncio
|
|
2
|
+
import logging
|
|
3
|
+
from typing import Tuple, Optional
|
|
4
|
+
from ..logging_.utils import get_logger
|
|
5
|
+
logger = get_logger(__name__)
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
async def async_cmd(
|
|
9
|
+
cmd: str,
|
|
10
|
+
*,
|
|
11
|
+
capture_stdout: bool = False,
|
|
12
|
+
capture_stderr: bool = False
|
|
13
|
+
) -> Tuple[int, Optional[bytes], Optional[bytes]]:
|
|
14
|
+
logger.debug("Executing async command: %s, capture_stdout=%s, capture_stderr=%s", cmd, capture_stdout, capture_stderr)
|
|
15
|
+
kwargs = {}
|
|
16
|
+
if capture_stdout:
|
|
17
|
+
kwargs['stdout'] = asyncio.subprocess.PIPE
|
|
18
|
+
if capture_stderr:
|
|
19
|
+
kwargs['stderr'] = asyncio.subprocess.PIPE
|
|
20
|
+
process = await asyncio.create_subprocess_shell(cmd, **kwargs) # type:ignore
|
|
21
|
+
stdout, stderr = await process.communicate()
|
|
22
|
+
logger.debug("Command completed with returncode=%s", process.returncode)
|
|
23
|
+
return process.returncode or 0, stdout, stderr
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
__all__ = [
|
|
27
|
+
'async_cmd',
|
|
28
|
+
]
|