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,119 @@
|
|
|
1
|
+
import logging
|
|
2
|
+
from typing import Union, TypeVar, Generic
|
|
3
|
+
from ..comparer import Comparer
|
|
4
|
+
from ...logging_.utils import get_logger
|
|
5
|
+
logger = get_logger(__name__)
|
|
6
|
+
|
|
7
|
+
T = TypeVar("T")
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class Heap(Generic[T]):
|
|
11
|
+
"""a Heap class which will do the sorting according to the supplied comparer object
|
|
12
|
+
"""
|
|
13
|
+
|
|
14
|
+
def __init__(self, comparer: Comparer) -> None:
|
|
15
|
+
logger.debug("Initializing Heap with comparer: %s", type(comparer).__name__)
|
|
16
|
+
self.arr: list = []
|
|
17
|
+
self.comparer = comparer
|
|
18
|
+
|
|
19
|
+
def push(self, val: T) -> None:
|
|
20
|
+
"""will add a new object to the heap
|
|
21
|
+
|
|
22
|
+
Args:
|
|
23
|
+
val (Any): the object to add to the heap
|
|
24
|
+
"""
|
|
25
|
+
logger.debug("Pushing value to heap: %s", val)
|
|
26
|
+
res: Union[int, float] = -1
|
|
27
|
+
curr_index = len(self)
|
|
28
|
+
self.arr.append(val)
|
|
29
|
+
logger.debug("Added value at index %s, heap size: %s", curr_index, len(self.arr))
|
|
30
|
+
parent_index = curr_index // 2 - (1 - curr_index % 2)
|
|
31
|
+
while res < 0 and parent_index >= 0:
|
|
32
|
+
res = self.comparer.compare(
|
|
33
|
+
self[parent_index], self[curr_index])
|
|
34
|
+
if res < 0:
|
|
35
|
+
logger.debug("Swapping values at indices %s and %s", parent_index, curr_index)
|
|
36
|
+
self.arr[parent_index], self.arr[curr_index] = self[curr_index], self[parent_index]
|
|
37
|
+
curr_index = parent_index
|
|
38
|
+
parent_index = curr_index // 2 - (1 - curr_index % 2)
|
|
39
|
+
|
|
40
|
+
def __len__(self):
|
|
41
|
+
return len(self.arr)
|
|
42
|
+
|
|
43
|
+
def __getitem__(self, index: int) -> T:
|
|
44
|
+
return self.arr[index]
|
|
45
|
+
|
|
46
|
+
def is_empty(self) -> bool:
|
|
47
|
+
"""return whether the heap is empty
|
|
48
|
+
|
|
49
|
+
Returns:
|
|
50
|
+
bool: result
|
|
51
|
+
"""
|
|
52
|
+
return len(self) == 0
|
|
53
|
+
|
|
54
|
+
def pop(self) -> T:
|
|
55
|
+
"""return the value at the top of the heap while removing it
|
|
56
|
+
|
|
57
|
+
Returns:
|
|
58
|
+
Any: the result
|
|
59
|
+
"""
|
|
60
|
+
if self.is_empty():
|
|
61
|
+
logger.warning("Attempted to pop from empty heap")
|
|
62
|
+
raise IndexError("pop from empty heap")
|
|
63
|
+
|
|
64
|
+
logger.debug("Popping from heap, current size: %s", len(self))
|
|
65
|
+
res = self[0]
|
|
66
|
+
self.arr[0], self.arr[-1] = self[-1], self[0]
|
|
67
|
+
self.arr.pop()
|
|
68
|
+
logger.debug("Swapped root with last element, new size: %s", len(self))
|
|
69
|
+
flag = True
|
|
70
|
+
curr_index = 0
|
|
71
|
+
while flag:
|
|
72
|
+
child1_index = curr_index * 2 + 1
|
|
73
|
+
child2_index = curr_index * 2 + 2
|
|
74
|
+
if len(self) > child2_index:
|
|
75
|
+
if self.comparer.compare(self[child1_index], self[child2_index]) < 0:
|
|
76
|
+
logger.debug("Swapping with right child at index %s", child2_index)
|
|
77
|
+
self.arr[curr_index], self.arr[child2_index] = self[child2_index], self[curr_index]
|
|
78
|
+
curr_index = child2_index
|
|
79
|
+
elif self.comparer.compare(self[child1_index], self[child2_index]) > 0:
|
|
80
|
+
logger.debug("Swapping with left child at index %s", child1_index)
|
|
81
|
+
self.arr[curr_index], self.arr[child1_index] = self[child1_index], self[curr_index]
|
|
82
|
+
curr_index = child1_index
|
|
83
|
+
else:
|
|
84
|
+
logger.debug("Children are equal, heap property satisfied")
|
|
85
|
+
flag = False
|
|
86
|
+
else:
|
|
87
|
+
if len(self) > child1_index:
|
|
88
|
+
if self.comparer.compare(self[child1_index], self[curr_index]) > 0:
|
|
89
|
+
logger.debug("Swapping with only child at index %s", child1_index)
|
|
90
|
+
self.arr[curr_index], self.arr[child1_index] = self[child1_index], self[curr_index]
|
|
91
|
+
curr_index = child1_index
|
|
92
|
+
else:
|
|
93
|
+
logger.debug("Heap property satisfied with only child")
|
|
94
|
+
flag = False
|
|
95
|
+
else:
|
|
96
|
+
logger.debug("No children, heap property satisfied")
|
|
97
|
+
flag = False
|
|
98
|
+
logger.debug("Heap pop completed, returning: %s", res)
|
|
99
|
+
return res
|
|
100
|
+
|
|
101
|
+
def __str__(self):
|
|
102
|
+
return str(self.arr)
|
|
103
|
+
|
|
104
|
+
def peek(self) -> T:
|
|
105
|
+
"""return the value at the top of the Heap without removing it
|
|
106
|
+
|
|
107
|
+
Returns:
|
|
108
|
+
Any: the result
|
|
109
|
+
"""
|
|
110
|
+
if self.is_empty():
|
|
111
|
+
logger.warning("Attempted to peek at empty heap")
|
|
112
|
+
raise IndexError("peek at empty heap")
|
|
113
|
+
logger.debug("Peeking at heap top: %s", self[0])
|
|
114
|
+
return self[0]
|
|
115
|
+
|
|
116
|
+
|
|
117
|
+
__all__ = [
|
|
118
|
+
"Heap",
|
|
119
|
+
]
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
from typing import TypeVar
|
|
2
|
+
from ..comparer import CompareGreater
|
|
3
|
+
from .heap import Heap
|
|
4
|
+
|
|
5
|
+
T = TypeVar("T")
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class MaxHeap(Heap[T]):
|
|
9
|
+
"""classic MaxHeap implementation
|
|
10
|
+
"""
|
|
11
|
+
|
|
12
|
+
def __init__(self):
|
|
13
|
+
super().__init__(CompareGreater)
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
__all__ = [
|
|
17
|
+
"MaxHeap"
|
|
18
|
+
]
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
from typing import TypeVar
|
|
2
|
+
from ..comparer import CompareSmaller
|
|
3
|
+
from .heap import Heap
|
|
4
|
+
|
|
5
|
+
T = TypeVar("T")
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class MinHeap(Heap[T]):
|
|
9
|
+
"""classic MinHeap implementation
|
|
10
|
+
"""
|
|
11
|
+
|
|
12
|
+
def __init__(self) -> None:
|
|
13
|
+
super().__init__(CompareSmaller)
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
__all__ = [
|
|
17
|
+
"MinHeap"
|
|
18
|
+
]
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
from typing import TypeVar
|
|
2
|
+
from ...metaclasses import AtomicClassMeta
|
|
3
|
+
from .queue import Queue
|
|
4
|
+
|
|
5
|
+
T = TypeVar("T")
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class AtomicQueue(Queue[T], metaclass=AtomicClassMeta):
|
|
9
|
+
"""Same as Queue but atomic
|
|
10
|
+
"""
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
__all__ = [
|
|
14
|
+
"AtomicQueue"
|
|
15
|
+
]
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
from typing import Callable, Any, Union, TypeVar
|
|
2
|
+
from ..heap import Heap
|
|
3
|
+
from ..comparer import Comparer, CompareGreater
|
|
4
|
+
from ..functions import default_weight_function
|
|
5
|
+
from .queue import Queue
|
|
6
|
+
|
|
7
|
+
T = TypeVar("T")
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class PriorityQueue(Queue[T]):
|
|
11
|
+
"""
|
|
12
|
+
A priority queue implementation based on a binary heap.
|
|
13
|
+
|
|
14
|
+
Args:
|
|
15
|
+
weight_func (Callable[[T], Union[int, float]], optional): A function to calculate the weight of items added
|
|
16
|
+
to the queue. Defaults to default_weight_function.
|
|
17
|
+
comparer (Comparer, optional): The comparer to use when comparing weights of items in the queue.
|
|
18
|
+
Defaults to Comparer.GREATER.
|
|
19
|
+
|
|
20
|
+
Raises:
|
|
21
|
+
ValueError: Raised if an item with the same weight value is added more than once.
|
|
22
|
+
|
|
23
|
+
Methods:
|
|
24
|
+
pop() -> T:
|
|
25
|
+
Removes and returns the item with the highest priority (i.e., the lowest weight value) from the queue.
|
|
26
|
+
push(value: T):
|
|
27
|
+
Adds a new item to the queue with the specified value and weight.
|
|
28
|
+
peek() -> T:
|
|
29
|
+
Returns the item with the highest priority
|
|
30
|
+
(i.e., the lowest weight value) from the queue without removing it.
|
|
31
|
+
__str__() -> str:
|
|
32
|
+
Returns a string representation of the queue.
|
|
33
|
+
|
|
34
|
+
Example:
|
|
35
|
+
>>> pq = PriorityQueue()
|
|
36
|
+
>>> pq.push(5)
|
|
37
|
+
>>> pq.push(10)
|
|
38
|
+
>>> pq.push(3)
|
|
39
|
+
>>> pq.pop()
|
|
40
|
+
10
|
|
41
|
+
"""
|
|
42
|
+
|
|
43
|
+
def __init__(self, weight_func: Callable[[Any], Union[int, float]] = default_weight_function):
|
|
44
|
+
super().__init__()
|
|
45
|
+
comparer = CompareGreater if weight_func is default_weight_function else Comparer( # type:ignore
|
|
46
|
+
lambda a, b: weight_func(a) - weight_func(b)) # type:ignore
|
|
47
|
+
self.data: Heap = Heap(comparer) # type:ignore
|
|
48
|
+
self.weight_func = weight_func
|
|
49
|
+
self.dct: dict = {}
|
|
50
|
+
|
|
51
|
+
def pop(self) -> T:
|
|
52
|
+
"""
|
|
53
|
+
Removes and returns the item with the highest priority (i.e., the lowest weight value) from the queue.
|
|
54
|
+
|
|
55
|
+
Returns:
|
|
56
|
+
T: The item with the highest priority in the queue.
|
|
57
|
+
|
|
58
|
+
Raises:
|
|
59
|
+
KeyError: Raised if the queue is empty.
|
|
60
|
+
"""
|
|
61
|
+
item_weight = self.data.pop()
|
|
62
|
+
res = self.dct[item_weight]
|
|
63
|
+
del self.dct[item_weight]
|
|
64
|
+
return res
|
|
65
|
+
|
|
66
|
+
def push(self, value: T) -> None:
|
|
67
|
+
"""
|
|
68
|
+
Adds a new item to the queue with the specified value and weight.
|
|
69
|
+
|
|
70
|
+
Args:
|
|
71
|
+
value (T): The value of the item to add to the queue.
|
|
72
|
+
|
|
73
|
+
Returns:
|
|
74
|
+
None
|
|
75
|
+
|
|
76
|
+
Raises:
|
|
77
|
+
ValueError: Raised if an item with the same weight value is added more than once.
|
|
78
|
+
"""
|
|
79
|
+
item_weight = self.weight_func(value)
|
|
80
|
+
if item_weight in self.dct:
|
|
81
|
+
raise ValueError(
|
|
82
|
+
"Can't have same weight value more than once in current implementation")
|
|
83
|
+
self.data.push(item_weight)
|
|
84
|
+
self.dct[item_weight] = value
|
|
85
|
+
|
|
86
|
+
def peek(self) -> T:
|
|
87
|
+
"""
|
|
88
|
+
Returns the item with the highest priority (i.e., the lowest weight value) from the queue without removing it.
|
|
89
|
+
|
|
90
|
+
Returns:
|
|
91
|
+
T: The item with the highest priority in the queue.
|
|
92
|
+
|
|
93
|
+
Raises:
|
|
94
|
+
KeyError: Raised if the queue is empty.
|
|
95
|
+
"""
|
|
96
|
+
return self.dct[self.data.peek()]
|
|
97
|
+
|
|
98
|
+
def __str__(self) -> str:
|
|
99
|
+
"""
|
|
100
|
+
Returns a string representation of the queue.
|
|
101
|
+
|
|
102
|
+
Returns:
|
|
103
|
+
str: A string representation of the queue.
|
|
104
|
+
"""
|
|
105
|
+
return str([str(self.dct[w]) for w in [self.data[i] for i in range(len(self.data))]])
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
__all__ = [
|
|
109
|
+
"PriorityQueue"
|
|
110
|
+
]
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
import logging
|
|
2
|
+
from typing import Generic, TypeVar, Iterator, List as List
|
|
3
|
+
from ...reflection import get_python_version
|
|
4
|
+
from ...logging_.utils import get_logger
|
|
5
|
+
|
|
6
|
+
if get_python_version() >= (3, 9):
|
|
7
|
+
from builtins import list as List
|
|
8
|
+
T = TypeVar("T")
|
|
9
|
+
|
|
10
|
+
logger = get_logger(__name__)
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class Queue(Generic[T]):
|
|
14
|
+
"""classic Queue data structure"""
|
|
15
|
+
|
|
16
|
+
def __init__(self) -> None:
|
|
17
|
+
self.data: list = []
|
|
18
|
+
logger.debug("Queue initialized")
|
|
19
|
+
|
|
20
|
+
def pop(self) -> T:
|
|
21
|
+
"""return the oldest element while removing it from the queue
|
|
22
|
+
|
|
23
|
+
Returns:
|
|
24
|
+
Any: result
|
|
25
|
+
"""
|
|
26
|
+
if self.is_empty():
|
|
27
|
+
logger.warning("Attempted to pop from empty queue")
|
|
28
|
+
raise IndexError("pop from empty queue")
|
|
29
|
+
|
|
30
|
+
result = self.data.pop()
|
|
31
|
+
logger.debug("Popped element from queue, remaining size: %s", len(self.data))
|
|
32
|
+
return result
|
|
33
|
+
|
|
34
|
+
def push(self, value: T) -> None:
|
|
35
|
+
"""adds a new element to the queue
|
|
36
|
+
|
|
37
|
+
Args:
|
|
38
|
+
value (Any): the value to add
|
|
39
|
+
"""
|
|
40
|
+
self.data.insert(0, value)
|
|
41
|
+
logger.debug("Pushed element to queue, new size: %s", len(self.data))
|
|
42
|
+
|
|
43
|
+
def peek(self) -> T:
|
|
44
|
+
"""returns the oldest element in the queue
|
|
45
|
+
without removing it from the queue
|
|
46
|
+
|
|
47
|
+
Returns:
|
|
48
|
+
Any: result
|
|
49
|
+
"""
|
|
50
|
+
if self.is_empty():
|
|
51
|
+
logger.warning("Attempted to peek at empty queue")
|
|
52
|
+
raise IndexError("peek at empty queue")
|
|
53
|
+
|
|
54
|
+
result = self.data[-1]
|
|
55
|
+
logger.debug("Peeked at element in queue, size: %s", len(self.data))
|
|
56
|
+
return result
|
|
57
|
+
|
|
58
|
+
def __len__(self) -> int:
|
|
59
|
+
return len(self.data)
|
|
60
|
+
|
|
61
|
+
def is_empty(self) -> bool:
|
|
62
|
+
"""returns whether the queue is empty
|
|
63
|
+
|
|
64
|
+
Returns:
|
|
65
|
+
bool: result
|
|
66
|
+
"""
|
|
67
|
+
return len(self) == 0
|
|
68
|
+
|
|
69
|
+
def __str__(self) -> str:
|
|
70
|
+
return repr(self)
|
|
71
|
+
|
|
72
|
+
def __repr__(self) -> str:
|
|
73
|
+
return str(self.data)
|
|
74
|
+
|
|
75
|
+
def __iter__(self) -> Iterator[T]:
|
|
76
|
+
return iter(self.data)
|
|
77
|
+
|
|
78
|
+
def push_many(self, arr: List[T]):
|
|
79
|
+
"""will push many objects to the Queue
|
|
80
|
+
|
|
81
|
+
Args:
|
|
82
|
+
arr (list): the objects to push
|
|
83
|
+
"""
|
|
84
|
+
logger.debug("Pushing %s elements to queue", len(arr))
|
|
85
|
+
for v in arr:
|
|
86
|
+
self.push(v)
|
|
87
|
+
logger.info("Successfully pushed %s elements to queue", len(arr))
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
__all__ = [
|
|
91
|
+
"Queue",
|
|
92
|
+
]
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
import logging
|
|
2
|
+
from typing import Optional, Generator, TypeVar, Generic
|
|
3
|
+
from .graph import Node
|
|
4
|
+
from ..logging_.utils import get_logger
|
|
5
|
+
logger = get_logger(__name__)
|
|
6
|
+
|
|
7
|
+
T = TypeVar('T')
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class Stack(Generic[T]):
|
|
11
|
+
"""A classic Stack class
|
|
12
|
+
"""
|
|
13
|
+
|
|
14
|
+
def __init__(self) -> None:
|
|
15
|
+
self.head: Optional[Node[T]] = None
|
|
16
|
+
self.size = 0
|
|
17
|
+
logger.debug("Stack initialized")
|
|
18
|
+
|
|
19
|
+
def push(self, value: T):
|
|
20
|
+
"""push an item to the stack
|
|
21
|
+
|
|
22
|
+
Args:
|
|
23
|
+
value (Any): item to push
|
|
24
|
+
"""
|
|
25
|
+
logger.debug("Pushing value to stack: %s", value)
|
|
26
|
+
if self.head is None:
|
|
27
|
+
self.head = Node(value)
|
|
28
|
+
logger.debug("Created first node in stack")
|
|
29
|
+
else:
|
|
30
|
+
new_head = Node(value, self.head)
|
|
31
|
+
self.head = new_head
|
|
32
|
+
logger.debug("Added new head node to stack")
|
|
33
|
+
self.size += 1
|
|
34
|
+
logger.debug("Stack size is now: %s", self.size)
|
|
35
|
+
|
|
36
|
+
def pop(self) -> T:
|
|
37
|
+
"""pop an item from the stack
|
|
38
|
+
|
|
39
|
+
Returns:
|
|
40
|
+
Any: poped item
|
|
41
|
+
"""
|
|
42
|
+
if not self.is_empty():
|
|
43
|
+
res = self.head.data # type:ignore
|
|
44
|
+
self.size -= 1
|
|
45
|
+
self.head = self.head.next # type:ignore
|
|
46
|
+
logger.debug("Popped value from stack: %s, remaining size: %s", res, self.size)
|
|
47
|
+
return res
|
|
48
|
+
logger.warning("Attempted to pop from empty stack")
|
|
49
|
+
raise RuntimeError("Can't pop from an empty stack")
|
|
50
|
+
|
|
51
|
+
def peek(self) -> Optional[T]:
|
|
52
|
+
"""
|
|
53
|
+
Returns the top element of the stack
|
|
54
|
+
Returns:
|
|
55
|
+
Optional[T]
|
|
56
|
+
"""
|
|
57
|
+
if self.is_empty():
|
|
58
|
+
logger.debug("Peek called on empty stack")
|
|
59
|
+
return None
|
|
60
|
+
result = self.head.data # type:ignore
|
|
61
|
+
logger.debug("Peeked at stack top: %s", result)
|
|
62
|
+
return result
|
|
63
|
+
|
|
64
|
+
def __len__(self) -> int:
|
|
65
|
+
return self.size
|
|
66
|
+
|
|
67
|
+
def __iter__(self) -> Generator[T, None, None]:
|
|
68
|
+
while self:
|
|
69
|
+
yield self.pop()
|
|
70
|
+
|
|
71
|
+
def is_empty(self) -> bool:
|
|
72
|
+
"""return whether the stack is empty
|
|
73
|
+
"""
|
|
74
|
+
return len(self) == 0
|
|
75
|
+
|
|
76
|
+
def __bool__(self) -> bool:
|
|
77
|
+
return not self.is_empty()
|
|
78
|
+
|
|
79
|
+
def __contains__(self, value: T) -> bool:
|
|
80
|
+
curr = self.head
|
|
81
|
+
while curr is not None:
|
|
82
|
+
if curr.data == value:
|
|
83
|
+
return True
|
|
84
|
+
curr = curr.next
|
|
85
|
+
return False
|
|
86
|
+
|
|
87
|
+
def __str__(self) -> str:
|
|
88
|
+
values = []
|
|
89
|
+
curr = self.head
|
|
90
|
+
while curr:
|
|
91
|
+
values.append(str(curr.data))
|
|
92
|
+
curr = curr.next
|
|
93
|
+
inside = ", ".join(values)
|
|
94
|
+
return f"Stack({inside})"
|
|
95
|
+
|
|
96
|
+
def __repr__(self) -> str:
|
|
97
|
+
return str(self)
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
__all__ = [
|
|
101
|
+
"Stack"
|
|
102
|
+
]
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
from typing import Union, Any, Callable,Dict as Dict
|
|
2
|
+
from .binary_tree import BinaryTree
|
|
3
|
+
from ..graph import BinaryNode
|
|
4
|
+
from ...reflection import get_python_version
|
|
5
|
+
if get_python_version()>=(3,9):
|
|
6
|
+
from builtins import dict as Dict
|
|
7
|
+
|
|
8
|
+
class BinarySyntaxTree(BinaryTree):
|
|
9
|
+
"""
|
|
10
|
+
Binary tree data structure to represent syntax of expressions
|
|
11
|
+
"""
|
|
12
|
+
|
|
13
|
+
@staticmethod
|
|
14
|
+
def _evaluate_node(v: BinaryNode, operator_func_dict: Dict[Any, Callable[[Any, Any], Any]]):
|
|
15
|
+
if not isinstance(v, BinaryNode) or v.left is None and v.right is None:
|
|
16
|
+
return v
|
|
17
|
+
|
|
18
|
+
lres = BinarySyntaxTree._evaluate_node(v.left, operator_func_dict)
|
|
19
|
+
rres = BinarySyntaxTree._evaluate_node(v.right, operator_func_dict)
|
|
20
|
+
|
|
21
|
+
return operator_func_dict[v.data](lres, rres)
|
|
22
|
+
|
|
23
|
+
@property
|
|
24
|
+
def bottom_right(self) -> BinaryNode:
|
|
25
|
+
cur = self.root
|
|
26
|
+
while hasattr(cur, "right"):
|
|
27
|
+
if isinstance(cur, BinaryNode):
|
|
28
|
+
if cur.right is None:
|
|
29
|
+
return cur
|
|
30
|
+
cur = cur.right
|
|
31
|
+
else:
|
|
32
|
+
break
|
|
33
|
+
return cur
|
|
34
|
+
|
|
35
|
+
@bottom_right.setter
|
|
36
|
+
def bottom_right(self, v: BinaryNode) -> None:
|
|
37
|
+
cur = self.root
|
|
38
|
+
while (
|
|
39
|
+
hasattr(cur, "right") and
|
|
40
|
+
isinstance(cur.right, BinaryNode) and
|
|
41
|
+
hasattr(cur.right, "right") and
|
|
42
|
+
isinstance(cur.right.right, BinaryNode)
|
|
43
|
+
):
|
|
44
|
+
cur = cur.right
|
|
45
|
+
cur.right = v
|
|
46
|
+
|
|
47
|
+
@property
|
|
48
|
+
def bottom_left(self) -> BinaryNode:
|
|
49
|
+
cur = self.root
|
|
50
|
+
while hasattr(cur, "left"):
|
|
51
|
+
if isinstance(cur, BinaryNode):
|
|
52
|
+
if cur.left is None:
|
|
53
|
+
return cur
|
|
54
|
+
cur = cur.left
|
|
55
|
+
else:
|
|
56
|
+
break
|
|
57
|
+
return cur
|
|
58
|
+
|
|
59
|
+
@bottom_left.setter
|
|
60
|
+
def bottom_left(self, v: BinaryNode) -> None:
|
|
61
|
+
cur = self.root
|
|
62
|
+
while (
|
|
63
|
+
hasattr(cur, "left") and
|
|
64
|
+
isinstance(cur.left, BinaryNode) and
|
|
65
|
+
hasattr(cur.left, "left") and
|
|
66
|
+
isinstance(cur.left.left, BinaryNode)
|
|
67
|
+
):
|
|
68
|
+
cur = cur.left
|
|
69
|
+
cur.left = v
|
|
70
|
+
|
|
71
|
+
def reverse(self) -> "BinarySyntaxTree":
|
|
72
|
+
return BinarySyntaxTree(self.root.reverse())
|
|
73
|
+
|
|
74
|
+
def __reversed__(self) -> "BinarySyntaxTree":
|
|
75
|
+
return self.reverse()
|
|
76
|
+
|
|
77
|
+
def evaluate(self, operator_func_dict: Dict[Any, Callable[[Any, Any], Any]]) -> Any:
|
|
78
|
+
return BinarySyntaxTree._evaluate_node(self.root, operator_func_dict)
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
__all__ = [
|
|
83
|
+
"BinarySyntaxTree"
|
|
84
|
+
]
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
from enum import Enum
|
|
2
|
+
|
|
3
|
+
from ..graph import BinaryNode
|
|
4
|
+
from typing import TypeVar, Generic, Iterator
|
|
5
|
+
|
|
6
|
+
T = TypeVar("T")
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class BinaryTree(Generic[T]):
|
|
10
|
+
Node: type = BinaryNode
|
|
11
|
+
|
|
12
|
+
class TraversalMode(Enum):
|
|
13
|
+
First = 1
|
|
14
|
+
Middle = 2
|
|
15
|
+
Last = 3
|
|
16
|
+
|
|
17
|
+
def __init__(self, root: BinaryNode[T]):
|
|
18
|
+
self._root = root
|
|
19
|
+
|
|
20
|
+
def __reversed__(self) -> 'BinaryTree[T]':
|
|
21
|
+
return self.reverse()
|
|
22
|
+
|
|
23
|
+
def __iter__(self) -> Iterator[BinaryNode[T]]:
|
|
24
|
+
yield from self.traverse(BinaryTree.TraversalMode.First)
|
|
25
|
+
|
|
26
|
+
def __eq__(self, other):
|
|
27
|
+
if not isinstance(other, BinaryTree):
|
|
28
|
+
return False
|
|
29
|
+
|
|
30
|
+
for a, b in zip(self, other):
|
|
31
|
+
if not (a == b):
|
|
32
|
+
return False
|
|
33
|
+
return True
|
|
34
|
+
|
|
35
|
+
@property
|
|
36
|
+
def root(self) -> BinaryNode[T]:
|
|
37
|
+
return self._root
|
|
38
|
+
|
|
39
|
+
def traverse(self, mode: 'TraversalMode') -> Iterator[BinaryNode[T]]:
|
|
40
|
+
def helper(node: BinaryNode[T]):
|
|
41
|
+
if mode == self.TraversalMode.First:
|
|
42
|
+
yield node
|
|
43
|
+
if node.left is not None:
|
|
44
|
+
yield from helper(node.left)
|
|
45
|
+
if mode == self.TraversalMode.Middle:
|
|
46
|
+
yield node
|
|
47
|
+
if node.right is not None:
|
|
48
|
+
yield from helper(node.right)
|
|
49
|
+
if mode == self.TraversalMode.Last:
|
|
50
|
+
yield node
|
|
51
|
+
|
|
52
|
+
yield from helper(self._root)
|
|
53
|
+
|
|
54
|
+
def reverse(self) -> "BinaryTree[T]":
|
|
55
|
+
return BinaryTree(self.root.reverse())
|
|
56
|
+
|
|
57
|
+
def depth(self) -> int:
|
|
58
|
+
return self.root.depth()
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
__all__ = [
|
|
62
|
+
'BinaryTree'
|
|
63
|
+
]
|
danielutils/date.py
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import json
|
|
2
|
+
import logging
|
|
3
|
+
from .decorators import validate
|
|
4
|
+
from .logging_.utils import get_logger
|
|
5
|
+
logger = get_logger(__name__)
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
@validate # type:ignore # type:ignore
|
|
9
|
+
def dict_to_json(dct: dict) -> str:
|
|
10
|
+
"""converts a python dict to a json object
|
|
11
|
+
|
|
12
|
+
Args:
|
|
13
|
+
d (dict): the dict to convert
|
|
14
|
+
|
|
15
|
+
Returns:
|
|
16
|
+
str: the json as string
|
|
17
|
+
"""
|
|
18
|
+
logger.debug("Converting dict with %d keys to JSON", len(dct))
|
|
19
|
+
result = json.dumps(dct, indent=4)
|
|
20
|
+
logger.debug("JSON conversion completed, result length: %d", len(result))
|
|
21
|
+
return result
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
@validate # type:ignore
|
|
25
|
+
def json_to_dict(json_str: str) -> dict:
|
|
26
|
+
"""converts a json object from a string to a python dict
|
|
27
|
+
|
|
28
|
+
Args:
|
|
29
|
+
j (str): the json str to convert
|
|
30
|
+
|
|
31
|
+
Returns:
|
|
32
|
+
dict: a python dict from the json
|
|
33
|
+
"""
|
|
34
|
+
logger.debug("Converting JSON string (length: %d) to dict", len(json_str))
|
|
35
|
+
result = json.loads(json_str)
|
|
36
|
+
logger.debug("JSON parsing completed, dict has %d keys", len(result))
|
|
37
|
+
return result
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
__all__ = [
|
|
41
|
+
"dict_to_json",
|
|
42
|
+
"json_to_dict"
|
|
43
|
+
]
|