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.
Files changed (260) hide show
  1. osbot_utils/__init__.py +1 -0
  2. osbot_utils/base_classes/Cache_Pickle.py +129 -0
  3. osbot_utils/base_classes/Kwargs_To_Disk.py +27 -0
  4. osbot_utils/base_classes/Kwargs_To_Self.py +308 -0
  5. osbot_utils/base_classes/Type_Safe__List.py +14 -0
  6. osbot_utils/base_classes/__init__.py +0 -0
  7. osbot_utils/context_managers/__init__.py +0 -0
  8. osbot_utils/context_managers/capture_duration.py +33 -0
  9. osbot_utils/decorators/__init__.py +0 -0
  10. osbot_utils/decorators/classes/__init__.py +0 -0
  11. osbot_utils/decorators/classes/singleton.py +9 -0
  12. osbot_utils/decorators/lists/__init__.py +0 -0
  13. osbot_utils/decorators/lists/filter_list.py +12 -0
  14. osbot_utils/decorators/lists/group_by.py +21 -0
  15. osbot_utils/decorators/lists/index_by.py +27 -0
  16. osbot_utils/decorators/methods/__init__.py +0 -0
  17. osbot_utils/decorators/methods/cache.py +19 -0
  18. osbot_utils/decorators/methods/cache_on_function.py +56 -0
  19. osbot_utils/decorators/methods/cache_on_self.py +78 -0
  20. osbot_utils/decorators/methods/cache_on_tmp.py +71 -0
  21. osbot_utils/decorators/methods/capture_exception.py +37 -0
  22. osbot_utils/decorators/methods/capture_status.py +20 -0
  23. osbot_utils/decorators/methods/catch.py +13 -0
  24. osbot_utils/decorators/methods/context.py +11 -0
  25. osbot_utils/decorators/methods/depreciated.py +79 -0
  26. osbot_utils/decorators/methods/function_type_check.py +62 -0
  27. osbot_utils/decorators/methods/obj_as_context.py +6 -0
  28. osbot_utils/decorators/methods/remove_return_value.py +22 -0
  29. osbot_utils/decorators/methods/required_fields.py +19 -0
  30. osbot_utils/fluent/Fluent_Dict.py +19 -0
  31. osbot_utils/fluent/Fluent_List.py +44 -0
  32. osbot_utils/fluent/__init__.py +1 -0
  33. osbot_utils/graphs/__init__.py +0 -0
  34. osbot_utils/graphs/mermaid/Mermaid.py +75 -0
  35. osbot_utils/graphs/mermaid/Mermaid__Edge.py +49 -0
  36. osbot_utils/graphs/mermaid/Mermaid__Graph.py +93 -0
  37. osbot_utils/graphs/mermaid/Mermaid__Node.py +69 -0
  38. osbot_utils/graphs/mermaid/Mermaid__Renderer.py +54 -0
  39. osbot_utils/graphs/mermaid/configs/Mermaid__Edge__Config.py +7 -0
  40. osbot_utils/graphs/mermaid/configs/Mermaid__Node__Config.py +9 -0
  41. osbot_utils/graphs/mermaid/configs/Mermaid__Render__Config.py +7 -0
  42. osbot_utils/graphs/mermaid/examples/Mermaid_Examples__FlowChart.py +98 -0
  43. osbot_utils/graphs/mermaid/models/Mermaid__Diagram_Direction.py +9 -0
  44. osbot_utils/graphs/mermaid/models/Mermaid__Diagram__Type.py +17 -0
  45. osbot_utils/graphs/mermaid/models/Mermaid__Node__Shape.py +30 -0
  46. osbot_utils/graphs/mgraph/MGraph.py +53 -0
  47. osbot_utils/graphs/mgraph/MGraph__Config.py +7 -0
  48. osbot_utils/graphs/mgraph/MGraph__Data.py +139 -0
  49. osbot_utils/graphs/mgraph/MGraph__Edge.py +27 -0
  50. osbot_utils/graphs/mgraph/MGraph__Node.py +33 -0
  51. osbot_utils/graphs/mgraph/MGraph__Random_Graphs.py +27 -0
  52. osbot_utils/graphs/mgraph/MGraph__Serializer.py +43 -0
  53. osbot_utils/graphs/mgraph/MGraphs.py +17 -0
  54. osbot_utils/graphs/mgraph/__init__.py +0 -0
  55. osbot_utils/helpers/CPrint.py +98 -0
  56. osbot_utils/helpers/Dict_To_Attr.py +7 -0
  57. osbot_utils/helpers/Local_Cache.py +111 -0
  58. osbot_utils/helpers/Local_Caches.py +54 -0
  59. osbot_utils/helpers/Print_Table.py +369 -0
  60. osbot_utils/helpers/Python_Audit.py +45 -0
  61. osbot_utils/helpers/Random_Seed.py +27 -0
  62. osbot_utils/helpers/SCP.py +58 -0
  63. osbot_utils/helpers/SSH.py +151 -0
  64. osbot_utils/helpers/Type_Registry.py +16 -0
  65. osbot_utils/helpers/__init__.py +0 -0
  66. osbot_utils/helpers/ast/Ast.py +35 -0
  67. osbot_utils/helpers/ast/Ast_Base.py +124 -0
  68. osbot_utils/helpers/ast/Ast_Data.py +28 -0
  69. osbot_utils/helpers/ast/Ast_Load.py +62 -0
  70. osbot_utils/helpers/ast/Ast_Merge.py +26 -0
  71. osbot_utils/helpers/ast/Ast_Node.py +117 -0
  72. osbot_utils/helpers/ast/Ast_Visit.py +85 -0
  73. osbot_utils/helpers/ast/Call_Tree.py +38 -0
  74. osbot_utils/helpers/ast/__init__.py +145 -0
  75. osbot_utils/helpers/ast/nodes/Ast_Add.py +6 -0
  76. osbot_utils/helpers/ast/nodes/Ast_Alias.py +6 -0
  77. osbot_utils/helpers/ast/nodes/Ast_And.py +6 -0
  78. osbot_utils/helpers/ast/nodes/Ast_Argument.py +7 -0
  79. osbot_utils/helpers/ast/nodes/Ast_Arguments.py +10 -0
  80. osbot_utils/helpers/ast/nodes/Ast_Assert.py +7 -0
  81. osbot_utils/helpers/ast/nodes/Ast_Assign.py +8 -0
  82. osbot_utils/helpers/ast/nodes/Ast_Attribute.py +9 -0
  83. osbot_utils/helpers/ast/nodes/Ast_Aug_Assign.py +9 -0
  84. osbot_utils/helpers/ast/nodes/Ast_Bin_Op.py +8 -0
  85. osbot_utils/helpers/ast/nodes/Ast_Bool_Op.py +7 -0
  86. osbot_utils/helpers/ast/nodes/Ast_Break.py +7 -0
  87. osbot_utils/helpers/ast/nodes/Ast_Call.py +17 -0
  88. osbot_utils/helpers/ast/nodes/Ast_Class_Def.py +9 -0
  89. osbot_utils/helpers/ast/nodes/Ast_Compare.py +9 -0
  90. osbot_utils/helpers/ast/nodes/Ast_Comprehension.py +10 -0
  91. osbot_utils/helpers/ast/nodes/Ast_Constant.py +6 -0
  92. osbot_utils/helpers/ast/nodes/Ast_Continue.py +7 -0
  93. osbot_utils/helpers/ast/nodes/Ast_Dict.py +8 -0
  94. osbot_utils/helpers/ast/nodes/Ast_Eq.py +6 -0
  95. osbot_utils/helpers/ast/nodes/Ast_Except_Handler.py +9 -0
  96. osbot_utils/helpers/ast/nodes/Ast_Expr.py +7 -0
  97. osbot_utils/helpers/ast/nodes/Ast_For.py +10 -0
  98. osbot_utils/helpers/ast/nodes/Ast_Function_Def.py +17 -0
  99. osbot_utils/helpers/ast/nodes/Ast_Generator_Exp.py +8 -0
  100. osbot_utils/helpers/ast/nodes/Ast_Gt.py +7 -0
  101. osbot_utils/helpers/ast/nodes/Ast_GtE.py +7 -0
  102. osbot_utils/helpers/ast/nodes/Ast_If.py +9 -0
  103. osbot_utils/helpers/ast/nodes/Ast_If_Exp.py +9 -0
  104. osbot_utils/helpers/ast/nodes/Ast_Import.py +7 -0
  105. osbot_utils/helpers/ast/nodes/Ast_Import_From.py +7 -0
  106. osbot_utils/helpers/ast/nodes/Ast_In.py +6 -0
  107. osbot_utils/helpers/ast/nodes/Ast_Is.py +6 -0
  108. osbot_utils/helpers/ast/nodes/Ast_Is_Not.py +7 -0
  109. osbot_utils/helpers/ast/nodes/Ast_Keyword.py +8 -0
  110. osbot_utils/helpers/ast/nodes/Ast_Lambda.py +8 -0
  111. osbot_utils/helpers/ast/nodes/Ast_List.py +8 -0
  112. osbot_utils/helpers/ast/nodes/Ast_List_Comp.py +8 -0
  113. osbot_utils/helpers/ast/nodes/Ast_Load.py +6 -0
  114. osbot_utils/helpers/ast/nodes/Ast_Lt.py +7 -0
  115. osbot_utils/helpers/ast/nodes/Ast_LtE.py +7 -0
  116. osbot_utils/helpers/ast/nodes/Ast_Mod.py +6 -0
  117. osbot_utils/helpers/ast/nodes/Ast_Module.py +20 -0
  118. osbot_utils/helpers/ast/nodes/Ast_Mult.py +6 -0
  119. osbot_utils/helpers/ast/nodes/Ast_Name.py +6 -0
  120. osbot_utils/helpers/ast/nodes/Ast_Not.py +7 -0
  121. osbot_utils/helpers/ast/nodes/Ast_Not_Eq.py +7 -0
  122. osbot_utils/helpers/ast/nodes/Ast_Not_In.py +6 -0
  123. osbot_utils/helpers/ast/nodes/Ast_Or.py +7 -0
  124. osbot_utils/helpers/ast/nodes/Ast_Pass.py +7 -0
  125. osbot_utils/helpers/ast/nodes/Ast_Pow.py +7 -0
  126. osbot_utils/helpers/ast/nodes/Ast_Raise.py +8 -0
  127. osbot_utils/helpers/ast/nodes/Ast_Return.py +7 -0
  128. osbot_utils/helpers/ast/nodes/Ast_Set.py +7 -0
  129. osbot_utils/helpers/ast/nodes/Ast_Slice.py +8 -0
  130. osbot_utils/helpers/ast/nodes/Ast_Starred.py +8 -0
  131. osbot_utils/helpers/ast/nodes/Ast_Store.py +7 -0
  132. osbot_utils/helpers/ast/nodes/Ast_Sub.py +7 -0
  133. osbot_utils/helpers/ast/nodes/Ast_Subscript.py +8 -0
  134. osbot_utils/helpers/ast/nodes/Ast_Try.py +9 -0
  135. osbot_utils/helpers/ast/nodes/Ast_Tuple.py +9 -0
  136. osbot_utils/helpers/ast/nodes/Ast_Unary_Op.py +7 -0
  137. osbot_utils/helpers/ast/nodes/Ast_While.py +8 -0
  138. osbot_utils/helpers/ast/nodes/Ast_With.py +7 -0
  139. osbot_utils/helpers/ast/nodes/Ast_With_Item.py +7 -0
  140. osbot_utils/helpers/ast/nodes/Ast_Yield.py +7 -0
  141. osbot_utils/helpers/ast/nodes/__init__.py +0 -0
  142. osbot_utils/helpers/html/Dict_To_Css.py +20 -0
  143. osbot_utils/helpers/html/Dict_To_Html.py +59 -0
  144. osbot_utils/helpers/html/Dict_To_Tags.py +88 -0
  145. osbot_utils/helpers/html/Html_To_Dict.py +75 -0
  146. osbot_utils/helpers/html/Html_To_Tag.py +20 -0
  147. osbot_utils/helpers/html/Tag__Base.py +91 -0
  148. osbot_utils/helpers/html/Tag__Body.py +5 -0
  149. osbot_utils/helpers/html/Tag__Div.py +5 -0
  150. osbot_utils/helpers/html/Tag__H.py +9 -0
  151. osbot_utils/helpers/html/Tag__HR.py +5 -0
  152. osbot_utils/helpers/html/Tag__Head.py +32 -0
  153. osbot_utils/helpers/html/Tag__Html.py +42 -0
  154. osbot_utils/helpers/html/Tag__Link.py +17 -0
  155. osbot_utils/helpers/html/Tag__Style.py +25 -0
  156. osbot_utils/helpers/html/__init__.py +0 -0
  157. osbot_utils/helpers/pubsub/Event__Queue.py +95 -0
  158. osbot_utils/helpers/pubsub/PubSub__Client.py +53 -0
  159. osbot_utils/helpers/pubsub/PubSub__Room.py +13 -0
  160. osbot_utils/helpers/pubsub/PubSub__Server.py +94 -0
  161. osbot_utils/helpers/pubsub/PubSub__Sqlite.py +24 -0
  162. osbot_utils/helpers/pubsub/__init__.py +0 -0
  163. osbot_utils/helpers/pubsub/schemas/Schema__Event.py +15 -0
  164. osbot_utils/helpers/pubsub/schemas/Schema__Event__Connect.py +7 -0
  165. osbot_utils/helpers/pubsub/schemas/Schema__Event__Disconnect.py +7 -0
  166. osbot_utils/helpers/pubsub/schemas/Schema__Event__Join_Room.py +8 -0
  167. osbot_utils/helpers/pubsub/schemas/Schema__Event__Leave_Room.py +8 -0
  168. osbot_utils/helpers/pubsub/schemas/Schema__Event__Message.py +7 -0
  169. osbot_utils/helpers/pubsub/schemas/Schema__PubSub__Client.py +8 -0
  170. osbot_utils/helpers/pubsub/schemas/__init__.py +0 -0
  171. osbot_utils/helpers/sqlite/Capture_Sqlite_Error.py +51 -0
  172. osbot_utils/helpers/sqlite/Sqlite__Cursor.py +87 -0
  173. osbot_utils/helpers/sqlite/Sqlite__Database.py +137 -0
  174. osbot_utils/helpers/sqlite/Sqlite__Field.py +70 -0
  175. osbot_utils/helpers/sqlite/Sqlite__Globals.py +5 -0
  176. osbot_utils/helpers/sqlite/Sqlite__Table.py +293 -0
  177. osbot_utils/helpers/sqlite/Sqlite__Table__Create.py +96 -0
  178. osbot_utils/helpers/sqlite/Temp_Sqlite__Database__Disk.py +17 -0
  179. osbot_utils/helpers/sqlite/Temp_Sqlite__Table.py +23 -0
  180. osbot_utils/helpers/sqlite/__init__.py +0 -0
  181. osbot_utils/helpers/sqlite/domains/Sqlite__Cache__Requests.py +214 -0
  182. osbot_utils/helpers/sqlite/domains/Sqlite__Cache__Requests__Patch.py +63 -0
  183. osbot_utils/helpers/sqlite/domains/Sqlite__DB__Files.py +23 -0
  184. osbot_utils/helpers/sqlite/domains/Sqlite__DB__Graph.py +47 -0
  185. osbot_utils/helpers/sqlite/domains/Sqlite__DB__Json.py +83 -0
  186. osbot_utils/helpers/sqlite/domains/Sqlite__DB__Local.py +20 -0
  187. osbot_utils/helpers/sqlite/domains/Sqlite__DB__Requests.py +39 -0
  188. osbot_utils/helpers/sqlite/domains/__init__.py +0 -0
  189. osbot_utils/helpers/sqlite/domains/schemas/Schema__Table__Requests.py +12 -0
  190. osbot_utils/helpers/sqlite/domains/schemas/__init__.py +0 -0
  191. osbot_utils/helpers/sqlite/models/Sqlite__Field__Type.py +37 -0
  192. osbot_utils/helpers/sqlite/models/__init__.py +0 -0
  193. osbot_utils/helpers/sqlite/sample_data/Sqlite__Sample_Data__Chinook.py +116 -0
  194. osbot_utils/helpers/sqlite/sample_data/__init__.py +0 -0
  195. osbot_utils/helpers/sqlite/sql_builder/SQL_Builder.py +159 -0
  196. osbot_utils/helpers/sqlite/sql_builder/SQL_Builder__Select.py +12 -0
  197. osbot_utils/helpers/sqlite/sql_builder/__init__.py +0 -0
  198. osbot_utils/helpers/sqlite/tables/Sqlite__Table__Config.py +63 -0
  199. osbot_utils/helpers/sqlite/tables/Sqlite__Table__Edges.py +46 -0
  200. osbot_utils/helpers/sqlite/tables/Sqlite__Table__Files.py +45 -0
  201. osbot_utils/helpers/sqlite/tables/Sqlite__Table__Nodes.py +52 -0
  202. osbot_utils/helpers/sqlite/tables/__init__.py +0 -0
  203. osbot_utils/helpers/trace/Trace_Call.py +120 -0
  204. osbot_utils/helpers/trace/Trace_Call__Config.py +94 -0
  205. osbot_utils/helpers/trace/Trace_Call__Graph.py +26 -0
  206. osbot_utils/helpers/trace/Trace_Call__Handler.py +215 -0
  207. osbot_utils/helpers/trace/Trace_Call__Print_Lines.py +85 -0
  208. osbot_utils/helpers/trace/Trace_Call__Print_Traces.py +170 -0
  209. osbot_utils/helpers/trace/Trace_Call__Stack.py +166 -0
  210. osbot_utils/helpers/trace/Trace_Call__Stack_Node.py +59 -0
  211. osbot_utils/helpers/trace/Trace_Call__Stats.py +71 -0
  212. osbot_utils/helpers/trace/Trace_Call__View_Model.py +75 -0
  213. osbot_utils/helpers/trace/Trace_Files.py +33 -0
  214. osbot_utils/helpers/trace/__init__.py +0 -0
  215. osbot_utils/testing/Catch.py +54 -0
  216. osbot_utils/testing/Duration.py +69 -0
  217. osbot_utils/testing/Hook_Method.py +118 -0
  218. osbot_utils/testing/Log_To_Queue.py +46 -0
  219. osbot_utils/testing/Log_To_String.py +37 -0
  220. osbot_utils/testing/Logging.py +81 -0
  221. osbot_utils/testing/Patch_Print.py +52 -0
  222. osbot_utils/testing/Profiler.py +89 -0
  223. osbot_utils/testing/Stderr.py +19 -0
  224. osbot_utils/testing/Stdout.py +19 -0
  225. osbot_utils/testing/Temp_File.py +46 -0
  226. osbot_utils/testing/Temp_Folder.py +114 -0
  227. osbot_utils/testing/Temp_Sys_Path.py +13 -0
  228. osbot_utils/testing/Temp_Web_Server.py +83 -0
  229. osbot_utils/testing/Temp_Zip.py +45 -0
  230. osbot_utils/testing/Temp_Zip_In_Memory.py +90 -0
  231. osbot_utils/testing/Unit_Test.py +34 -0
  232. osbot_utils/testing/Unzip_File.py +30 -0
  233. osbot_utils/testing/__init__.py +0 -0
  234. osbot_utils/utils/Assert.py +52 -0
  235. osbot_utils/utils/Call_Stack.py +187 -0
  236. osbot_utils/utils/Csv.py +32 -0
  237. osbot_utils/utils/Dev.py +47 -0
  238. osbot_utils/utils/Exceptions.py +7 -0
  239. osbot_utils/utils/Files.py +528 -0
  240. osbot_utils/utils/Functions.py +113 -0
  241. osbot_utils/utils/Http.py +136 -0
  242. osbot_utils/utils/Int.py +6 -0
  243. osbot_utils/utils/Json.py +171 -0
  244. osbot_utils/utils/Json_Cache.py +59 -0
  245. osbot_utils/utils/Lists.py +198 -0
  246. osbot_utils/utils/Misc.py +496 -0
  247. osbot_utils/utils/Objects.py +341 -0
  248. osbot_utils/utils/Png.py +29 -0
  249. osbot_utils/utils/Process.py +73 -0
  250. osbot_utils/utils/Python_Logger.py +301 -0
  251. osbot_utils/utils/Status.py +79 -0
  252. osbot_utils/utils/Str.py +63 -0
  253. osbot_utils/utils/Version.py +16 -0
  254. osbot_utils/utils/Zip.py +97 -0
  255. osbot_utils/utils/__init__.py +16 -0
  256. osbot_utils/version +1 -0
  257. osbot_utils-1.7.7.dist-info/LICENSE +201 -0
  258. osbot_utils-1.7.7.dist-info/METADATA +46 -0
  259. osbot_utils-1.7.7.dist-info/RECORD +260 -0
  260. 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
@@ -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)