osbot-utils 1.7.7__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.
- osbot_utils/__init__.py +1 -0
- osbot_utils/base_classes/Cache_Pickle.py +129 -0
- osbot_utils/base_classes/Kwargs_To_Disk.py +27 -0
- osbot_utils/base_classes/Kwargs_To_Self.py +308 -0
- osbot_utils/base_classes/Type_Safe__List.py +14 -0
- osbot_utils/base_classes/__init__.py +0 -0
- osbot_utils/context_managers/__init__.py +0 -0
- osbot_utils/context_managers/capture_duration.py +33 -0
- osbot_utils/decorators/__init__.py +0 -0
- osbot_utils/decorators/classes/__init__.py +0 -0
- osbot_utils/decorators/classes/singleton.py +9 -0
- osbot_utils/decorators/lists/__init__.py +0 -0
- osbot_utils/decorators/lists/filter_list.py +12 -0
- osbot_utils/decorators/lists/group_by.py +21 -0
- osbot_utils/decorators/lists/index_by.py +27 -0
- osbot_utils/decorators/methods/__init__.py +0 -0
- osbot_utils/decorators/methods/cache.py +19 -0
- osbot_utils/decorators/methods/cache_on_function.py +56 -0
- osbot_utils/decorators/methods/cache_on_self.py +78 -0
- osbot_utils/decorators/methods/cache_on_tmp.py +71 -0
- osbot_utils/decorators/methods/capture_exception.py +37 -0
- osbot_utils/decorators/methods/capture_status.py +20 -0
- osbot_utils/decorators/methods/catch.py +13 -0
- osbot_utils/decorators/methods/context.py +11 -0
- osbot_utils/decorators/methods/depreciated.py +79 -0
- osbot_utils/decorators/methods/function_type_check.py +62 -0
- osbot_utils/decorators/methods/obj_as_context.py +6 -0
- osbot_utils/decorators/methods/remove_return_value.py +22 -0
- osbot_utils/decorators/methods/required_fields.py +19 -0
- osbot_utils/fluent/Fluent_Dict.py +19 -0
- osbot_utils/fluent/Fluent_List.py +44 -0
- osbot_utils/fluent/__init__.py +1 -0
- osbot_utils/graphs/__init__.py +0 -0
- osbot_utils/graphs/mermaid/Mermaid.py +75 -0
- osbot_utils/graphs/mermaid/Mermaid__Edge.py +49 -0
- osbot_utils/graphs/mermaid/Mermaid__Graph.py +93 -0
- osbot_utils/graphs/mermaid/Mermaid__Node.py +69 -0
- osbot_utils/graphs/mermaid/Mermaid__Renderer.py +54 -0
- osbot_utils/graphs/mermaid/configs/Mermaid__Edge__Config.py +7 -0
- osbot_utils/graphs/mermaid/configs/Mermaid__Node__Config.py +9 -0
- osbot_utils/graphs/mermaid/configs/Mermaid__Render__Config.py +7 -0
- osbot_utils/graphs/mermaid/examples/Mermaid_Examples__FlowChart.py +98 -0
- osbot_utils/graphs/mermaid/models/Mermaid__Diagram_Direction.py +9 -0
- osbot_utils/graphs/mermaid/models/Mermaid__Diagram__Type.py +17 -0
- osbot_utils/graphs/mermaid/models/Mermaid__Node__Shape.py +30 -0
- osbot_utils/graphs/mgraph/MGraph.py +53 -0
- osbot_utils/graphs/mgraph/MGraph__Config.py +7 -0
- osbot_utils/graphs/mgraph/MGraph__Data.py +139 -0
- osbot_utils/graphs/mgraph/MGraph__Edge.py +27 -0
- osbot_utils/graphs/mgraph/MGraph__Node.py +33 -0
- osbot_utils/graphs/mgraph/MGraph__Random_Graphs.py +27 -0
- osbot_utils/graphs/mgraph/MGraph__Serializer.py +43 -0
- osbot_utils/graphs/mgraph/MGraphs.py +17 -0
- osbot_utils/graphs/mgraph/__init__.py +0 -0
- osbot_utils/helpers/CPrint.py +98 -0
- osbot_utils/helpers/Dict_To_Attr.py +7 -0
- osbot_utils/helpers/Local_Cache.py +111 -0
- osbot_utils/helpers/Local_Caches.py +54 -0
- osbot_utils/helpers/Print_Table.py +369 -0
- osbot_utils/helpers/Python_Audit.py +45 -0
- osbot_utils/helpers/Random_Seed.py +27 -0
- osbot_utils/helpers/SCP.py +58 -0
- osbot_utils/helpers/SSH.py +151 -0
- osbot_utils/helpers/Type_Registry.py +16 -0
- osbot_utils/helpers/__init__.py +0 -0
- osbot_utils/helpers/ast/Ast.py +35 -0
- osbot_utils/helpers/ast/Ast_Base.py +124 -0
- osbot_utils/helpers/ast/Ast_Data.py +28 -0
- osbot_utils/helpers/ast/Ast_Load.py +62 -0
- osbot_utils/helpers/ast/Ast_Merge.py +26 -0
- osbot_utils/helpers/ast/Ast_Node.py +117 -0
- osbot_utils/helpers/ast/Ast_Visit.py +85 -0
- osbot_utils/helpers/ast/Call_Tree.py +38 -0
- osbot_utils/helpers/ast/__init__.py +145 -0
- osbot_utils/helpers/ast/nodes/Ast_Add.py +6 -0
- osbot_utils/helpers/ast/nodes/Ast_Alias.py +6 -0
- osbot_utils/helpers/ast/nodes/Ast_And.py +6 -0
- osbot_utils/helpers/ast/nodes/Ast_Argument.py +7 -0
- osbot_utils/helpers/ast/nodes/Ast_Arguments.py +10 -0
- osbot_utils/helpers/ast/nodes/Ast_Assert.py +7 -0
- osbot_utils/helpers/ast/nodes/Ast_Assign.py +8 -0
- osbot_utils/helpers/ast/nodes/Ast_Attribute.py +9 -0
- osbot_utils/helpers/ast/nodes/Ast_Aug_Assign.py +9 -0
- osbot_utils/helpers/ast/nodes/Ast_Bin_Op.py +8 -0
- osbot_utils/helpers/ast/nodes/Ast_Bool_Op.py +7 -0
- osbot_utils/helpers/ast/nodes/Ast_Break.py +7 -0
- osbot_utils/helpers/ast/nodes/Ast_Call.py +17 -0
- osbot_utils/helpers/ast/nodes/Ast_Class_Def.py +9 -0
- osbot_utils/helpers/ast/nodes/Ast_Compare.py +9 -0
- osbot_utils/helpers/ast/nodes/Ast_Comprehension.py +10 -0
- osbot_utils/helpers/ast/nodes/Ast_Constant.py +6 -0
- osbot_utils/helpers/ast/nodes/Ast_Continue.py +7 -0
- osbot_utils/helpers/ast/nodes/Ast_Dict.py +8 -0
- osbot_utils/helpers/ast/nodes/Ast_Eq.py +6 -0
- osbot_utils/helpers/ast/nodes/Ast_Except_Handler.py +9 -0
- osbot_utils/helpers/ast/nodes/Ast_Expr.py +7 -0
- osbot_utils/helpers/ast/nodes/Ast_For.py +10 -0
- osbot_utils/helpers/ast/nodes/Ast_Function_Def.py +17 -0
- osbot_utils/helpers/ast/nodes/Ast_Generator_Exp.py +8 -0
- osbot_utils/helpers/ast/nodes/Ast_Gt.py +7 -0
- osbot_utils/helpers/ast/nodes/Ast_GtE.py +7 -0
- osbot_utils/helpers/ast/nodes/Ast_If.py +9 -0
- osbot_utils/helpers/ast/nodes/Ast_If_Exp.py +9 -0
- osbot_utils/helpers/ast/nodes/Ast_Import.py +7 -0
- osbot_utils/helpers/ast/nodes/Ast_Import_From.py +7 -0
- osbot_utils/helpers/ast/nodes/Ast_In.py +6 -0
- osbot_utils/helpers/ast/nodes/Ast_Is.py +6 -0
- osbot_utils/helpers/ast/nodes/Ast_Is_Not.py +7 -0
- osbot_utils/helpers/ast/nodes/Ast_Keyword.py +8 -0
- osbot_utils/helpers/ast/nodes/Ast_Lambda.py +8 -0
- osbot_utils/helpers/ast/nodes/Ast_List.py +8 -0
- osbot_utils/helpers/ast/nodes/Ast_List_Comp.py +8 -0
- osbot_utils/helpers/ast/nodes/Ast_Load.py +6 -0
- osbot_utils/helpers/ast/nodes/Ast_Lt.py +7 -0
- osbot_utils/helpers/ast/nodes/Ast_LtE.py +7 -0
- osbot_utils/helpers/ast/nodes/Ast_Mod.py +6 -0
- osbot_utils/helpers/ast/nodes/Ast_Module.py +20 -0
- osbot_utils/helpers/ast/nodes/Ast_Mult.py +6 -0
- osbot_utils/helpers/ast/nodes/Ast_Name.py +6 -0
- osbot_utils/helpers/ast/nodes/Ast_Not.py +7 -0
- osbot_utils/helpers/ast/nodes/Ast_Not_Eq.py +7 -0
- osbot_utils/helpers/ast/nodes/Ast_Not_In.py +6 -0
- osbot_utils/helpers/ast/nodes/Ast_Or.py +7 -0
- osbot_utils/helpers/ast/nodes/Ast_Pass.py +7 -0
- osbot_utils/helpers/ast/nodes/Ast_Pow.py +7 -0
- osbot_utils/helpers/ast/nodes/Ast_Raise.py +8 -0
- osbot_utils/helpers/ast/nodes/Ast_Return.py +7 -0
- osbot_utils/helpers/ast/nodes/Ast_Set.py +7 -0
- osbot_utils/helpers/ast/nodes/Ast_Slice.py +8 -0
- osbot_utils/helpers/ast/nodes/Ast_Starred.py +8 -0
- osbot_utils/helpers/ast/nodes/Ast_Store.py +7 -0
- osbot_utils/helpers/ast/nodes/Ast_Sub.py +7 -0
- osbot_utils/helpers/ast/nodes/Ast_Subscript.py +8 -0
- osbot_utils/helpers/ast/nodes/Ast_Try.py +9 -0
- osbot_utils/helpers/ast/nodes/Ast_Tuple.py +9 -0
- osbot_utils/helpers/ast/nodes/Ast_Unary_Op.py +7 -0
- osbot_utils/helpers/ast/nodes/Ast_While.py +8 -0
- osbot_utils/helpers/ast/nodes/Ast_With.py +7 -0
- osbot_utils/helpers/ast/nodes/Ast_With_Item.py +7 -0
- osbot_utils/helpers/ast/nodes/Ast_Yield.py +7 -0
- osbot_utils/helpers/ast/nodes/__init__.py +0 -0
- osbot_utils/helpers/html/Dict_To_Css.py +20 -0
- osbot_utils/helpers/html/Dict_To_Html.py +59 -0
- osbot_utils/helpers/html/Dict_To_Tags.py +88 -0
- osbot_utils/helpers/html/Html_To_Dict.py +75 -0
- osbot_utils/helpers/html/Html_To_Tag.py +20 -0
- osbot_utils/helpers/html/Tag__Base.py +91 -0
- osbot_utils/helpers/html/Tag__Body.py +5 -0
- osbot_utils/helpers/html/Tag__Div.py +5 -0
- osbot_utils/helpers/html/Tag__H.py +9 -0
- osbot_utils/helpers/html/Tag__HR.py +5 -0
- osbot_utils/helpers/html/Tag__Head.py +32 -0
- osbot_utils/helpers/html/Tag__Html.py +42 -0
- osbot_utils/helpers/html/Tag__Link.py +17 -0
- osbot_utils/helpers/html/Tag__Style.py +25 -0
- osbot_utils/helpers/html/__init__.py +0 -0
- osbot_utils/helpers/pubsub/Event__Queue.py +95 -0
- osbot_utils/helpers/pubsub/PubSub__Client.py +53 -0
- osbot_utils/helpers/pubsub/PubSub__Room.py +13 -0
- osbot_utils/helpers/pubsub/PubSub__Server.py +94 -0
- osbot_utils/helpers/pubsub/PubSub__Sqlite.py +24 -0
- osbot_utils/helpers/pubsub/__init__.py +0 -0
- osbot_utils/helpers/pubsub/schemas/Schema__Event.py +15 -0
- osbot_utils/helpers/pubsub/schemas/Schema__Event__Connect.py +7 -0
- osbot_utils/helpers/pubsub/schemas/Schema__Event__Disconnect.py +7 -0
- osbot_utils/helpers/pubsub/schemas/Schema__Event__Join_Room.py +8 -0
- osbot_utils/helpers/pubsub/schemas/Schema__Event__Leave_Room.py +8 -0
- osbot_utils/helpers/pubsub/schemas/Schema__Event__Message.py +7 -0
- osbot_utils/helpers/pubsub/schemas/Schema__PubSub__Client.py +8 -0
- osbot_utils/helpers/pubsub/schemas/__init__.py +0 -0
- osbot_utils/helpers/sqlite/Capture_Sqlite_Error.py +51 -0
- osbot_utils/helpers/sqlite/Sqlite__Cursor.py +87 -0
- osbot_utils/helpers/sqlite/Sqlite__Database.py +137 -0
- osbot_utils/helpers/sqlite/Sqlite__Field.py +70 -0
- osbot_utils/helpers/sqlite/Sqlite__Globals.py +5 -0
- osbot_utils/helpers/sqlite/Sqlite__Table.py +293 -0
- osbot_utils/helpers/sqlite/Sqlite__Table__Create.py +96 -0
- osbot_utils/helpers/sqlite/Temp_Sqlite__Database__Disk.py +17 -0
- osbot_utils/helpers/sqlite/Temp_Sqlite__Table.py +23 -0
- osbot_utils/helpers/sqlite/__init__.py +0 -0
- osbot_utils/helpers/sqlite/domains/Sqlite__Cache__Requests.py +214 -0
- osbot_utils/helpers/sqlite/domains/Sqlite__Cache__Requests__Patch.py +63 -0
- osbot_utils/helpers/sqlite/domains/Sqlite__DB__Files.py +23 -0
- osbot_utils/helpers/sqlite/domains/Sqlite__DB__Graph.py +47 -0
- osbot_utils/helpers/sqlite/domains/Sqlite__DB__Json.py +83 -0
- osbot_utils/helpers/sqlite/domains/Sqlite__DB__Local.py +20 -0
- osbot_utils/helpers/sqlite/domains/Sqlite__DB__Requests.py +39 -0
- osbot_utils/helpers/sqlite/domains/__init__.py +0 -0
- osbot_utils/helpers/sqlite/domains/schemas/Schema__Table__Requests.py +12 -0
- osbot_utils/helpers/sqlite/domains/schemas/__init__.py +0 -0
- osbot_utils/helpers/sqlite/models/Sqlite__Field__Type.py +37 -0
- osbot_utils/helpers/sqlite/models/__init__.py +0 -0
- osbot_utils/helpers/sqlite/sample_data/Sqlite__Sample_Data__Chinook.py +116 -0
- osbot_utils/helpers/sqlite/sample_data/__init__.py +0 -0
- osbot_utils/helpers/sqlite/sql_builder/SQL_Builder.py +159 -0
- osbot_utils/helpers/sqlite/sql_builder/SQL_Builder__Select.py +12 -0
- osbot_utils/helpers/sqlite/sql_builder/__init__.py +0 -0
- osbot_utils/helpers/sqlite/tables/Sqlite__Table__Config.py +63 -0
- osbot_utils/helpers/sqlite/tables/Sqlite__Table__Edges.py +46 -0
- osbot_utils/helpers/sqlite/tables/Sqlite__Table__Files.py +45 -0
- osbot_utils/helpers/sqlite/tables/Sqlite__Table__Nodes.py +52 -0
- osbot_utils/helpers/sqlite/tables/__init__.py +0 -0
- osbot_utils/helpers/trace/Trace_Call.py +120 -0
- osbot_utils/helpers/trace/Trace_Call__Config.py +94 -0
- osbot_utils/helpers/trace/Trace_Call__Graph.py +26 -0
- osbot_utils/helpers/trace/Trace_Call__Handler.py +215 -0
- osbot_utils/helpers/trace/Trace_Call__Print_Lines.py +85 -0
- osbot_utils/helpers/trace/Trace_Call__Print_Traces.py +170 -0
- osbot_utils/helpers/trace/Trace_Call__Stack.py +166 -0
- osbot_utils/helpers/trace/Trace_Call__Stack_Node.py +59 -0
- osbot_utils/helpers/trace/Trace_Call__Stats.py +71 -0
- osbot_utils/helpers/trace/Trace_Call__View_Model.py +75 -0
- osbot_utils/helpers/trace/Trace_Files.py +33 -0
- osbot_utils/helpers/trace/__init__.py +0 -0
- osbot_utils/testing/Catch.py +54 -0
- osbot_utils/testing/Duration.py +69 -0
- osbot_utils/testing/Hook_Method.py +118 -0
- osbot_utils/testing/Log_To_Queue.py +46 -0
- osbot_utils/testing/Log_To_String.py +37 -0
- osbot_utils/testing/Logging.py +81 -0
- osbot_utils/testing/Patch_Print.py +52 -0
- osbot_utils/testing/Profiler.py +89 -0
- osbot_utils/testing/Stderr.py +19 -0
- osbot_utils/testing/Stdout.py +19 -0
- osbot_utils/testing/Temp_File.py +46 -0
- osbot_utils/testing/Temp_Folder.py +114 -0
- osbot_utils/testing/Temp_Sys_Path.py +13 -0
- osbot_utils/testing/Temp_Web_Server.py +83 -0
- osbot_utils/testing/Temp_Zip.py +45 -0
- osbot_utils/testing/Temp_Zip_In_Memory.py +90 -0
- osbot_utils/testing/Unit_Test.py +34 -0
- osbot_utils/testing/Unzip_File.py +30 -0
- osbot_utils/testing/__init__.py +0 -0
- osbot_utils/utils/Assert.py +52 -0
- osbot_utils/utils/Call_Stack.py +187 -0
- osbot_utils/utils/Csv.py +32 -0
- osbot_utils/utils/Dev.py +47 -0
- osbot_utils/utils/Exceptions.py +7 -0
- osbot_utils/utils/Files.py +528 -0
- osbot_utils/utils/Functions.py +113 -0
- osbot_utils/utils/Http.py +136 -0
- osbot_utils/utils/Int.py +6 -0
- osbot_utils/utils/Json.py +171 -0
- osbot_utils/utils/Json_Cache.py +59 -0
- osbot_utils/utils/Lists.py +198 -0
- osbot_utils/utils/Misc.py +496 -0
- osbot_utils/utils/Objects.py +341 -0
- osbot_utils/utils/Png.py +29 -0
- osbot_utils/utils/Process.py +73 -0
- osbot_utils/utils/Python_Logger.py +301 -0
- osbot_utils/utils/Status.py +79 -0
- osbot_utils/utils/Str.py +63 -0
- osbot_utils/utils/Version.py +16 -0
- osbot_utils/utils/Zip.py +97 -0
- osbot_utils/utils/__init__.py +16 -0
- osbot_utils/version +1 -0
- osbot_utils-1.7.7.dist-info/LICENSE +201 -0
- osbot_utils-1.7.7.dist-info/METADATA +46 -0
- osbot_utils-1.7.7.dist-info/RECORD +260 -0
- osbot_utils-1.7.7.dist-info/WHEEL +4 -0
@@ -0,0 +1,341 @@
|
|
1
|
+
# todo add tests
|
2
|
+
import inspect
|
3
|
+
import io
|
4
|
+
import json
|
5
|
+
import os
|
6
|
+
import pickle
|
7
|
+
import types
|
8
|
+
from typing import get_origin, Union, get_args
|
9
|
+
|
10
|
+
from dotenv import load_dotenv
|
11
|
+
|
12
|
+
from osbot_utils.utils.Misc import list_set
|
13
|
+
from osbot_utils.utils.Str import str_unicode_escape, str_max_width
|
14
|
+
|
15
|
+
def are_types_compatible_for_assigment(source_type, target_type):
|
16
|
+
if source_type is target_type:
|
17
|
+
return True
|
18
|
+
if source_type is int and target_type is float:
|
19
|
+
return True
|
20
|
+
if target_type in source_type.__mro__: # this means that the source_type has the target_type has of its base types
|
21
|
+
return True
|
22
|
+
|
23
|
+
return False
|
24
|
+
|
25
|
+
def are_types_magic_mock(source_type, target_type):
|
26
|
+
from unittest.mock import MagicMock
|
27
|
+
if isinstance(source_type, MagicMock):
|
28
|
+
return True
|
29
|
+
if isinstance(target_type, MagicMock):
|
30
|
+
return True
|
31
|
+
if source_type is MagicMock:
|
32
|
+
return True
|
33
|
+
if target_type is MagicMock:
|
34
|
+
return True
|
35
|
+
# if class_full_name(source_type) == 'unittest.mock.MagicMock':
|
36
|
+
# return True
|
37
|
+
# if class_full_name(target_type) == 'unittest.mock.MagicMock':
|
38
|
+
# return True
|
39
|
+
return False
|
40
|
+
|
41
|
+
def base_classes(cls):
|
42
|
+
if type(cls) is type:
|
43
|
+
target = cls
|
44
|
+
else:
|
45
|
+
target = type(cls)
|
46
|
+
return type_base_classes(target)
|
47
|
+
|
48
|
+
def class_functions_names(target):
|
49
|
+
return list_set(class_functions(target))
|
50
|
+
|
51
|
+
def class_functions(target):
|
52
|
+
functions = {}
|
53
|
+
for function_name, function_ref in inspect.getmembers(type(target), predicate=inspect.isfunction):
|
54
|
+
functions[function_name] = function_ref
|
55
|
+
return functions
|
56
|
+
|
57
|
+
def class_name(target):
|
58
|
+
if target:
|
59
|
+
return type(target).__name__
|
60
|
+
|
61
|
+
def class_full_name(target):
|
62
|
+
if target:
|
63
|
+
type_target = type(target)
|
64
|
+
type_module = type_target.__module__
|
65
|
+
type_name = type_target.__name__
|
66
|
+
return f'{type_module}.{type_name}'
|
67
|
+
|
68
|
+
def default_value(target : type):
|
69
|
+
try:
|
70
|
+
return target() # try to create the object using the default constructor
|
71
|
+
except TypeError:
|
72
|
+
return None # if not return None
|
73
|
+
|
74
|
+
def dict_remove(data, target):
|
75
|
+
if type(data) is dict:
|
76
|
+
if type(target) is list:
|
77
|
+
for key in list(data.keys()):
|
78
|
+
if key in target:
|
79
|
+
del data[key]
|
80
|
+
else:
|
81
|
+
if target in data:
|
82
|
+
del data[target]
|
83
|
+
return data
|
84
|
+
|
85
|
+
def enum_from_value(enum_type, value):
|
86
|
+
try:
|
87
|
+
return enum_type[value] # Attempt to convert the value to an Enum member by name
|
88
|
+
except KeyError:
|
89
|
+
raise ValueError(f"Value '{value}' is not a valid member of {enum_type.__name__}.") # Handle the case where the value does not match any Enum member
|
90
|
+
|
91
|
+
def env_value(var_name):
|
92
|
+
return env_vars().get(var_name, None)
|
93
|
+
|
94
|
+
def env_vars_list():
|
95
|
+
return list_set(env_vars())
|
96
|
+
|
97
|
+
def env_vars(reload_vars=False):
|
98
|
+
"""
|
99
|
+
if reload_vars reload data from .env file
|
100
|
+
then return dictionary with current environment variables
|
101
|
+
"""
|
102
|
+
if reload_vars:
|
103
|
+
load_dotenv()
|
104
|
+
vars = os.environ
|
105
|
+
data = {}
|
106
|
+
for key in vars:
|
107
|
+
data[key] = vars[key]
|
108
|
+
return data
|
109
|
+
|
110
|
+
def get_field(target, field, default=None):
|
111
|
+
if target is not None:
|
112
|
+
try:
|
113
|
+
value = getattr(target, field)
|
114
|
+
if value is not None:
|
115
|
+
return value
|
116
|
+
except:
|
117
|
+
pass
|
118
|
+
return default
|
119
|
+
|
120
|
+
def get_missing_fields(target,fields):
|
121
|
+
missing_fields = []
|
122
|
+
if fields:
|
123
|
+
for field in fields:
|
124
|
+
if get_field(target, field) is None:
|
125
|
+
missing_fields.append(field)
|
126
|
+
return missing_fields
|
127
|
+
|
128
|
+
def get_value(target, key, default=None):
|
129
|
+
if target is not None:
|
130
|
+
value = target.get(key)
|
131
|
+
if value is not None:
|
132
|
+
return value
|
133
|
+
return default
|
134
|
+
|
135
|
+
def print_object_methods(target, name_width=30, value_width=100, show_private=False, show_internals=False):
|
136
|
+
print_object_members(target, name_width=name_width, value_width=value_width,show_private=show_private,show_internals=show_internals, only_show_methods=True)
|
137
|
+
|
138
|
+
def print_obj_data_aligned(obj_data):
|
139
|
+
print(obj_data_aligned(obj_data))
|
140
|
+
|
141
|
+
def print_obj_data_as_dict(target, **kwargs):
|
142
|
+
data = obj_data(target, **kwargs)
|
143
|
+
indented_items = obj_data_aligned(data, tab_size=5)
|
144
|
+
print("dict(" + indented_items + " )")
|
145
|
+
return data
|
146
|
+
|
147
|
+
def obj_data_aligned(obj_data, tab_size=0):
|
148
|
+
max_key_length = max(len(k) for k in obj_data.keys()) # Find the maximum key length
|
149
|
+
items = [f"{k:<{max_key_length}} = {v!r:6}," for k, v in obj_data.items()] # Format each key-value pair
|
150
|
+
items[-1] = items[-1][:-2] # Remove comma from the last item
|
151
|
+
tab_string = f"\n{' ' * tab_size }" # apply tabbing (if needed)
|
152
|
+
indented_items = tab_string.join(items) # Join the items with newline and
|
153
|
+
return indented_items
|
154
|
+
|
155
|
+
# todo: add option to not show class methods that are not bultin types
|
156
|
+
def print_object_members(target, name_width=30, value_width=100, show_private=False, show_internals=False, show_value_class=False, show_methods=False, only_show_methods=False):
|
157
|
+
max_width = name_width + value_width
|
158
|
+
print()
|
159
|
+
print(f"Members for object:\n\t {target} of type:{type(target)}")
|
160
|
+
print(f"Settings:\n\t name_width: {name_width} | value_width: {value_width} | show_private: {show_private} | show_internals: {show_internals}")
|
161
|
+
print()
|
162
|
+
if only_show_methods:
|
163
|
+
show_methods = True # need to make sure this setting is True, or there will be no methods to show
|
164
|
+
print(f"{'method':<{name_width}} (params)")
|
165
|
+
else:
|
166
|
+
if show_value_class:
|
167
|
+
print(f"{'field':<{name_width}} | {'type':<{name_width}} |value")
|
168
|
+
else:
|
169
|
+
print(f"{'field':<{name_width}} | value")
|
170
|
+
|
171
|
+
print(f"{'-' * max_width}")
|
172
|
+
for name, value in obj_data(target, name_width=name_width, value_width=value_width, show_private=show_private, show_internals=show_internals, show_value_class=show_value_class, show_methods=show_methods, only_show_methods=only_show_methods).items():
|
173
|
+
if only_show_methods:
|
174
|
+
print(f"{name:<{name_width}} {value}"[:max_width])
|
175
|
+
else:
|
176
|
+
if show_value_class:
|
177
|
+
value_class = obj_full_name(value)
|
178
|
+
print(f"{name:<{name_width}} | {value_class:{name_width}} | {value}"[:max_width])
|
179
|
+
else:
|
180
|
+
print(f"{name:<{name_width}} | {value}"[:max_width])
|
181
|
+
|
182
|
+
def obj_base_classes(obj):
|
183
|
+
return [obj_type for obj_type in type_base_classes(type(obj))]
|
184
|
+
|
185
|
+
def type_mro(target):
|
186
|
+
if type(target) is type:
|
187
|
+
cls = target
|
188
|
+
else:
|
189
|
+
cls = type(target)
|
190
|
+
return list(inspect.getmro(cls))
|
191
|
+
|
192
|
+
def type_base_classes(cls):
|
193
|
+
base_classes = cls.__bases__
|
194
|
+
all_base_classes = list(base_classes)
|
195
|
+
for base in base_classes:
|
196
|
+
all_base_classes.extend(type_base_classes(base))
|
197
|
+
return all_base_classes
|
198
|
+
|
199
|
+
def obj_base_classes_names(obj, show_module=False):
|
200
|
+
names = []
|
201
|
+
for base in obj_base_classes(obj):
|
202
|
+
if show_module:
|
203
|
+
names.append(base.__module__ + '.' + base.__name__)
|
204
|
+
else:
|
205
|
+
names.append(base.__name__)
|
206
|
+
return names
|
207
|
+
|
208
|
+
def obj_data(target, name_width=30, value_width=100, show_private=False, show_internals=False, show_value_class=False, show_methods=False, only_show_methods=False):
|
209
|
+
result = {}
|
210
|
+
if show_internals:
|
211
|
+
show_private = True # show_private will skip all internals, so need to make sure it is True
|
212
|
+
for name, value in inspect.getmembers(target):
|
213
|
+
if show_methods is False and type(value) is types.MethodType:
|
214
|
+
continue
|
215
|
+
if only_show_methods and type(value) is not types.MethodType:
|
216
|
+
continue
|
217
|
+
if not show_private and name.startswith("_"):
|
218
|
+
continue
|
219
|
+
if not show_internals and name.startswith("__"):
|
220
|
+
continue
|
221
|
+
if only_show_methods:
|
222
|
+
value = inspect.signature(value)
|
223
|
+
if value !=None and type(value) not in [bool, int, float]:
|
224
|
+
value = str(value).encode('unicode_escape').decode("utf-8")
|
225
|
+
value = str_unicode_escape(value)
|
226
|
+
value = str_max_width(value, value_width)
|
227
|
+
name = str_max_width(name, name_width)
|
228
|
+
result[name] = value
|
229
|
+
return result
|
230
|
+
|
231
|
+
# def obj_data(target=None):
|
232
|
+
# data = {}
|
233
|
+
# for key,value in obj_items(target):
|
234
|
+
# data[key] = value
|
235
|
+
# return data
|
236
|
+
|
237
|
+
def obj_dict(target=None):
|
238
|
+
if target and hasattr(target,'__dict__'):
|
239
|
+
return target.__dict__
|
240
|
+
return {}
|
241
|
+
|
242
|
+
def obj_items(target=None):
|
243
|
+
return sorted(list(obj_dict(target).items()))
|
244
|
+
|
245
|
+
def obj_keys(target=None):
|
246
|
+
return sorted(list(obj_dict(target).keys()))
|
247
|
+
|
248
|
+
def obj_full_name(target):
|
249
|
+
module = target.__class__.__module__
|
250
|
+
name = target.__class__.__qualname__
|
251
|
+
return f"{module}.{name}"
|
252
|
+
|
253
|
+
def obj_get_value(target=None, key=None, default=None):
|
254
|
+
return get_field(target=target, field=key, default=default)
|
255
|
+
|
256
|
+
def obj_values(target=None):
|
257
|
+
return list(obj_dict(target).values())
|
258
|
+
|
259
|
+
def raise_exception_on_obj_type_annotation_mismatch(target, attr_name, value):
|
260
|
+
# todo : check if this is is not causing the type safety issues
|
261
|
+
if value_type_matches_obj_annotation_for_attr(target, attr_name, value) is False: # handle case with normal types
|
262
|
+
if value_type_matches_obj_annotation_for_union_attr(target, attr_name, value) is True: # handle union cases
|
263
|
+
return # this is done like this because value_type_matches_obj_annotation_for_union_attr will return None when there is no Union objects
|
264
|
+
raise Exception(f"Invalid type for attribute '{attr_name}'. Expected '{target.__annotations__.get(attr_name)}' but got '{type(value)}'")
|
265
|
+
|
266
|
+
def obj_attribute_annotation(target, attr_name):
|
267
|
+
if target is not None and attr_name is not None:
|
268
|
+
if hasattr(target, '__annotations__'):
|
269
|
+
obj_annotations = target.__annotations__
|
270
|
+
if hasattr(obj_annotations,'get'):
|
271
|
+
attribute_annotation = obj_annotations.get(attr_name)
|
272
|
+
return attribute_annotation
|
273
|
+
return None
|
274
|
+
|
275
|
+
def obj_is_attribute_annotation_of_type(target, attr_name, expected_type):
|
276
|
+
attribute_annotation = obj_attribute_annotation(target, attr_name)
|
277
|
+
attribute_type = type(attribute_annotation)
|
278
|
+
return attribute_type is expected_type
|
279
|
+
|
280
|
+
def obj_is_type_union_compatible(var_type, compatible_types):
|
281
|
+
origin = get_origin(var_type)
|
282
|
+
if origin is Union: # For Union types, including Optionals
|
283
|
+
args = get_args(var_type) # Get the argument types
|
284
|
+
for arg in args: # Iterate through each argument in the Union
|
285
|
+
if not (arg in compatible_types or arg is type(None)): # Check if the argument is either in the compatible_types or is type(None)
|
286
|
+
return False # If any arg doesn't meet the criteria, return False immediately
|
287
|
+
return True # If all args are compatible, return True
|
288
|
+
return var_type in compatible_types or var_type is type(None) # Check for direct compatibility or type(None) for non-Union types
|
289
|
+
|
290
|
+
def value_type_matches_obj_annotation_for_union_attr(target, attr_name, value):
|
291
|
+
value_type = type(value)
|
292
|
+
attribute_annotation = obj_attribute_annotation(target,attr_name)
|
293
|
+
origin = get_origin(attribute_annotation)
|
294
|
+
if origin is Union: # For Union types, including Optionals
|
295
|
+
args = get_args(attribute_annotation) # Get the argument types
|
296
|
+
return value_type in args
|
297
|
+
return None # if it is not an Union type just return None (to give an indication to the caller that the comparison was not made)
|
298
|
+
|
299
|
+
|
300
|
+
def pickle_save_to_bytes(target: object) -> bytes:
|
301
|
+
return pickle.dumps(target)
|
302
|
+
|
303
|
+
def pickle_load_from_bytes(pickled_data: bytes):
|
304
|
+
if type(pickled_data) is bytes:
|
305
|
+
return pickle.loads(pickled_data)
|
306
|
+
|
307
|
+
def value_type_matches_obj_annotation_for_attr(target, attr_name, value):
|
308
|
+
if hasattr(target, '__annotations__'):
|
309
|
+
obj_annotations = target.__annotations__
|
310
|
+
if hasattr(obj_annotations,'get'):
|
311
|
+
attr_type = obj_annotations.get(attr_name)
|
312
|
+
if attr_type:
|
313
|
+
origin_attr_type = get_origin(attr_type) # to handle when type definion contains an generic
|
314
|
+
if origin_attr_type:
|
315
|
+
attr_type = origin_attr_type
|
316
|
+
value_type = type(value)
|
317
|
+
if are_types_compatible_for_assigment(source_type=value_type, target_type=attr_type):
|
318
|
+
return True
|
319
|
+
if are_types_magic_mock(source_type=value_type, target_type=attr_type):
|
320
|
+
return True
|
321
|
+
|
322
|
+
return value_type is attr_type
|
323
|
+
return None
|
324
|
+
|
325
|
+
|
326
|
+
|
327
|
+
|
328
|
+
|
329
|
+
# helper duplicate methods
|
330
|
+
base_types = base_classes
|
331
|
+
|
332
|
+
full_type_name = class_full_name
|
333
|
+
|
334
|
+
obj_list_set = obj_keys
|
335
|
+
obj_info = print_object_members
|
336
|
+
obj_methods = print_object_methods
|
337
|
+
|
338
|
+
obj_to_bytes = pickle_save_to_bytes
|
339
|
+
bytes_to_obj = pickle_load_from_bytes
|
340
|
+
|
341
|
+
type_full_name = class_full_name
|
osbot_utils/utils/Png.py
ADDED
@@ -0,0 +1,29 @@
|
|
1
|
+
import base64
|
2
|
+
import logging
|
3
|
+
|
4
|
+
from osbot_utils.utils.Dev import Dev
|
5
|
+
|
6
|
+
from osbot_utils.utils.Files import temp_file
|
7
|
+
from osbot_utils.utils.Misc import bytes_to_base64
|
8
|
+
|
9
|
+
logger_png = logging.getLogger()
|
10
|
+
|
11
|
+
def save_png_bytes_to_file(bytes, png_file=None):
|
12
|
+
png_data = bytes_to_base64(bytes)
|
13
|
+
return save_png_base64_to_file(png_data, png_file)
|
14
|
+
|
15
|
+
def save_png_base64_to_file(png_data, png_file=None):
|
16
|
+
if png_data is not None:
|
17
|
+
if type(png_data) is not str:
|
18
|
+
logger_png.error(f'Png data was not a string: {png_data}')
|
19
|
+
else:
|
20
|
+
if png_file is None:
|
21
|
+
png_file = temp_file('.png')
|
22
|
+
try:
|
23
|
+
with open(png_file, "wb") as fh:
|
24
|
+
fh.write(base64.decodebytes(png_data.encode()))
|
25
|
+
logger_png.error(f'Png data with size {len(png_data)} saved to {png_file}') # note: this is currently set to error because nothing else seems to be picked up by logging.getLogger().addHandler(logging.StreamHandler())
|
26
|
+
return png_file
|
27
|
+
except Exception as error:
|
28
|
+
logger_png.error(f'png save error: {error}')
|
29
|
+
logger_png.error(png_data)
|
@@ -0,0 +1,73 @@
|
|
1
|
+
import os
|
2
|
+
import signal
|
3
|
+
import subprocess
|
4
|
+
|
5
|
+
|
6
|
+
#def run_process(executable, params = None, cwd='.'):
|
7
|
+
# return Process.run(executable, params, cwd)
|
8
|
+
|
9
|
+
def chmod_x(executable_path):
|
10
|
+
return run_process("chmod", ['+x', executable_path])
|
11
|
+
|
12
|
+
class Process:
|
13
|
+
|
14
|
+
@staticmethod
|
15
|
+
def run(executable, params = None, cwd='.', **run_kwargs):
|
16
|
+
params = params or []
|
17
|
+
if type(params) is str:
|
18
|
+
params = [params]
|
19
|
+
run_params = [executable] + params
|
20
|
+
error = None
|
21
|
+
stderr = ''
|
22
|
+
stdout = ''
|
23
|
+
kwargs = { 'cwd' : cwd ,
|
24
|
+
'stdout' : subprocess.PIPE ,
|
25
|
+
'stderr' : subprocess.PIPE ,
|
26
|
+
'timeout': None }
|
27
|
+
kwargs = { **kwargs , **run_kwargs } # merge dictionaries with run_kwargs taking precedence
|
28
|
+
try:
|
29
|
+
result = subprocess.run(run_params, **kwargs)
|
30
|
+
stderr = result.stderr.decode()
|
31
|
+
stdout = result.stdout.decode()
|
32
|
+
status = "ok"
|
33
|
+
except subprocess.TimeoutExpired as timeout_error:
|
34
|
+
if timeout_error.stderr:
|
35
|
+
stderr = timeout_error.stderr.decode()
|
36
|
+
if timeout_error.stdout:
|
37
|
+
stdout = timeout_error.stdout.decode()
|
38
|
+
error = timeout_error
|
39
|
+
status = 'error'
|
40
|
+
except Exception as exception:
|
41
|
+
error = exception
|
42
|
+
status = 'error'
|
43
|
+
return {
|
44
|
+
"cwd" : cwd ,
|
45
|
+
"error" : error ,
|
46
|
+
"kwargs" : kwargs ,
|
47
|
+
"runParams" : run_params ,
|
48
|
+
"status" : status ,
|
49
|
+
"stdout" : stdout ,
|
50
|
+
"stderr" : stderr
|
51
|
+
}
|
52
|
+
@staticmethod
|
53
|
+
def stop(pid):
|
54
|
+
return os.kill(pid, signal.SIGKILL)
|
55
|
+
|
56
|
+
# exec helpers
|
57
|
+
@staticmethod
|
58
|
+
def exec_open(file_path, cwd='.'): return Process.run("open", [file_path], cwd)
|
59
|
+
|
60
|
+
|
61
|
+
kill_process = Process.stop
|
62
|
+
run_process = Process.run
|
63
|
+
exec_open = Process.exec_open
|
64
|
+
exec_process = Process.run
|
65
|
+
process_run = Process.run
|
66
|
+
start_process = Process.run
|
67
|
+
stop_process = Process.stop
|
68
|
+
|
69
|
+
|
70
|
+
|
71
|
+
|
72
|
+
#def run_process(executable, params = None, cwd='.'):
|
73
|
+
# return Process.run(executable, params, cwd)
|