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,125 @@
|
|
|
1
|
+
"""file with math_ symbols constants"""
|
|
2
|
+
from typing import Union, Dict as Dict
|
|
3
|
+
from ..reflection import get_python_version
|
|
4
|
+
if get_python_version() >= (3, 9):
|
|
5
|
+
from builtins import dict as Dict
|
|
6
|
+
# https://unicode-table.com/en/sets/mathematical-signs/
|
|
7
|
+
SPECIAL_DOUBLE_N = "ℕ"
|
|
8
|
+
SPECIAL_DOUBLE_Q = "ℚ"
|
|
9
|
+
SPECIAL_DOUBLE_R = "ℝ"
|
|
10
|
+
SPECIAL_DOUBLE_Z = "ℤ"
|
|
11
|
+
SPECIAL_DOUBLE_C = "ℂ"
|
|
12
|
+
SPECIAL_RPAB = "〉" # right pointing angle bracket
|
|
13
|
+
SPECIAL_LPAB = "〈" # left pointing angle bracket
|
|
14
|
+
SPECIAL_FORALL = "∀"
|
|
15
|
+
SPECIAL_EXISTS = "∃"
|
|
16
|
+
SPEICAL_VL = "|"
|
|
17
|
+
SPECIAL_SIGMA = "∑"
|
|
18
|
+
SPECIAL_PI = "∏"
|
|
19
|
+
SPECIAL_CIRCLED_PLUS = "⊕"
|
|
20
|
+
SPECIAL_NE = "≠"
|
|
21
|
+
SPECIAL_EQUIV = "≡"
|
|
22
|
+
SPECIAL_LAMBDA = "λ"
|
|
23
|
+
SPECIAL_SQRT = "√"
|
|
24
|
+
SPECIAL_CUBE_ROOT = "∛"
|
|
25
|
+
SPECIAL_4TH_ROOT = "∜"
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
SUPERSCRIPT_SMALL_LETTERS = ['ᵃ', 'ᵇ', 'ᶜ', 'ᵈ', 'ᵉ', 'ᶠ', 'ᵍ', 'ʰ', 'ⁱ', 'ʲ', 'ᵏ', 'ˡ', 'ᵐ', 'ⁿ',
|
|
29
|
+
'ᵒ', 'ᵖ', '𐞥', 'ʳ', 'ˢ', 'ᵗ', 'ᵘ', 'ᵛ', 'ʷ', 'ˣ', 'ʸ', 'ᶻ']
|
|
30
|
+
|
|
31
|
+
# superscript_big_case_a = 'ⁱ'
|
|
32
|
+
# superscript_big_case_b = 'ⁱ'
|
|
33
|
+
# superscript_big_case_c = 'ⁱ'
|
|
34
|
+
# superscript_big_case_d = 'ⁱ'
|
|
35
|
+
# superscript_big_case_e = 'ⁱ'
|
|
36
|
+
# superscript_big_case_f = 'ⁱ'
|
|
37
|
+
# superscript_big_case_g = 'ⁱ'
|
|
38
|
+
# superscript_big_case_h = 'ⁱ'
|
|
39
|
+
# superscript_big_case_i = 'ⁱ'
|
|
40
|
+
# superscript_big_case_j = 'ⁱ'
|
|
41
|
+
# superscript_big_case_k = 'ⁱ'
|
|
42
|
+
# superscript_big_case_l = 'ⁱ'
|
|
43
|
+
# superscript_big_case_m = 'ⁱ'
|
|
44
|
+
# superscript_big_case_n = 'ⁱ'
|
|
45
|
+
# superscript_big_case_o = 'ⁱ'
|
|
46
|
+
# superscript_big_case_p = 'ⁱ'
|
|
47
|
+
# superscript_big_case_q = 'ⁱ'
|
|
48
|
+
# superscript_big_case_r = 'ⁱ'
|
|
49
|
+
# superscript_big_case_s = 'ⁱ'
|
|
50
|
+
# superscript_big_case_t = 'ⁱ'
|
|
51
|
+
# superscript_big_case_u = 'ⁱ'
|
|
52
|
+
# superscript_big_case_v = 'ⁱ'
|
|
53
|
+
# superscript_big_case_w = 'ⁱ'
|
|
54
|
+
# superscript_big_case_x = 'ⁱ'
|
|
55
|
+
# superscript_big_case_y = 'ⁱ'
|
|
56
|
+
# superscript_big_case_z = 'ⁱ'
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
superscript_dict: Dict[Union[str, int], str] = {}
|
|
60
|
+
superscript_dict.update(
|
|
61
|
+
{chr(i+ord('a')): SUPERSCRIPT_SMALL_LETTERS[i] for i in range(26)}
|
|
62
|
+
)
|
|
63
|
+
superscript_digits = ["⁰", "¹", "²", "³",
|
|
64
|
+
"⁴", "⁵", "⁶", "⁷", "⁸", "⁹"]
|
|
65
|
+
superscript_dict.update(
|
|
66
|
+
{i: superscript_digits[i] for i in range(len(superscript_digits))}
|
|
67
|
+
)
|
|
68
|
+
superscript_dict.update({
|
|
69
|
+
"+": "⁺",
|
|
70
|
+
"-": "⁻",
|
|
71
|
+
"=": "⁼",
|
|
72
|
+
"(": "⁽",
|
|
73
|
+
")": "⁾",
|
|
74
|
+
})
|
|
75
|
+
|
|
76
|
+
SUBSCRIPT_SMALL_LETTERS = ['ₐ', '', '', '', 'ₑ', '', '', 'ₕ', 'ᵢ', 'ⱼ', 'ₖ', 'ₗ', 'ₘ',
|
|
77
|
+
'ₙ', 'ₒ', 'ₚ', '', 'ᵣ', 'ₛ', 'ₜ', 'ᵤ', 'ᵥ', '', 'ₓ', '', '']
|
|
78
|
+
# subscript_big_case_a = 'ⁱ'
|
|
79
|
+
# subscript_big_case_b = 'ⁱ'
|
|
80
|
+
# subscript_big_case_c = 'ⁱ'
|
|
81
|
+
# subscript_big_case_d = 'ⁱ'
|
|
82
|
+
# subscript_big_case_e = 'ⁱ'
|
|
83
|
+
# subscript_big_case_f = 'ⁱ'
|
|
84
|
+
# subscript_big_case_g = 'ⁱ'
|
|
85
|
+
# subscript_big_case_h = 'ⁱ'
|
|
86
|
+
# subscript_big_case_i = 'ⁱ'
|
|
87
|
+
# subscript_big_case_j = 'ⁱ'
|
|
88
|
+
# subscript_big_case_k = 'ⁱ'
|
|
89
|
+
# subscript_big_case_l = 'ⁱ'
|
|
90
|
+
# subscript_big_case_m = 'ⁱ'
|
|
91
|
+
# subscript_big_case_n = 'ⁱ'
|
|
92
|
+
# subscript_big_case_o = 'ⁱ'
|
|
93
|
+
# subscript_big_case_p = 'ⁱ'
|
|
94
|
+
# subscript_big_case_q = 'ⁱ'
|
|
95
|
+
# subscript_big_case_r = 'ⁱ'
|
|
96
|
+
# subscript_big_case_s = 'ⁱ'
|
|
97
|
+
# subscript_big_case_t = 'ⁱ'
|
|
98
|
+
# subscript_big_case_u = 'ⁱ'
|
|
99
|
+
# subscript_big_case_v = 'ⁱ'
|
|
100
|
+
# subscript_big_case_w = 'ⁱ'
|
|
101
|
+
# subscript_big_case_x = 'ⁱ'
|
|
102
|
+
# subscript_big_case_y = 'ⁱ'
|
|
103
|
+
# subscript_big_case_z = 'ⁱ'
|
|
104
|
+
subscript_dict: Dict[Union[str, int], str] = {}
|
|
105
|
+
subscript_dict.update(
|
|
106
|
+
{chr(i+ord('a')): SUBSCRIPT_SMALL_LETTERS[i]
|
|
107
|
+
for i in range(len(SUBSCRIPT_SMALL_LETTERS))}
|
|
108
|
+
)
|
|
109
|
+
|
|
110
|
+
subscript_digits = ["\u2080", "\u2081", "\u2082", "\u2083",
|
|
111
|
+
"\u2084", "\u2085", "\u2086", "\u2087", "\u2088", "\u2089"]
|
|
112
|
+
subscript_dict.update(
|
|
113
|
+
{f'{i}': subscript_digits[i] for i in range(len(subscript_digits))}
|
|
114
|
+
)
|
|
115
|
+
subscript_dict.update({
|
|
116
|
+
"+": "\u208A",
|
|
117
|
+
"-": "\u208B",
|
|
118
|
+
"=": "\u208C",
|
|
119
|
+
"(": "\u208D",
|
|
120
|
+
")": "\u208E",
|
|
121
|
+
})
|
|
122
|
+
__all__ = [
|
|
123
|
+
"subscript_dict",
|
|
124
|
+
"subscript_dict",
|
|
125
|
+
]
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import logging
|
|
2
|
+
from ..decorators import atomic
|
|
3
|
+
from ..logging_.utils import get_logger
|
|
4
|
+
|
|
5
|
+
logger = get_logger(__name__)
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class AtomicClassMeta(type):
|
|
9
|
+
"""will make all of the class's function atomic
|
|
10
|
+
"""
|
|
11
|
+
def __new__(mcs, name, bases, namespace):
|
|
12
|
+
logger.info("Creating atomic class: %s", name)
|
|
13
|
+
|
|
14
|
+
# Process class methods
|
|
15
|
+
class_methods_processed = 0
|
|
16
|
+
for k, v in namespace.items():
|
|
17
|
+
if callable(v):
|
|
18
|
+
namespace[k] = atomic(v) # type:ignore
|
|
19
|
+
class_methods_processed += 1
|
|
20
|
+
|
|
21
|
+
# Process inherited methods
|
|
22
|
+
inherited_methods_processed = 0
|
|
23
|
+
for base in bases:
|
|
24
|
+
for k, v in base.__dict__.items():
|
|
25
|
+
if callable(v):
|
|
26
|
+
if k not in namespace:
|
|
27
|
+
namespace[k] = atomic(v) # type:ignore
|
|
28
|
+
inherited_methods_processed += 1
|
|
29
|
+
|
|
30
|
+
logger.info("AtomicClassMeta: %s created with %s class methods and %s inherited methods made atomic", name, class_methods_processed, inherited_methods_processed)
|
|
31
|
+
return super().__new__(mcs, name, bases, namespace)
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
__all__ = [
|
|
35
|
+
"AtomicClassMeta"
|
|
36
|
+
]
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import logging
|
|
2
|
+
from typing import Optional
|
|
3
|
+
from ..logging_.utils import get_logger
|
|
4
|
+
|
|
5
|
+
logger = get_logger(__name__)
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class DeletedException(AttributeError):
|
|
9
|
+
"""an exception to be raised if a function is deleted
|
|
10
|
+
"""
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def deleted(func, cls_name: Optional[str] = None):
|
|
14
|
+
"""replaces a function with a pre-scripted deleted function
|
|
15
|
+
|
|
16
|
+
Args:
|
|
17
|
+
func (_type_): _description_
|
|
18
|
+
cls_name (Optional[str], optional): _description_. Defaults to None.
|
|
19
|
+
|
|
20
|
+
Raises:
|
|
21
|
+
DeletedException: _description_
|
|
22
|
+
|
|
23
|
+
Returns:
|
|
24
|
+
_type_: _description_
|
|
25
|
+
"""
|
|
26
|
+
msg = f"'{func.__qualname__}' has been marked as deleted"
|
|
27
|
+
if cls_name:
|
|
28
|
+
msg = f"'{cls_name}.{func.__name__}' has been marked as deleted"
|
|
29
|
+
|
|
30
|
+
def new_func(*args, **kwargs): # pylint: disable=unused-argument
|
|
31
|
+
nonlocal func
|
|
32
|
+
logger.warning("Attempted to call deleted function: %s", func.__qualname__)
|
|
33
|
+
raise DeletedException(msg)
|
|
34
|
+
return new_func
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
class ImplicitDataDeleterMeta(type):
|
|
38
|
+
"""Inheriting from this metaclass will 'delete' all non builtin function
|
|
39
|
+
and will replace them with a new function which will raise and error
|
|
40
|
+
"""
|
|
41
|
+
def __new__(mcs, name, bases, namespace):
|
|
42
|
+
logger.info("Creating ImplicitDataDeleterMeta class: %s", name)
|
|
43
|
+
|
|
44
|
+
cls_functions = set()
|
|
45
|
+
for k, v in namespace.items():
|
|
46
|
+
if callable(v):
|
|
47
|
+
if hasattr(v, "__objclass__"):
|
|
48
|
+
if v.__objclass__ in {object}:
|
|
49
|
+
continue
|
|
50
|
+
|
|
51
|
+
elif hasattr(v, "__module__"):
|
|
52
|
+
if v.__module__ in {'builtins', None}:
|
|
53
|
+
continue
|
|
54
|
+
|
|
55
|
+
cls_functions.add(v)
|
|
56
|
+
|
|
57
|
+
parent_functions = set()
|
|
58
|
+
for base in bases:
|
|
59
|
+
for k, v in base.__dict__.items():
|
|
60
|
+
if callable(v):
|
|
61
|
+
parent_functions.add(v)
|
|
62
|
+
|
|
63
|
+
parent_dct = {func.__name__: func for func in parent_functions}
|
|
64
|
+
cls_dct = {func.__name__: func for func in cls_functions}
|
|
65
|
+
to_delete = set({k: v for k, v in parent_dct.items()
|
|
66
|
+
if k not in cls_dct}.values())
|
|
67
|
+
|
|
68
|
+
deleted_count = 0
|
|
69
|
+
for func in to_delete:
|
|
70
|
+
if func.__name__ in dir(object):
|
|
71
|
+
if func.__name__ in {"__init__"}:
|
|
72
|
+
continue
|
|
73
|
+
namespace[func.__name__] = object.__dict__[func.__name__]
|
|
74
|
+
elif func.__name__ in {"__len__", "__bool__"}:
|
|
75
|
+
namespace[func.__name__] = lambda self: True
|
|
76
|
+
else:
|
|
77
|
+
namespace[func.__name__] = deleted(func, name)
|
|
78
|
+
deleted_count += 1
|
|
79
|
+
|
|
80
|
+
logger.info("ImplicitDataDeleterMeta: %s created with %s functions marked as deleted", name, deleted_count)
|
|
81
|
+
return super().__new__(mcs, name, bases, namespace)
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
__all__ = [
|
|
85
|
+
"ImplicitDataDeleterMeta",
|
|
86
|
+
"DeletedException"
|
|
87
|
+
]
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import logging
|
|
2
|
+
from ..better_builtins.counter import Counter
|
|
3
|
+
from ..logging_.utils import get_logger
|
|
4
|
+
|
|
5
|
+
logger = get_logger(__name__)
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class InstanceCacheMeta(type):
|
|
9
|
+
"""Adds automatic caching of all instances of the class
|
|
10
|
+
|
|
11
|
+
Adds the following API to the class
|
|
12
|
+
@staticmethod
|
|
13
|
+
instances() -> returns a generator of all of the instances
|
|
14
|
+
|
|
15
|
+
@staticmethod
|
|
16
|
+
get_id(instance) -> returns the id of the instance
|
|
17
|
+
|
|
18
|
+
@staticmethod
|
|
19
|
+
get_instance(id) -> return the id of an instance
|
|
20
|
+
|
|
21
|
+
"""
|
|
22
|
+
def __new__(mcs, name, bases, namespace):
|
|
23
|
+
logger.info("Creating InstanceCacheMeta class: %s", name)
|
|
24
|
+
|
|
25
|
+
INIT = "__init__"
|
|
26
|
+
instance_2_id: dict = {}
|
|
27
|
+
id_2_instance: dict = {}
|
|
28
|
+
counter = Counter()
|
|
29
|
+
original_init = None
|
|
30
|
+
if INIT in namespace:
|
|
31
|
+
original_init = namespace[INIT]
|
|
32
|
+
|
|
33
|
+
def new_init(*args, **kwargs):
|
|
34
|
+
instance_id = counter.get()
|
|
35
|
+
instance = args[0]
|
|
36
|
+
id_2_instance[instance_id] = instance
|
|
37
|
+
instance_2_id[instance] = instance_id
|
|
38
|
+
counter.increment()
|
|
39
|
+
if original_init:
|
|
40
|
+
original_init(*args, **kwargs)
|
|
41
|
+
|
|
42
|
+
def get_id(instance):
|
|
43
|
+
if instance not in instance_2_id:
|
|
44
|
+
logger.warning("Instance %s not found in cache", instance)
|
|
45
|
+
raise KeyError(f"Instance {instance} not found in cache")
|
|
46
|
+
return instance_2_id[instance]
|
|
47
|
+
|
|
48
|
+
def get_instance(id_: int):
|
|
49
|
+
if id_ not in id_2_instance:
|
|
50
|
+
logger.warning("Instance with ID %s not found in cache", id_)
|
|
51
|
+
raise KeyError(f"Instance with ID {id_} not found in cache")
|
|
52
|
+
return id_2_instance[id_]
|
|
53
|
+
|
|
54
|
+
def instances():
|
|
55
|
+
return instance_2_id.keys()
|
|
56
|
+
|
|
57
|
+
namespace["instances"] = staticmethod(instances)
|
|
58
|
+
namespace["get_id"] = staticmethod(get_id)
|
|
59
|
+
namespace["get_instance"] = staticmethod(get_instance)
|
|
60
|
+
namespace[INIT] = new_init
|
|
61
|
+
|
|
62
|
+
logger.info("InstanceCacheMeta: %s created with instance caching enabled", name)
|
|
63
|
+
return super().__new__(mcs, name, bases, namespace)
|
|
64
|
+
__all__ =[
|
|
65
|
+
"InstanceCacheMeta"
|
|
66
|
+
]
|
|
@@ -0,0 +1,328 @@
|
|
|
1
|
+
import inspect
|
|
2
|
+
import logging
|
|
3
|
+
import re
|
|
4
|
+
import traceback
|
|
5
|
+
import functools
|
|
6
|
+
from typing import Callable, Iterable, Any, Generator, Optional, Union, \
|
|
7
|
+
List as List, Set as Set, Type as t_type, Dict as Dict
|
|
8
|
+
from ..logging_.utils import get_logger
|
|
9
|
+
from ..reflection import get_python_version
|
|
10
|
+
from ..reflection.declarations import _should_use_runtime_introspection, _source_matches_class
|
|
11
|
+
if get_python_version() >= (3, 9):
|
|
12
|
+
from builtins import list as List, set as Set, type as t_type, dict as Dict # type:ignore
|
|
13
|
+
# from ..decorators.decorate_conditionally import decorate_conditionally
|
|
14
|
+
|
|
15
|
+
logger = get_logger(__name__)
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
class InterfaceHelper:
|
|
19
|
+
"""a helper class for Interface metaclass
|
|
20
|
+
"""
|
|
21
|
+
ORIGINAL_INIT = "__original_init__"
|
|
22
|
+
|
|
23
|
+
@staticmethod
|
|
24
|
+
def flatten_iterables(iterable: Iterable) -> list:
|
|
25
|
+
"""will flatten and iterable recursively
|
|
26
|
+
|
|
27
|
+
Args:
|
|
28
|
+
iterable (Iterable): iterable to flatten
|
|
29
|
+
|
|
30
|
+
Returns:
|
|
31
|
+
list: the flattened result
|
|
32
|
+
"""
|
|
33
|
+
res = []
|
|
34
|
+
for obj in iterable:
|
|
35
|
+
if isinstance(obj, Iterable) and not isinstance(obj, str):
|
|
36
|
+
res.extend(InterfaceHelper.flatten_iterables(obj))
|
|
37
|
+
else:
|
|
38
|
+
res.append(obj)
|
|
39
|
+
return res
|
|
40
|
+
|
|
41
|
+
@staticmethod
|
|
42
|
+
def is_func_implemented(func: Callable) -> bool:
|
|
43
|
+
"""will check if a function is implemented
|
|
44
|
+
|
|
45
|
+
Args:
|
|
46
|
+
func (Callable): the function to check
|
|
47
|
+
|
|
48
|
+
Returns:
|
|
49
|
+
bool: will return true iff the function is not implemented according to Interface.IMPLICIT_ABSTRACT
|
|
50
|
+
or not decorated with Interface.abstractmethod
|
|
51
|
+
or not an overridden instance of a default implementation of an Interface's function
|
|
52
|
+
"""
|
|
53
|
+
if hasattr(func, Interface.FUNC_KEY):
|
|
54
|
+
return not func.__dict__[Interface.FUNC_KEY]
|
|
55
|
+
|
|
56
|
+
if getattr(func, "__is_interface_handler__", False):
|
|
57
|
+
return False
|
|
58
|
+
|
|
59
|
+
try:
|
|
60
|
+
src = inspect.getsource(func)
|
|
61
|
+
except (OSError, TypeError):
|
|
62
|
+
code = func.__code__
|
|
63
|
+
if code.co_code == b'd\x00S\x00' and code.co_consts == (None,):
|
|
64
|
+
return False
|
|
65
|
+
if code.co_code == (lambda: ...).__code__.co_code:
|
|
66
|
+
return False
|
|
67
|
+
return True
|
|
68
|
+
|
|
69
|
+
if re.match(Interface.IMPLICIT_ABSTRACT, src):
|
|
70
|
+
return False
|
|
71
|
+
|
|
72
|
+
is_default_override = (
|
|
73
|
+
func.__qualname__.startswith(InterfaceHelper.__name__))
|
|
74
|
+
return not is_default_override
|
|
75
|
+
|
|
76
|
+
@staticmethod
|
|
77
|
+
def unimplemented_functions(cls) -> Generator[str, None, None]:
|
|
78
|
+
"""will yield all function that are unimplemented in a class according to InterfaceHelper.is_func_implemented
|
|
79
|
+
|
|
80
|
+
Yields:
|
|
81
|
+
Generator[str, None, None]: yields str which are function names
|
|
82
|
+
"""
|
|
83
|
+
for func_name in InterfaceHelper.get_declared_function_names(cls):
|
|
84
|
+
if func_name not in cls.__dict__:
|
|
85
|
+
continue
|
|
86
|
+
func = cls.__dict__[func_name]
|
|
87
|
+
if not InterfaceHelper.is_func_implemented(func):
|
|
88
|
+
yield func_name
|
|
89
|
+
|
|
90
|
+
@staticmethod
|
|
91
|
+
def implemented_functions(cls) -> Generator[str, None, None]:
|
|
92
|
+
"""will yield all function that are implemented in a class according to InterfaceHelper.is_func_implemented
|
|
93
|
+
|
|
94
|
+
Yields:
|
|
95
|
+
Generator[str, None, None]: yields str which are function names
|
|
96
|
+
"""
|
|
97
|
+
for func_name in InterfaceHelper.get_declared_function_names(cls):
|
|
98
|
+
if func_name not in cls.__dict__:
|
|
99
|
+
continue
|
|
100
|
+
func = cls.__dict__[func_name]
|
|
101
|
+
if InterfaceHelper.is_func_implemented(func):
|
|
102
|
+
yield func_name
|
|
103
|
+
|
|
104
|
+
@staticmethod
|
|
105
|
+
def _runtime_declared_function_names(cls) -> Generator[str, None, None]:
|
|
106
|
+
expected_qualname_prefix = f"{cls.__qualname__}."
|
|
107
|
+
for name, value in cls.__dict__.items():
|
|
108
|
+
if not inspect.isfunction(value):
|
|
109
|
+
continue
|
|
110
|
+
if getattr(value, "__qualname__", "") != expected_qualname_prefix + name:
|
|
111
|
+
continue
|
|
112
|
+
yield name
|
|
113
|
+
|
|
114
|
+
@staticmethod
|
|
115
|
+
def get_declared_function_names(cls) -> Generator[str, None, None]:
|
|
116
|
+
"""will yield the names of all the functions declared inside a class
|
|
117
|
+
|
|
118
|
+
Yields:
|
|
119
|
+
Generator[str, None, None]: yields str values which are names of declared functions
|
|
120
|
+
"""
|
|
121
|
+
if _should_use_runtime_introspection(cls):
|
|
122
|
+
yield from InterfaceHelper._runtime_declared_function_names(cls)
|
|
123
|
+
return
|
|
124
|
+
|
|
125
|
+
try:
|
|
126
|
+
source = inspect.getsource(cls)
|
|
127
|
+
if not _source_matches_class(cls, source):
|
|
128
|
+
raise OSError("source does not match class")
|
|
129
|
+
source_lines = source.splitlines()
|
|
130
|
+
except (OSError, TypeError):
|
|
131
|
+
yield from InterfaceHelper._runtime_declared_function_names(cls)
|
|
132
|
+
return
|
|
133
|
+
|
|
134
|
+
for line in source_lines:
|
|
135
|
+
if re.match(r".*def \w+\(.*\).*:", line):
|
|
136
|
+
func_name = re.findall(r".*def (\w+)\(.*", line)[0]
|
|
137
|
+
if func_name in cls.__dict__:
|
|
138
|
+
yield func_name
|
|
139
|
+
|
|
140
|
+
@staticmethod
|
|
141
|
+
def create_init_handler(cls_name, missing: Optional[Union[List[str],
|
|
142
|
+
Set[str]]] = None, original: Optional[Callable] = None):
|
|
143
|
+
"""this function will create the default interface __init__ function with the wanted behavior"""
|
|
144
|
+
# @decorate_conditionally(functools.wraps, original
|
|
145
|
+
# is not None, [original]) # TODO implement this decorator
|
|
146
|
+
def __interface_init__(*args, **kwargs):
|
|
147
|
+
instance = args[0]
|
|
148
|
+
caller_frame = traceback.format_stack()[-2]
|
|
149
|
+
is_super_call = bool(re.match(
|
|
150
|
+
fr"\s+File \".*\", line \d+, in __init__\n\s+(?:super\(\)|{cls_name})\.__init__\(.*\)\n", caller_frame))
|
|
151
|
+
|
|
152
|
+
if is_super_call:
|
|
153
|
+
mro = instance.__class__.mro()
|
|
154
|
+
i = 0
|
|
155
|
+
for i, cls in enumerate(mro):
|
|
156
|
+
if cls.__name__ == cls_name:
|
|
157
|
+
break
|
|
158
|
+
mro = mro[i:]
|
|
159
|
+
for cls in mro:
|
|
160
|
+
if hasattr(cls, InterfaceHelper.ORIGINAL_INIT):
|
|
161
|
+
result = getattr(cls, InterfaceHelper.ORIGINAL_INIT)(
|
|
162
|
+
*args, **kwargs)
|
|
163
|
+
return result
|
|
164
|
+
logger.error("No original init found in MRO for %s", cls_name)
|
|
165
|
+
raise NotImplementedError(
|
|
166
|
+
f"Can't use super().__init__(...) in {cls_name}.__init__(...) "
|
|
167
|
+
"if the __init__ function is not defined a parent interface")
|
|
168
|
+
|
|
169
|
+
if missing:
|
|
170
|
+
logger.warning("Interface %s missing implementations: %s", cls_name, missing)
|
|
171
|
+
raise NotImplementedError(f"Can't instantiate '{cls_name}' because it is an interface."
|
|
172
|
+
f" It is missing implementations for {missing}")
|
|
173
|
+
logger.warning("Attempting to instantiate interface %s", cls_name)
|
|
174
|
+
raise NotImplementedError(
|
|
175
|
+
f"'{cls_name}' is an interface, Can't create instances")
|
|
176
|
+
return __interface_init__
|
|
177
|
+
|
|
178
|
+
@staticmethod
|
|
179
|
+
def create_generic_handler(cls: str, original: Callable):
|
|
180
|
+
"""this function will create the generic function handler
|
|
181
|
+
|
|
182
|
+
Args:
|
|
183
|
+
func_name (_type_): the name of the interface
|
|
184
|
+
"""
|
|
185
|
+
@functools.wraps(original)
|
|
186
|
+
def __interface_handler__(*args, **kwargs):
|
|
187
|
+
logger.warning("Generic handler called for unimplemented method in interface %s", cls)
|
|
188
|
+
raise NotImplementedError(
|
|
189
|
+
f"Interface {cls} must be implemented")
|
|
190
|
+
__interface_handler__.__is_interface_handler__ = True
|
|
191
|
+
return __interface_handler__
|
|
192
|
+
|
|
193
|
+
|
|
194
|
+
class Interface(type):
|
|
195
|
+
"""This is a metaclass that will enable better_builtins that inherit it directly (or indirectly)
|
|
196
|
+
to behave like interfaces in OOP languages like java
|
|
197
|
+
"""
|
|
198
|
+
IMPLICIT_ABSTRACT = r"\s*def \w+\(.*?\)(?:\s*->\s*[\w\[\],\s]+)?:(?:[^\n]*)\n(?:\s*\"{3}.*\"{3}\n)?\s*\.{3}\n"
|
|
199
|
+
KEY = "__is_interface__"
|
|
200
|
+
FUNC_KEY = "__is_abstractmethod__"
|
|
201
|
+
|
|
202
|
+
@staticmethod
|
|
203
|
+
def abstractmethod(func):
|
|
204
|
+
"""will explicitly mark a method as an abstract method"""
|
|
205
|
+
setattr(func, Interface.FUNC_KEY, True)
|
|
206
|
+
return func
|
|
207
|
+
|
|
208
|
+
def __new__(mcs: t_type['Interface'], name: str, bases: tuple, namespace: dict):
|
|
209
|
+
if len(bases) == 0:
|
|
210
|
+
return mcs._handle_new_interface(mcs, name, bases, namespace)
|
|
211
|
+
return mcs._handle_new_subclass(mcs, name, bases, namespace)
|
|
212
|
+
|
|
213
|
+
@staticmethod
|
|
214
|
+
def _handle_new_interface(mcs, name: str, bases: tuple, namespace: Dict[str, Any]) -> type:
|
|
215
|
+
logger.info("Handling new interface creation: %s", name)
|
|
216
|
+
namespace[InterfaceHelper.ORIGINAL_INIT] = None
|
|
217
|
+
if "__init__" in namespace:
|
|
218
|
+
namespace[InterfaceHelper.ORIGINAL_INIT] = namespace["__init__"]
|
|
219
|
+
namespace["__init__"] = InterfaceHelper.create_init_handler(
|
|
220
|
+
name, original=namespace[InterfaceHelper.ORIGINAL_INIT])
|
|
221
|
+
|
|
222
|
+
abstract_methods = 0
|
|
223
|
+
for k, v in namespace.items():
|
|
224
|
+
if callable(v) and not k == "__init__":
|
|
225
|
+
if not InterfaceHelper.is_func_implemented(v):
|
|
226
|
+
namespace[k] = InterfaceHelper.create_generic_handler(k, v)
|
|
227
|
+
abstract_methods += 1
|
|
228
|
+
logger.info("Created interface %s with %s abstract methods", name, abstract_methods)
|
|
229
|
+
namespace[Interface.KEY] = True
|
|
230
|
+
return type.__new__(mcs, name, bases, namespace)
|
|
231
|
+
|
|
232
|
+
@staticmethod
|
|
233
|
+
def _handle_new_subclass(mcs: t_type['Interface'], name: str, bases: tuple, namespace: Dict[str, Any]) -> type:
|
|
234
|
+
need_to_be_implemented: set = set()
|
|
235
|
+
ancestry = set()
|
|
236
|
+
for base in bases:
|
|
237
|
+
cls_tree = inspect.getclasstree([base], unique=True)
|
|
238
|
+
ancestry.update(InterfaceHelper.flatten_iterables(cls_tree))
|
|
239
|
+
for item in cls_tree:
|
|
240
|
+
if isinstance(item, tuple):
|
|
241
|
+
derived, parent = item
|
|
242
|
+
elif len(item) == 1:
|
|
243
|
+
item = item[0]
|
|
244
|
+
derived, parent = item
|
|
245
|
+
else:
|
|
246
|
+
# multiple inheritance case - need to be implemented
|
|
247
|
+
continue
|
|
248
|
+
|
|
249
|
+
if derived is object:
|
|
250
|
+
continue
|
|
251
|
+
|
|
252
|
+
if isinstance(parent, tuple):
|
|
253
|
+
if len(parent) != 1:
|
|
254
|
+
pass
|
|
255
|
+
parent = parent[0]
|
|
256
|
+
|
|
257
|
+
if parent is not object:
|
|
258
|
+
need_to_be_implemented.update(
|
|
259
|
+
InterfaceHelper.unimplemented_functions(parent))
|
|
260
|
+
|
|
261
|
+
need_to_be_implemented.difference_update(
|
|
262
|
+
InterfaceHelper.implemented_functions(derived))
|
|
263
|
+
need_to_be_implemented.update(
|
|
264
|
+
InterfaceHelper.unimplemented_functions(derived))
|
|
265
|
+
|
|
266
|
+
# cleanup
|
|
267
|
+
del cls_tree, derived, parent
|
|
268
|
+
|
|
269
|
+
if object in ancestry:
|
|
270
|
+
ancestry.remove(object)
|
|
271
|
+
|
|
272
|
+
missing: set = set()
|
|
273
|
+
for func_name in need_to_be_implemented:
|
|
274
|
+
has_been_declared = func_name in namespace
|
|
275
|
+
if not has_been_declared:
|
|
276
|
+
missing.add(func_name)
|
|
277
|
+
continue
|
|
278
|
+
|
|
279
|
+
is_implemented = InterfaceHelper.is_func_implemented(
|
|
280
|
+
namespace[func_name])
|
|
281
|
+
if is_implemented:
|
|
282
|
+
continue
|
|
283
|
+
|
|
284
|
+
for ancestor in ancestry:
|
|
285
|
+
if func_name in InterfaceHelper.implemented_functions(ancestor):
|
|
286
|
+
break
|
|
287
|
+
else:
|
|
288
|
+
missing.add(func_name)
|
|
289
|
+
|
|
290
|
+
# cleanup
|
|
291
|
+
del ancestry
|
|
292
|
+
|
|
293
|
+
if "__init__" in namespace:
|
|
294
|
+
namespace[InterfaceHelper.ORIGINAL_INIT] = namespace["__init__"]
|
|
295
|
+
else:
|
|
296
|
+
if InterfaceHelper.ORIGINAL_INIT in namespace:
|
|
297
|
+
del namespace[InterfaceHelper.ORIGINAL_INIT]
|
|
298
|
+
|
|
299
|
+
if missing:
|
|
300
|
+
namespace[Interface.KEY] = True
|
|
301
|
+
# if "__init__" in need_to_be_implemented:
|
|
302
|
+
namespace["__init__"] = InterfaceHelper.create_init_handler(
|
|
303
|
+
name, missing)
|
|
304
|
+
else:
|
|
305
|
+
namespace[Interface.KEY] = False
|
|
306
|
+
if "__init__" not in namespace:
|
|
307
|
+
namespace["__init__"] = object.__init__
|
|
308
|
+
|
|
309
|
+
return type.__new__(mcs, name, bases, namespace)
|
|
310
|
+
|
|
311
|
+
@staticmethod
|
|
312
|
+
def is_cls_interface(cls_to_check: type) -> bool:
|
|
313
|
+
"""will check if a class is an interface
|
|
314
|
+
|
|
315
|
+
Args:
|
|
316
|
+
cls_to_check (type): the class to check
|
|
317
|
+
|
|
318
|
+
Returns:
|
|
319
|
+
bool: will return true iff a class is an interface
|
|
320
|
+
"""
|
|
321
|
+
if hasattr(cls_to_check, Interface.KEY):
|
|
322
|
+
return cls_to_check.__dict__[Interface.KEY]
|
|
323
|
+
return False
|
|
324
|
+
|
|
325
|
+
|
|
326
|
+
__all__ = [
|
|
327
|
+
"Interface"
|
|
328
|
+
]
|