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,207 @@
|
|
|
1
|
+
import ast
|
|
2
|
+
from enum import Enum
|
|
3
|
+
from typing import List, Union, Optional
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class ImportType(Enum):
|
|
7
|
+
"""Enumeration of import types."""
|
|
8
|
+
GLOBAL_PACKAGE = "global_package" # "import x"
|
|
9
|
+
GLOBAL_FROM_PACKAGE = "global_from_package" # "from x import y"
|
|
10
|
+
LOCAL_PACKAGE = "local_package" # "import .x" or "import ..x"
|
|
11
|
+
# "from .x import y" or "from ..x import y"
|
|
12
|
+
LOCAL_FROM_PACKAGE = "local_from_package"
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class ImportInfo:
|
|
16
|
+
"""
|
|
17
|
+
A class that encapsulates information about a single import statement.
|
|
18
|
+
|
|
19
|
+
This class provides detailed information about imports including:
|
|
20
|
+
- The type of import (global vs local, package vs from_package)
|
|
21
|
+
- Whether the import is absolute or relative
|
|
22
|
+
- Module names and imported object names
|
|
23
|
+
- Aliases used in the import
|
|
24
|
+
"""
|
|
25
|
+
|
|
26
|
+
def __init__(self,
|
|
27
|
+
import_type: ImportType,
|
|
28
|
+
is_absolute: bool,
|
|
29
|
+
module_name: str,
|
|
30
|
+
imported_object: Optional[str] = None,
|
|
31
|
+
alias: Optional[str] = None,
|
|
32
|
+
relative_level: int = 0) -> None:
|
|
33
|
+
"""
|
|
34
|
+
Initialize ImportInfo.
|
|
35
|
+
|
|
36
|
+
:param import_type: The type of import (from ImportType enum)
|
|
37
|
+
:param is_absolute: Whether the import is absolute (True) or relative (False)
|
|
38
|
+
:param module_name: The name of the module being imported from
|
|
39
|
+
:param imported_object: The name of the specific object being imported (for from imports)
|
|
40
|
+
:param alias: The alias used for the import (if any)
|
|
41
|
+
:param relative_level: The number of dots for relative imports (0 for absolute)
|
|
42
|
+
"""
|
|
43
|
+
self._import_type = import_type
|
|
44
|
+
self._is_absolute = is_absolute
|
|
45
|
+
self._module_name = module_name
|
|
46
|
+
self._imported_object = imported_object
|
|
47
|
+
self._alias = alias
|
|
48
|
+
self._relative_level = relative_level
|
|
49
|
+
|
|
50
|
+
@property
|
|
51
|
+
def import_type(self) -> ImportType:
|
|
52
|
+
"""Returns the type of import."""
|
|
53
|
+
return self._import_type
|
|
54
|
+
|
|
55
|
+
@property
|
|
56
|
+
def is_absolute(self) -> bool:
|
|
57
|
+
"""Returns True if the import is absolute, False if relative."""
|
|
58
|
+
return self._is_absolute
|
|
59
|
+
|
|
60
|
+
@property
|
|
61
|
+
def is_relative(self) -> bool:
|
|
62
|
+
"""Returns True if the import is relative, False if absolute."""
|
|
63
|
+
return not self._is_absolute
|
|
64
|
+
|
|
65
|
+
@property
|
|
66
|
+
def module_name(self) -> str:
|
|
67
|
+
"""Returns the name of the module being imported from."""
|
|
68
|
+
return self._module_name
|
|
69
|
+
|
|
70
|
+
@property
|
|
71
|
+
def imported_object(self) -> Optional[str]:
|
|
72
|
+
"""Returns the name of the imported object (for from imports)."""
|
|
73
|
+
return self._imported_object
|
|
74
|
+
|
|
75
|
+
@property
|
|
76
|
+
def alias(self) -> Optional[str]:
|
|
77
|
+
"""Returns the alias used for the import (if any)."""
|
|
78
|
+
return self._alias
|
|
79
|
+
|
|
80
|
+
@property
|
|
81
|
+
def relative_level(self) -> int:
|
|
82
|
+
"""Returns the number of dots for relative imports (0 for absolute)."""
|
|
83
|
+
return self._relative_level
|
|
84
|
+
|
|
85
|
+
@property
|
|
86
|
+
def effective_name(self) -> str:
|
|
87
|
+
"""Returns the effective name used in the code (alias if provided, otherwise imported_object or module_name)."""
|
|
88
|
+
if self.alias:
|
|
89
|
+
return self.alias
|
|
90
|
+
elif self.imported_object:
|
|
91
|
+
return self.imported_object
|
|
92
|
+
else:
|
|
93
|
+
return self.module_name.split('.')[-1]
|
|
94
|
+
|
|
95
|
+
def __str__(self) -> str:
|
|
96
|
+
return (f"ImportInfo(type={self.import_type.value}, "
|
|
97
|
+
f"absolute={self.is_absolute}, "
|
|
98
|
+
f"module='{self.module_name}', "
|
|
99
|
+
f"object={self.imported_object}, "
|
|
100
|
+
f"alias={self.alias})")
|
|
101
|
+
|
|
102
|
+
def __repr__(self) -> str:
|
|
103
|
+
return self.__str__()
|
|
104
|
+
|
|
105
|
+
def to_dict(self) -> dict:
|
|
106
|
+
"""Returns a dictionary representation of the import information."""
|
|
107
|
+
return {
|
|
108
|
+
"import_type": self.import_type.value,
|
|
109
|
+
"is_absolute": self.is_absolute,
|
|
110
|
+
"module_name": self.module_name,
|
|
111
|
+
"imported_object": self.imported_object,
|
|
112
|
+
"alias": self.alias,
|
|
113
|
+
"relative_level": self.relative_level,
|
|
114
|
+
"effective_name": self.effective_name
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
@classmethod
|
|
118
|
+
def from_ast(cls, node: Union[ast.Import, ast.ImportFrom]) -> List["ImportInfo"]:
|
|
119
|
+
"""
|
|
120
|
+
Create ImportInfo instances from AST nodes.
|
|
121
|
+
|
|
122
|
+
:param node: An ast.Import or ast.ImportFrom node
|
|
123
|
+
:return: A list of ImportInfo objects
|
|
124
|
+
"""
|
|
125
|
+
imports = []
|
|
126
|
+
|
|
127
|
+
if isinstance(node, ast.Import):
|
|
128
|
+
# Handle "import x" statements
|
|
129
|
+
for alias in node.names:
|
|
130
|
+
module_name = alias.name
|
|
131
|
+
is_absolute = not module_name.startswith('.')
|
|
132
|
+
relative_level = 0
|
|
133
|
+
|
|
134
|
+
if not is_absolute:
|
|
135
|
+
# Count leading dots for relative imports
|
|
136
|
+
relative_level = len(module_name) - \
|
|
137
|
+
len(module_name.lstrip('.'))
|
|
138
|
+
module_name = module_name[relative_level:]
|
|
139
|
+
|
|
140
|
+
import_type = ImportType.LOCAL_PACKAGE if not is_absolute else ImportType.GLOBAL_PACKAGE
|
|
141
|
+
|
|
142
|
+
imports.append(cls(
|
|
143
|
+
import_type=import_type,
|
|
144
|
+
is_absolute=is_absolute,
|
|
145
|
+
module_name=module_name,
|
|
146
|
+
imported_object=None,
|
|
147
|
+
alias=alias.asname,
|
|
148
|
+
relative_level=relative_level
|
|
149
|
+
))
|
|
150
|
+
|
|
151
|
+
elif isinstance(node, ast.ImportFrom):
|
|
152
|
+
# Handle "from x import y" statements
|
|
153
|
+
module_name = node.module if node.module is not None else ""
|
|
154
|
+
is_absolute = True # Default to absolute
|
|
155
|
+
relative_level = 0
|
|
156
|
+
|
|
157
|
+
# Check if this is a relative import by looking at the level attribute
|
|
158
|
+
if hasattr(node, 'level') and node.level > 0:
|
|
159
|
+
is_absolute = False
|
|
160
|
+
relative_level = node.level
|
|
161
|
+
elif module_name.startswith('.'):
|
|
162
|
+
# Fallback: check if module name starts with dots
|
|
163
|
+
is_absolute = False
|
|
164
|
+
relative_level = len(module_name) - \
|
|
165
|
+
len(module_name.lstrip('.'))
|
|
166
|
+
module_name = module_name[relative_level:]
|
|
167
|
+
|
|
168
|
+
import_type = ImportType.LOCAL_FROM_PACKAGE if not is_absolute else ImportType.GLOBAL_FROM_PACKAGE
|
|
169
|
+
|
|
170
|
+
for alias in node.names:
|
|
171
|
+
imported_object = alias.name
|
|
172
|
+
imports.append(cls(
|
|
173
|
+
import_type=import_type,
|
|
174
|
+
is_absolute=is_absolute,
|
|
175
|
+
module_name=module_name,
|
|
176
|
+
imported_object=imported_object,
|
|
177
|
+
alias=alias.asname,
|
|
178
|
+
relative_level=relative_level
|
|
179
|
+
))
|
|
180
|
+
|
|
181
|
+
return imports
|
|
182
|
+
|
|
183
|
+
@classmethod
|
|
184
|
+
def from_string(cls, import_string: str) -> List["ImportInfo"]:
|
|
185
|
+
"""
|
|
186
|
+
Create ImportInfo instances from a string representation of import statements.
|
|
187
|
+
|
|
188
|
+
:param import_string: A string containing import statements
|
|
189
|
+
:return: A list of ImportInfo objects
|
|
190
|
+
"""
|
|
191
|
+
try:
|
|
192
|
+
tree = ast.parse(import_string)
|
|
193
|
+
imports = []
|
|
194
|
+
|
|
195
|
+
for node in ast.walk(tree):
|
|
196
|
+
if isinstance(node, (ast.Import, ast.ImportFrom)):
|
|
197
|
+
imports.extend(cls.from_ast(node))
|
|
198
|
+
|
|
199
|
+
return imports
|
|
200
|
+
except SyntaxError:
|
|
201
|
+
raise ValueError(f"Invalid import string: {import_string}")
|
|
202
|
+
|
|
203
|
+
|
|
204
|
+
__all__ = [
|
|
205
|
+
"ImportInfo",
|
|
206
|
+
"ImportType"
|
|
207
|
+
]
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import logging
|
|
2
|
+
import inspect
|
|
3
|
+
from typing import Optional
|
|
4
|
+
from types import FrameType
|
|
5
|
+
from ...logging_.utils import get_logger
|
|
6
|
+
|
|
7
|
+
logger = get_logger(__name__)
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def _get_prev_frame_from(frame: Optional[FrameType]) -> Optional[FrameType]:
|
|
11
|
+
"""Get the previous frame (caller's frame) in the call stack."""
|
|
12
|
+
return frame.f_back if frame is not None else None
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def get_current_frame() -> Optional[FrameType]:
|
|
16
|
+
logger.debug("Getting current frame")
|
|
17
|
+
frame = _get_prev_frame_from(inspect.currentframe())
|
|
18
|
+
logger.debug("Current frame: %s", frame)
|
|
19
|
+
return frame
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def get_prev_frame(n_steps: int = 1) -> Optional[FrameType]:
|
|
23
|
+
logger.debug("Getting previous frame with %s steps", n_steps)
|
|
24
|
+
if (f := get_current_frame()) is None:
|
|
25
|
+
logger.debug("No current frame available")
|
|
26
|
+
return None
|
|
27
|
+
i = 0
|
|
28
|
+
while i < n_steps:
|
|
29
|
+
if (f := f.f_back) is None:
|
|
30
|
+
logger.debug("Reached end of call stack at step %s", i)
|
|
31
|
+
return None
|
|
32
|
+
i += 1
|
|
33
|
+
logger.debug("Found previous frame after %s steps", n_steps)
|
|
34
|
+
return f
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def get_prev_line_of_code(n_steps: int = 1) -> Optional[str]:
|
|
38
|
+
logger.debug("Getting previous line of code with %s steps", n_steps)
|
|
39
|
+
frame = get_prev_frame(n_steps + 1)
|
|
40
|
+
if frame is None:
|
|
41
|
+
logger.debug("No frame found for line of code")
|
|
42
|
+
return None
|
|
43
|
+
file = frame.f_back.f_code.co_filename # type:ignore
|
|
44
|
+
line_number = frame.f_back.f_lineno - 1 # type:ignore
|
|
45
|
+
logger.debug("Reading line %s from file: %s", line_number, file)
|
|
46
|
+
with open(file, "r", encoding="utf-8") as f:
|
|
47
|
+
lines = f.readlines()
|
|
48
|
+
line = lines[line_number]
|
|
49
|
+
logger.debug("Previous line of code: %s", line.strip())
|
|
50
|
+
return line
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
__all__ = [
|
|
54
|
+
"get_current_frame",
|
|
55
|
+
"get_prev_frame",
|
|
56
|
+
"get_prev_line_of_code"
|
|
57
|
+
]
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import traceback
|
|
2
|
+
from .python_version import get_python_version
|
|
3
|
+
|
|
4
|
+
if get_python_version() < (3, 9):
|
|
5
|
+
from typing import List as List
|
|
6
|
+
else:
|
|
7
|
+
from builtins import list as List
|
|
8
|
+
|
|
9
|
+
from .is_debugging import is_debugging
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def get_traceback() -> List[str]:
|
|
13
|
+
"""returns the traceback of the stack until current frame
|
|
14
|
+
|
|
15
|
+
Returns:
|
|
16
|
+
list[str]: list of frames as strings
|
|
17
|
+
"""
|
|
18
|
+
s = slice(8, -2)
|
|
19
|
+
if not is_debugging():
|
|
20
|
+
# TODO: fix
|
|
21
|
+
s = s
|
|
22
|
+
return traceback.format_stack()[8:-2]
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
__all__ = [
|
|
26
|
+
"get_traceback"
|
|
27
|
+
]
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import os
|
|
2
|
+
import sys
|
|
3
|
+
from typing import Any
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class Interpreter:
|
|
7
|
+
def get_version(self):
|
|
8
|
+
pass
|
|
9
|
+
|
|
10
|
+
def __init__(self, executable_path: str) -> None:
|
|
11
|
+
self._executable_path = executable_path
|
|
12
|
+
|
|
13
|
+
def _execute(self, command: str) -> None:
|
|
14
|
+
os.system(command)
|
|
15
|
+
|
|
16
|
+
def run_module(self, module_name: str, *args, **kwargs) -> None:
|
|
17
|
+
jargs = " ".join(args)
|
|
18
|
+
jkwargs = " ".join([f"{k}={v}" for k, v in kwargs.items()])
|
|
19
|
+
self._execute(f'{self._executable_path} -m {module_name} {jargs} {jkwargs}')
|
|
20
|
+
|
|
21
|
+
def execute_file(self, file_name: str, *args, **kwargs) -> None:
|
|
22
|
+
jargs = " ".join(args)
|
|
23
|
+
jkwargs = " ".join([f"{k}={v}" for k, v in kwargs.items()])
|
|
24
|
+
self._execute(f'{self._executable_path} {file_name} {jargs} {jkwargs}')
|
|
25
|
+
|
|
26
|
+
def execute_package(self, package_name: str, *args, **kwargs) -> None:
|
|
27
|
+
self.execute_file(package_name, *args, **kwargs)
|
|
28
|
+
|
|
29
|
+
def evaluate(self, expr: str) -> Any:
|
|
30
|
+
return self._execute(f"{self._executable_path} -c {expr}")
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
class CurrentInterpreter(Interpreter):
|
|
34
|
+
def __init__(self) -> None:
|
|
35
|
+
super().__init__(sys.executable)
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
__all__ = [
|
|
39
|
+
"Interpreter",
|
|
40
|
+
"CurrentInterpreter",
|
|
41
|
+
]
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import sys
|
|
2
|
+
from enum import Enum
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
class OSType(Enum):
|
|
6
|
+
"""enum result for possible results of get_os()
|
|
7
|
+
"""
|
|
8
|
+
LINUX = "Linux"
|
|
9
|
+
WINDOWS = "Windows"
|
|
10
|
+
OSX = "OS X"
|
|
11
|
+
UNKNOWN = "Unknown"
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def get_os() -> OSType:
|
|
15
|
+
"""returns the type of operation system running this code
|
|
16
|
+
|
|
17
|
+
Returns:
|
|
18
|
+
OSType: enum result
|
|
19
|
+
"""
|
|
20
|
+
p = sys.platform
|
|
21
|
+
if p in {"linux", "linux2"}:
|
|
22
|
+
return OSType.LINUX
|
|
23
|
+
if p == "darwin":
|
|
24
|
+
return OSType.OSX
|
|
25
|
+
if p == "win32":
|
|
26
|
+
return OSType.WINDOWS
|
|
27
|
+
return OSType.UNKNOWN
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
__all__ = [
|
|
31
|
+
"OSType",
|
|
32
|
+
"get_os",
|
|
33
|
+
]
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
# from typing import Optional
|
|
2
|
+
# from ...system import cm
|
|
3
|
+
# from ...decorators import memo
|
|
4
|
+
#
|
|
5
|
+
# @memo
|
|
6
|
+
# def get_currently_installed_packages() -> list[tuple[str, str]]:
|
|
7
|
+
# ret, out, err = cm("pip", "list")
|
|
8
|
+
# text = out.decode("utf-8")
|
|
9
|
+
# res = []
|
|
10
|
+
# for line in text.splitlines()[2:]:
|
|
11
|
+
# splits = line.split(" ")
|
|
12
|
+
# dep = splits[0]
|
|
13
|
+
# ver = splits[-1]
|
|
14
|
+
# res.append((dep, ver))
|
|
15
|
+
# return res
|
|
16
|
+
#
|
|
17
|
+
#
|
|
18
|
+
# def is_package_installed(name: str, *, min_version: Optional[str] = None, max_version: Optional[str] = None) -> bool:
|
|
19
|
+
# for dep, ver in get_currently_installed_packages():
|
|
20
|
+
# if dep == name:
|
|
21
|
+
# res = True
|
|
22
|
+
# if min_version is not None:
|
|
23
|
+
# res = res and ver >= min_version
|
|
24
|
+
# if max_version is not None:
|
|
25
|
+
# res = res and ver <= max_version
|
|
26
|
+
# return res
|
|
27
|
+
# return False
|
|
28
|
+
#
|
|
29
|
+
#
|
|
30
|
+
# __all__ = [
|
|
31
|
+
# "get_currently_installed_packages",
|
|
32
|
+
# "is_package_installed"
|
|
33
|
+
# ]
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import logging
|
|
2
|
+
import platform
|
|
3
|
+
from ...logging_.utils import get_logger
|
|
4
|
+
|
|
5
|
+
logger = get_logger(__name__)
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def _get_python_version_untyped() -> tuple:
|
|
9
|
+
values = (int(v) for v in platform.python_version().split("."))
|
|
10
|
+
version = tuple(values) # type:ignore
|
|
11
|
+
return version
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
if _get_python_version_untyped() < (3, 9):
|
|
15
|
+
from typing import Tuple as Tuple
|
|
16
|
+
else:
|
|
17
|
+
from builtins import tuple as Tuple # type:ignore
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def get_python_version() -> Tuple[int, int, int]:
|
|
21
|
+
"""return the version of python that is currently running this code
|
|
22
|
+
|
|
23
|
+
Returns:
|
|
24
|
+
tuple[int, int, int]: version
|
|
25
|
+
"""
|
|
26
|
+
version = _get_python_version_untyped()
|
|
27
|
+
logger.info("Python version: %s", version)
|
|
28
|
+
return version # type:ignore
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
__all__ = [
|
|
32
|
+
"get_python_version"
|
|
33
|
+
]
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
from typing import Callable, Optional
|
|
2
|
+
from types import FrameType
|
|
3
|
+
import signal
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
def register_signal_handler(signal_number: int, handler: Callable[[int, Optional[FrameType]], None]):
|
|
7
|
+
"""register a signal handler for specified signal
|
|
8
|
+
|
|
9
|
+
Args:
|
|
10
|
+
signal_number (int): signal number to handle
|
|
11
|
+
handler (Callable[[int, FrameType], None]): the handler function
|
|
12
|
+
"""
|
|
13
|
+
signal.signal(signal_number, handler)
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
__all__ = [
|
|
17
|
+
"register_signal_handler"
|
|
18
|
+
]
|
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
import sys
|
|
2
|
+
from abc import ABC, abstractmethod
|
|
3
|
+
from enum import Enum
|
|
4
|
+
from types import FrameType
|
|
5
|
+
from typing import Any, Callable, Optional, Set as Set, Dict as Dict
|
|
6
|
+
from .python_version import get_python_version
|
|
7
|
+
|
|
8
|
+
if get_python_version() >= (3, 9):
|
|
9
|
+
from builtins import set as Set, dict as Dict
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
# from danielutils import singleton
|
|
13
|
+
# @singleton
|
|
14
|
+
class Tracer(ABC):
|
|
15
|
+
"""
|
|
16
|
+
A class to trace (during runtime) the flow of the currently executing code
|
|
17
|
+
"""
|
|
18
|
+
_INSTANCE = None
|
|
19
|
+
|
|
20
|
+
class EventType(Enum):
|
|
21
|
+
"""
|
|
22
|
+
An enum class to represent all the types of possible events
|
|
23
|
+
"""
|
|
24
|
+
CALL = "call"
|
|
25
|
+
C_CALL = "c_call"
|
|
26
|
+
RETURN = "return"
|
|
27
|
+
C_RETURN = "c_return"
|
|
28
|
+
|
|
29
|
+
@staticmethod
|
|
30
|
+
def from_value(value: str):
|
|
31
|
+
"""
|
|
32
|
+
Converts the given value to a Tracer.EventType instance
|
|
33
|
+
"""
|
|
34
|
+
if value not in Tracer.EventType._value2member_map_:
|
|
35
|
+
raise KeyError("Unknown value")
|
|
36
|
+
return Tracer.EventType._value2member_map_[value]
|
|
37
|
+
|
|
38
|
+
def __init__(
|
|
39
|
+
self,
|
|
40
|
+
*,
|
|
41
|
+
skip_call: bool = False,
|
|
42
|
+
skip_return: bool = False,
|
|
43
|
+
skip_c_call: bool = True,
|
|
44
|
+
skip_c_return: bool = True,
|
|
45
|
+
functions: bool = True,
|
|
46
|
+
classes: bool = True,
|
|
47
|
+
instance_methods: bool = True,
|
|
48
|
+
static_methods: bool = True,
|
|
49
|
+
class_methods: bool = True,
|
|
50
|
+
exclude: Optional[Set[str]] = None
|
|
51
|
+
) -> None:
|
|
52
|
+
|
|
53
|
+
self._call_dict: Dict[Tracer.EventType, Callable] = {
|
|
54
|
+
Tracer.EventType.CALL: self.on_call,
|
|
55
|
+
Tracer.EventType.C_CALL: self.on_call_c,
|
|
56
|
+
Tracer.EventType.RETURN: self.on_return,
|
|
57
|
+
Tracer.EventType.C_RETURN: self.on_return_c,
|
|
58
|
+
}
|
|
59
|
+
self._skip_call = skip_call
|
|
60
|
+
self._skip_return = skip_return
|
|
61
|
+
self._skip_c_call = skip_c_call
|
|
62
|
+
self._skip_c_return = skip_c_return
|
|
63
|
+
self._functions = functions
|
|
64
|
+
self._classes = classes
|
|
65
|
+
self._instance_methods = instance_methods
|
|
66
|
+
self._static_methods = static_methods
|
|
67
|
+
self._class_methods = class_methods
|
|
68
|
+
self._exclude = exclude if exclude is not None else set()
|
|
69
|
+
from ...data_structures import Stack
|
|
70
|
+
self._exclude_stack: Stack[str] = Stack()
|
|
71
|
+
|
|
72
|
+
def _handler(self, stack_frame: FrameType, event_type: str, return_value: Optional[Any]):
|
|
73
|
+
et = Tracer.EventType.from_value(event_type)
|
|
74
|
+
if not self._should_skip(et, stack_frame):
|
|
75
|
+
values = self.parse_event(stack_frame, et, return_value)
|
|
76
|
+
self._call_dict[et](*values)
|
|
77
|
+
return self._handler
|
|
78
|
+
|
|
79
|
+
def _should_skip(self, et: 'Tracer.EventType', frame: FrameType) -> bool:
|
|
80
|
+
func_name = frame.f_code.co_qualname
|
|
81
|
+
|
|
82
|
+
if not self._exclude_stack.is_empty():
|
|
83
|
+
if et in {Tracer.EventType.CALL, Tracer.EventType.C_CALL}:
|
|
84
|
+
self._exclude_stack.push(func_name)
|
|
85
|
+
else:
|
|
86
|
+
self._exclude_stack.pop()
|
|
87
|
+
return True
|
|
88
|
+
if func_name in self._exclude:
|
|
89
|
+
self._exclude_stack.push(func_name)
|
|
90
|
+
return True
|
|
91
|
+
|
|
92
|
+
if et == Tracer.EventType.CALL and self._skip_call or \
|
|
93
|
+
et == Tracer.EventType.RETURN and self._skip_return or \
|
|
94
|
+
et == Tracer.EventType.C_CALL and self._skip_c_call or \
|
|
95
|
+
et == Tracer.EventType.C_RETURN and self._skip_c_return:
|
|
96
|
+
return True
|
|
97
|
+
|
|
98
|
+
# TODO add more cases
|
|
99
|
+
return False
|
|
100
|
+
|
|
101
|
+
def __enter__(self):
|
|
102
|
+
if Tracer._INSTANCE is not None:
|
|
103
|
+
raise RuntimeError("Can only have one active Tracer object at a time")
|
|
104
|
+
Tracer._INSTANCE = self
|
|
105
|
+
self.start_tracing()
|
|
106
|
+
return self
|
|
107
|
+
|
|
108
|
+
def __exit__(self, exc_type, exc_val, exc_tb):
|
|
109
|
+
Tracer._INSTANCE = None
|
|
110
|
+
self.stop_tracing()
|
|
111
|
+
|
|
112
|
+
def start_tracing(self) -> None:
|
|
113
|
+
sys.setprofile(self._handler)
|
|
114
|
+
|
|
115
|
+
def stop_tracing(self) -> None:
|
|
116
|
+
sys.setprofile(None)
|
|
117
|
+
|
|
118
|
+
@abstractmethod
|
|
119
|
+
def parse_event(self, frame: FrameType, et: EventType, return_value: Optional[Any]) -> tuple:
|
|
120
|
+
pass
|
|
121
|
+
|
|
122
|
+
@abstractmethod
|
|
123
|
+
def on_call(self, *args, **kwargs) -> None:
|
|
124
|
+
"""
|
|
125
|
+
A Handler for when a function is called
|
|
126
|
+
"""
|
|
127
|
+
|
|
128
|
+
@abstractmethod
|
|
129
|
+
def on_call_c(self, *args, **kwargs) -> None:
|
|
130
|
+
"""
|
|
131
|
+
A Handler for when an underlying C function is called
|
|
132
|
+
"""
|
|
133
|
+
|
|
134
|
+
@abstractmethod
|
|
135
|
+
def on_return(self, *args, **kwargs) -> None:
|
|
136
|
+
"""
|
|
137
|
+
A Handler for when a function returns
|
|
138
|
+
"""
|
|
139
|
+
|
|
140
|
+
@abstractmethod
|
|
141
|
+
def on_return_c(self, *args, **kwargs) -> None:
|
|
142
|
+
"""
|
|
143
|
+
A Handler for when an underlying C function returns
|
|
144
|
+
"""
|
|
145
|
+
|
|
146
|
+
|
|
147
|
+
class ConsoleTracer(Tracer):
|
|
148
|
+
# def __init__(self, *args, **kwargs) -> None:
|
|
149
|
+
# super().__init__(*args, **kwargs)
|
|
150
|
+
|
|
151
|
+
def parse_event(self, frame: FrameType, event_type: Tracer.EventType, return_value: Optional[Any]) -> tuple:
|
|
152
|
+
func_name: str = frame.f_code.co_qualname
|
|
153
|
+
base: tuple = event_type, func_name
|
|
154
|
+
final: tuple = base
|
|
155
|
+
if return_value is not None:
|
|
156
|
+
final = *base, return_value
|
|
157
|
+
return final
|
|
158
|
+
|
|
159
|
+
def on_call(self, *args, **kwargs) -> None:
|
|
160
|
+
print(*args, **kwargs)
|
|
161
|
+
|
|
162
|
+
def on_call_c(self, *args, **kwargs) -> None:
|
|
163
|
+
print(*args, **kwargs)
|
|
164
|
+
|
|
165
|
+
def on_return(self, *args, **kwargs) -> None:
|
|
166
|
+
print(*args, **kwargs)
|
|
167
|
+
|
|
168
|
+
def on_return_c(self, *args, **kwargs) -> None:
|
|
169
|
+
print(*args, **kwargs)
|
|
170
|
+
|
|
171
|
+
|
|
172
|
+
__all__ = [
|
|
173
|
+
"Tracer",
|
|
174
|
+
"ConsoleTracer"
|
|
175
|
+
]
|