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,63 @@
1
+ from osbot_utils.base_classes.Kwargs_To_Self import Kwargs_To_Self
2
+ from osbot_utils.decorators.methods.cache_on_self import cache_on_self
3
+ from osbot_utils.helpers.sqlite.Sqlite__Table import Sqlite__Table
4
+ from osbot_utils.utils.Objects import pickle_save_to_bytes, pickle_load_from_bytes
5
+
6
+ SQLITE__TABLE_NAME__CONFIG = 'config'
7
+
8
+ class Schema__Table__Config(Kwargs_To_Self):
9
+ key : str
10
+ value: bytes
11
+
12
+ class Sqlite__Table__Config(Sqlite__Table):
13
+ def __init__(self, **kwargs):
14
+ self.table_name = SQLITE__TABLE_NAME__CONFIG
15
+ self.row_schema = Schema__Table__Config
16
+ super().__init__(**kwargs)
17
+
18
+ def config_data(self):
19
+ config_data = {}
20
+ for row in self.rows():
21
+ key, value = self.deserialize_row(row)
22
+ config_data[key] = value
23
+ return config_data
24
+
25
+ @cache_on_self
26
+ def data(self):
27
+ return self.config_data()
28
+
29
+ def deserialize_row(self, row):
30
+ if row:
31
+ key = row.get('key')
32
+ pickled_value = row.get('value')
33
+ value = pickle_load_from_bytes(pickled_value)
34
+ return key, value
35
+ return None, None
36
+
37
+ def set_config_data(self, config_data: dict):
38
+ self.clear()
39
+ for key,value in config_data.items():
40
+ self.set_value(key=key, value=value)
41
+ self.commit()
42
+
43
+ def set_value(self, key, value):
44
+ if self.not_contains(key=key):
45
+ pickled_value = pickle_save_to_bytes(value)
46
+ return self.add_row_and_commit(key=key, value=pickled_value)
47
+ return self.update_value(key,value)
48
+
49
+ def update_value(self, key, value):
50
+ pickled_value = pickle_save_to_bytes(value)
51
+ self.row_update(dict(key=key, value=pickled_value), dict(key=key))
52
+
53
+ def setup(self):
54
+ if self.exists() is False:
55
+ self.create()
56
+ self.index_create('key')
57
+ return self
58
+
59
+ def value(self, key):
60
+ row = self.where_one(key=key)
61
+ key, value = self.deserialize_row(row)
62
+ return value
63
+
@@ -0,0 +1,46 @@
1
+ from osbot_utils.decorators.lists.index_by import index_by
2
+ from osbot_utils.helpers.sqlite.Sqlite__Table import Sqlite__Table
3
+ from osbot_utils.base_classes.Kwargs_To_Self import Kwargs_To_Self
4
+ from osbot_utils.utils.Misc import timestamp_utc_now
5
+
6
+ SQLITE__TABLE_NAME__EDGES = 'edges'
7
+
8
+ class Schema__Table__Edges(Kwargs_To_Self):
9
+ source_key : str
10
+ target_key : str
11
+ value : bytes
12
+ properties : bytes
13
+ timestamp : int
14
+
15
+ class Sqlite__Table__Edges(Sqlite__Table):
16
+ auto_pickle_blob : bool = True
17
+ set_timestamp : bool = True
18
+
19
+ def __init__(self, **kwargs):
20
+ self.table_name = SQLITE__TABLE_NAME__EDGES
21
+ self.row_schema = Schema__Table__Edges
22
+ super().__init__(**kwargs)
23
+
24
+
25
+ def add_edge(self, source_key, target_key, value=None, properties=None):
26
+ row_data = self.create_node_data(source_key, target_key,value, properties)
27
+ return self.add_row_and_commit(**row_data)
28
+
29
+ def create_node_data(self, source_key, target_key, value=None, properties=None):
30
+ node_data = {'source_key' : source_key ,
31
+ 'target_key' : target_key ,
32
+ 'value' : value ,
33
+ 'properties' : properties }
34
+ if self.set_timestamp:
35
+ node_data['timestamp'] = timestamp_utc_now()
36
+ return node_data
37
+
38
+ def edges(self):
39
+ return self.rows()
40
+
41
+ def setup(self):
42
+ if self.exists() is False:
43
+ self.create()
44
+ self.index_create('source_key')
45
+ self.index_create('target_key')
46
+ return self
@@ -0,0 +1,45 @@
1
+ from osbot_utils.base_classes.Kwargs_To_Self import Kwargs_To_Self
2
+ from osbot_utils.helpers.sqlite.Sqlite__Table import Sqlite__Table
3
+ from osbot_utils.utils.Misc import timestamp_utc_now
4
+
5
+ SQLITE__TABLE_NAME__FILES = 'files'
6
+
7
+ class Schema__Table__Files(Kwargs_To_Self):
8
+ path : str
9
+ contents : bytes
10
+ metadata : bytes
11
+ timestamp: int
12
+
13
+
14
+ class Sqlite__Table__Files(Sqlite__Table):
15
+ auto_pickle_blob : bool = True
16
+ set_timestamp : bool = True
17
+
18
+ def __init__(self, **kwargs):
19
+ self.table_name = SQLITE__TABLE_NAME__FILES
20
+ self.row_schema = Schema__Table__Files
21
+ super().__init__(**kwargs)
22
+
23
+ def add_file(self, path, contents=None, metadata= None):
24
+ if self.contains(path=path): # don't allow multiple entries for the same file path (until we add versioning support)
25
+ return None
26
+ row_data = self.create_node_data(path, contents, metadata)
27
+ return self.add_row_and_commit(**row_data)
28
+
29
+ def create_node_data(self, path, contents=None, metadata= None):
30
+ node_data = {'path' : path ,
31
+ 'contents': contents ,
32
+ 'metadata': metadata }
33
+ if self.set_timestamp:
34
+ node_data['timestamp'] = timestamp_utc_now()
35
+ return node_data
36
+
37
+
38
+ def files(self):
39
+ return self.rows()
40
+
41
+ def setup(self):
42
+ if self.exists() is False:
43
+ self.create()
44
+ self.index_create('path')
45
+ return self
@@ -0,0 +1,52 @@
1
+ from osbot_utils.decorators.lists.index_by import index_by
2
+ from osbot_utils.helpers.sqlite.Sqlite__Table import Sqlite__Table
3
+
4
+ from osbot_utils.base_classes.Kwargs_To_Self import Kwargs_To_Self
5
+ from osbot_utils.utils.Lists import unique
6
+ from osbot_utils.utils.Misc import timestamp_utc_now
7
+
8
+ SQLITE__TABLE_NAME__NODES = 'nodes'
9
+
10
+ class Schema__Table__Nodes(Kwargs_To_Self):
11
+ key : str
12
+ value : bytes
13
+ properties: bytes
14
+ timestamp : int
15
+
16
+ class Sqlite__Table__Nodes(Sqlite__Table):
17
+ allow_duplicate_keys: bool = False
18
+ auto_pickle_blob : bool = True
19
+ set_timestamp : bool = True
20
+
21
+ def __init__(self, **kwargs):
22
+ self.table_name = SQLITE__TABLE_NAME__NODES
23
+ self.row_schema = Schema__Table__Nodes
24
+ super().__init__(**kwargs)
25
+
26
+ def add_node(self, key, value=None, properties=None):
27
+ if self.allow_duplicate_keys is False:
28
+ if self.contains(key=key):
29
+ return None
30
+ row_data = self.create_node_data(key,value, properties)
31
+ return self.add_row_and_commit(**row_data)
32
+
33
+ def create_node_data(self, key, value=None, properties=None):
34
+ node_data = {'key' : key ,
35
+ 'value' : value ,
36
+ 'properties' : properties }
37
+ if self.set_timestamp:
38
+ node_data['timestamp'] = timestamp_utc_now()
39
+ return node_data
40
+
41
+ @index_by
42
+ def nodes(self):
43
+ return self.rows()
44
+
45
+ def keys(self):
46
+ return unique(self.select_field_values('key'))
47
+
48
+ def setup(self):
49
+ if self.exists() is False:
50
+ self.create()
51
+ self.index_create('key')
52
+ return self
File without changes
@@ -0,0 +1,120 @@
1
+ import linecache
2
+ import sys
3
+ from functools import wraps
4
+
5
+ from osbot_utils.base_classes.Kwargs_To_Self import Kwargs_To_Self
6
+ from osbot_utils.helpers.trace.Trace_Call__Config import Trace_Call__Config, PRINT_MAX_STRING_LENGTH
7
+ from osbot_utils.helpers.trace.Trace_Call__Handler import Trace_Call__Handler
8
+ from osbot_utils.helpers.trace.Trace_Call__Print_Lines import Trace_Call__Print_Lines
9
+ from osbot_utils.helpers.trace.Trace_Call__Print_Traces import Trace_Call__Print_Traces
10
+ from osbot_utils.helpers.trace.Trace_Call__View_Model import Trace_Call__View_Model
11
+
12
+
13
+ def trace_calls(title = None , print_traces = True , show_locals = False, source_code = False ,
14
+ ignore = None , include = None , show_path = False, duration_bigger_than = 0 ,
15
+ max_string = None , show_types = False, show_duration = False ,# show_caller = False , # todo: add back when show_caller is working again
16
+ show_class = False, contains = None , show_internals = False, enabled = True ,
17
+ extra_data = False, show_lines = False, print_lines = False, show_types_padding = None , duration_padding=None):
18
+ def decorator(func):
19
+ @wraps(func)
20
+ def wrapper(*args, **kwargs):
21
+ config_kwargs = dict(title=title, print_traces_on_exit=print_traces, print_locals=show_locals,
22
+ capture_locals = show_locals,
23
+ trace_capture_source_code=source_code, ignore_start_with=ignore,
24
+ trace_capture_start_with=include, print_max_string_length=max_string,
25
+ show_parent_info=show_types, show_method_class=show_class,
26
+ show_source_code_path=show_path,
27
+ capture_duration=show_duration, print_duration= show_duration,
28
+ with_duration_bigger_than=duration_bigger_than,
29
+ trace_capture_contains=contains, trace_show_internals=show_internals,
30
+ capture_extra_data=extra_data,
31
+ print_padding_parent_info= show_types_padding, print_padding_duration=duration_padding,
32
+ print_lines_on_exit=print_lines, trace_enabled=enabled,
33
+ trace_capture_lines=show_lines or print_lines)
34
+
35
+ config = (Trace_Call__Config().update_from_kwargs (**config_kwargs))
36
+
37
+ with Trace_Call(config=config):
38
+ result = func(*args, **kwargs)
39
+ return result
40
+ return wrapper
41
+ return decorator
42
+
43
+ class Trace_Call(Kwargs_To_Self):
44
+
45
+ config : Trace_Call__Config
46
+ started : bool
47
+ prev_trace_function: None
48
+
49
+ def __init__(self, **kwargs):
50
+ super().__init__(**kwargs)
51
+
52
+ self.trace_call_handler = Trace_Call__Handler (config=self.config)
53
+ self.trace_call_print_traces = Trace_Call__Print_Traces(config=self.config)
54
+ self.trace_call_view_model = Trace_Call__View_Model ()
55
+ self.config.print_traces_on_exit = self.config.print_traces_on_exit
56
+ #self.config.trace_capture_start_with = self.config.capture_start_with or [] # todo add a better way to set these to [] when then value is null
57
+ self.config.trace_ignore_start_with = self.config.ignore_start_with or [] # probablty better done inside Kwargs_To_Self since it doesn't make sense for lists or dicts to have None value
58
+ self.config.trace_capture_contains = self.config.trace_capture_contains or [] # and None will be quite common since we can use [] on method's params
59
+ self.config.print_max_string_length = self.config.print_max_string_length or PRINT_MAX_STRING_LENGTH
60
+ self.stack = self.trace_call_handler.stack
61
+ #self.prev_trace_function = None # Stores the previous trace function
62
+
63
+
64
+ def __enter__(self):
65
+ return self.on_enter()
66
+
67
+ def __exit__(self, exc_type, exc_val, exc_tb):
68
+ return self.on_exit()
69
+
70
+ def on_enter(self):
71
+ if self.config.trace_enabled:
72
+ self.start() # Start the tracing
73
+ return self
74
+
75
+ def on_exit(self):
76
+ if self.config.trace_enabled:
77
+ self.stop() # Stop the tracing
78
+ if self.config.print_traces_on_exit:
79
+ self.print()
80
+ if self.config.print_lines_on_exit:
81
+ self.print_lines()
82
+
83
+ def capture_all(self):
84
+ self.config.trace_capture_all = True
85
+ return self
86
+
87
+ def view_data(self):
88
+ return self.trace_call_view_model.create(self.stack)
89
+
90
+ def print(self):
91
+ view_model = self.view_data()
92
+ self.trace_call_print_traces.print_traces(view_model)
93
+ #self.print_lines()
94
+ return view_model
95
+
96
+ def print_lines(self):
97
+ print()
98
+ view_model = self.view_data()
99
+ print_lines = Trace_Call__Print_Lines(config=self.config, view_model=view_model)
100
+ print_lines.print_lines()
101
+
102
+ def start(self):
103
+ self.trace_call_handler.stack.add_node(title=self.trace_call_handler.config.title)
104
+ self.prev_trace_function = sys.gettrace()
105
+ self.started = True # set this here so that it does show in the trace
106
+ sys.settrace(self.trace_call_handler.trace_calls) # Set the new trace function
107
+
108
+
109
+ def stop(self):
110
+ if self.started:
111
+ sys.settrace(self.prev_trace_function) # Restore the previous trace function
112
+ self.stack.empty_stack()
113
+ self.started = False
114
+
115
+ def stats(self):
116
+ return self.trace_call_handler.stats
117
+
118
+ def stats_data(self):
119
+ return self.trace_call_handler.stats.raw_call_stats
120
+
@@ -0,0 +1,94 @@
1
+ from osbot_utils.utils.Dev import pprint
2
+
3
+ from osbot_utils.base_classes.Kwargs_To_Self import Kwargs_To_Self
4
+
5
+ PRINT_MAX_STRING_LENGTH = 100
6
+ PRINT_PADDING__DURATION = 100
7
+ PRINT_PADDING_PARENT_INFO = 60
8
+
9
+ class Trace_Call__Config(Kwargs_To_Self):
10
+ title : str
11
+ capture_locals : bool = False
12
+ capture_duration : bool
13
+ capture_extra_data : bool
14
+ capture_frame : bool = True
15
+ capture_frame_stats : bool
16
+ deep_copy_locals : bool
17
+ trace_capture_lines : bool
18
+ ignore_start_with : list
19
+ print_padding_duration : int = PRINT_PADDING__DURATION
20
+ print_padding_parent_info : int = PRINT_PADDING_PARENT_INFO
21
+ print_duration : bool
22
+ print_max_string_length : int = PRINT_MAX_STRING_LENGTH
23
+ print_locals : bool
24
+ print_traces_on_exit : bool
25
+ print_lines_on_exit : bool
26
+ show_parent_info : bool = False
27
+ show_caller : bool
28
+ show_method_class : bool = True
29
+ show_source_code_path : bool
30
+ trace_capture_all : bool
31
+ trace_capture_source_code : bool
32
+ trace_capture_start_with : list
33
+ trace_capture_contains : list
34
+ trace_enabled : bool = True
35
+ trace_ignore_start_with : list
36
+ trace_show_internals : bool
37
+ trace_up_to_depth : int
38
+ with_duration_bigger_than : float
39
+
40
+ def __init__(self, **wargs):
41
+ super().__init__(**wargs)
42
+ #self.locked()
43
+
44
+ def all(self, up_to_depth=0, print_traces=True):
45
+ self.trace_capture_all = True
46
+ self.print_traces_on_exit = print_traces
47
+ self.trace_up_to_depth = up_to_depth
48
+ return self
49
+
50
+ def capture(self, starts_with=None, contains=None, ignore=None):
51
+ if starts_with:
52
+ if type(starts_with) is str:
53
+ starts_with = [starts_with]
54
+ self.trace_capture_start_with = starts_with
55
+ if contains:
56
+ if type(contains) is str:
57
+ contains = [contains]
58
+ self.trace_capture_contains = contains
59
+ if ignore:
60
+ if type(ignore) is str:
61
+ ignore = [ignore]
62
+ self.ignore_start_with = ignore
63
+ self.print_traces_on_exit = True
64
+ return self
65
+
66
+ def duration(self, bigger_than=0, padding=PRINT_PADDING__DURATION):
67
+ self.capture_duration = True
68
+ self.print_duration = True
69
+ self.print_padding_duration = padding
70
+ self.with_duration_bigger_than = bigger_than
71
+ return self
72
+
73
+ def locals(self):
74
+ self.capture_locals = True
75
+ self.print_locals = True
76
+ return self
77
+
78
+ def lines(self, print_traces=True, print_lines=True):
79
+ self.trace_capture_lines = True
80
+ self.print_traces_on_exit = print_traces
81
+ self.print_lines_on_exit = print_lines
82
+ return self
83
+
84
+ def print_config(self):
85
+ pprint(self.__locals__())
86
+ return self
87
+
88
+ def print_on_exit(self, value=True):
89
+ self.print_traces_on_exit = value
90
+ return self
91
+
92
+ def up_to_depth(self, depth):
93
+ self.trace_up_to_depth = depth
94
+ return self
@@ -0,0 +1,26 @@
1
+ from osbot_utils.graphs.mermaid.Mermaid__Graph import Mermaid__Graph
2
+ from osbot_utils.utils.Dev import pprint
3
+
4
+ from osbot_utils.base_classes.Kwargs_To_Self import Kwargs_To_Self
5
+ from osbot_utils.helpers.trace.Trace_Call import Trace_Call
6
+
7
+ # todo: reimplement this class when Mermaid__Graph has been updated to new version
8
+ class Trace_Call__Graph(Trace_Call):
9
+
10
+ def create(self):
11
+ mermaid_graph = Mermaid__Graph()
12
+ self.trace_call_handler.stack.root_node.func_name = 'trace_root'
13
+ for trace in self.trace_call_handler.traces():
14
+ node_key = trace.func_name
15
+ class_name = trace.module.split('.')[-1]
16
+ node_label = f'`**{trace.func_name}**\n*{class_name}*`'
17
+ mermaid_graph.add_node(key=node_key, label=node_label)
18
+
19
+ nodes__by_key = mermaid_graph.data().nodes__by_key()
20
+
21
+ for trace in self.trace_call_handler.traces():
22
+ from_node = nodes__by_key[trace.func_name]
23
+ for child in trace.children:
24
+ to_node = nodes__by_key[child.func_name]
25
+ mermaid_graph.add_edge(from_node=from_node, to_node=to_node)
26
+ return mermaid_graph
@@ -0,0 +1,215 @@
1
+ import inspect
2
+ import linecache
3
+ from osbot_utils.utils.Objects import class_full_name
4
+ from osbot_utils.base_classes.Kwargs_To_Self import Kwargs_To_Self
5
+ from osbot_utils.helpers.trace.Trace_Call__Config import Trace_Call__Config
6
+ from osbot_utils.helpers.trace.Trace_Call__Stack import Trace_Call__Stack
7
+ from osbot_utils.helpers.trace.Trace_Call__Stack_Node import Trace_Call__Stack_Node, EXTRA_DATA__RETURN_VALUE
8
+ from osbot_utils.helpers.trace.Trace_Call__Stats import Trace_Call__Stats
9
+
10
+ DEFAULT_ROOT_NODE_NODE_TITLE = 'Trace Session'
11
+ GLOBAL_FUNCTIONS_TO_IGNORE = ['value_type_matches_obj_annotation_for_attr' , # these are type safety functions which introduce quite a lot of noise in the traces (and unless one is debugging type safety, they will not be needed)
12
+ 'value_type_matches_obj_annotation_for_union_attr' , # todo: map out and document why exactly these methods are ignore (and what is the side effect)
13
+ 'are_types_compatible_for_assigment' ,
14
+ 'obj_attribute_annotation' ,
15
+ 'get_origin' ,
16
+ 'getmro' ,
17
+ 'default_value' ,
18
+ 'raise_exception_on_obj_type_annotation_mismatch' ,
19
+ '__cls_kwargs__' ,
20
+ '__default__value__' ,
21
+ '__setattr__' ,
22
+ '<module>']
23
+ GLOBAL_MODULES_TO_IGNORE = ['osbot_utils.helpers.trace.Trace_Call' , # todo: map out and document why exactly these modules are ignore (and what is the side effect)
24
+ 'osbot_utils.helpers.CPrint' , # also see if this should be done here or at the print/view stage
25
+ 'osbot_utils.helpers.Print_Table' ,
26
+ 'osbot_utils.decorators.methods.cache_on_self' ,
27
+ 'codecs']
28
+
29
+ class Trace_Call__Handler(Kwargs_To_Self):
30
+ config : Trace_Call__Config
31
+ stack : Trace_Call__Stack
32
+ stats : Trace_Call__Stats
33
+
34
+
35
+ def __init__(self, **kwargs):
36
+ super().__init__(**kwargs)
37
+ self.config.title = self.config.title or DEFAULT_ROOT_NODE_NODE_TITLE # Title for the trace root node
38
+ self.stack.config = self.config
39
+
40
+ def add_default_root_node(self):
41
+ return self.stack.add_node(title=self.config.title)
42
+
43
+ def add_line(self, frame):
44
+ if self.config.trace_capture_lines:
45
+ if frame:
46
+ target_node = self.stack.top() # lines captured are added to the current top of the stack
47
+ #obj_info(target_node)
48
+ if self.stack.top():
49
+ target_node__func_name = target_node.func_name
50
+ target_node__module = target_node.module
51
+ frame_func_name = frame.f_code.co_name
52
+ frame_module = frame.f_globals.get("__name__", "")
53
+ if frame_func_name == target_node__func_name:
54
+ if frame_module == target_node__module:
55
+ return self.add_line_to_node(frame, target_node, 'line')
56
+ return False
57
+
58
+ def add_line_to_node(self, frame, target_node, event):
59
+ def source_code_for_frame(function_name):
60
+ try:
61
+ return inspect.getsource(frame.f_code)
62
+ except Exception as error:
63
+ return ''
64
+ #print(self.stack.line_index, f'def {function_name}() : error] {error}' )
65
+ #return f'def {function_name}() : [error] {error}'
66
+
67
+ if frame and target_node:
68
+ func_name = frame.f_code.co_name
69
+ module = frame.f_globals.get("__name__", "")
70
+ self_local = class_full_name(frame.f_locals.get('self'))
71
+ stack_size = len(self.stack)
72
+ line =''
73
+ line_number=0
74
+ if event == 'call': # if this is a call we need to do the code below to get the actual method signature (and decorators)
75
+ function_name = frame.f_code.co_name
76
+ filename = frame.f_code.co_filename # Get the filename where the function is defined
77
+ start_line_number = frame.f_code.co_firstlineno # Get the starting line number
78
+ source_lines = source_code_for_frame(function_name).split('\n')
79
+ if source_lines:
80
+ def_line_number = start_line_number # Try to find the actual 'def' line
81
+ for line in source_lines:
82
+ if line.strip().startswith('def ' + function_name):
83
+ break
84
+ def_line_number += 1
85
+ else:
86
+ def_line_number = start_line_number # If the 'def' line wasn't found, default to the starting line
87
+ line_number = def_line_number
88
+ line = linecache.getline(filename, line_number).rstrip() # todo: refactor this to not capture this info here, and to use the Ast_* utils to get a better source code mapping
89
+ else:
90
+ filename = frame.f_code.co_filename # get the filename
91
+ line_number = frame.f_lineno # get the current line number
92
+ line = linecache.getline(filename, line_number) # get the line
93
+
94
+ if line:
95
+ self.stack.line_index += 1
96
+ line_data = dict(event=event, index = self.stack.line_index, func_name=func_name,
97
+ line = line.rstrip(), line_number=line_number,
98
+ module=module, self_local=self_local,
99
+ stack_size=stack_size)
100
+ target_node.lines.append(line_data)
101
+ return True
102
+ # else:
103
+ # print(f'no line for : {self.stack.line_index}, {module}.{func_name}')
104
+ return False
105
+
106
+ def add_frame(self, frame):
107
+ return self.handle_event__call(frame)
108
+
109
+ def add_trace_ignore(self, value):
110
+ self.config.trace_ignore_start_with.append(value)
111
+ return
112
+
113
+ def handle_event__call(self, frame):
114
+ if frame:
115
+ if self.config.capture_frame_stats:
116
+ self.stats.log_frame(frame)
117
+ if self.should_capture(frame):
118
+ new_node = self.stack.add_frame(frame)
119
+ if self.config.trace_capture_lines:
120
+ self.add_line_to_node(frame, new_node,'call')
121
+ return new_node
122
+ else:
123
+ self.stats.calls_skipped += 1
124
+
125
+ def handle_event__line(self, frame):
126
+ return self.add_line(frame)
127
+
128
+
129
+ def handle_event__return(self, frame, return_value=None):
130
+ if return_value and self.config.capture_extra_data:
131
+ extra_data = { EXTRA_DATA__RETURN_VALUE : return_value}
132
+ else:
133
+ extra_data = {}
134
+ return self.stack.pop(target=frame, extra_data = extra_data)
135
+
136
+ def should_capture(self, frame): # todo: see if we can optimise these 3 lines (starting with frame.f_code) which are repeated in a number of places here
137
+ if self.config.trace_up_to_depth:
138
+ if len(self.stack) > self.config.trace_up_to_depth:
139
+ return False
140
+
141
+ capture = False
142
+ if frame:
143
+ code = frame.f_code # Get code object from frame
144
+ func_name = code.co_name # Get function name
145
+ module = frame.f_globals.get("__name__", "") # Get module name
146
+
147
+ if module in GLOBAL_MODULES_TO_IGNORE: # check if we should skip this module
148
+ return False
149
+
150
+ if func_name in GLOBAL_FUNCTIONS_TO_IGNORE: # check if we should skip this function
151
+ return False
152
+
153
+ if module and func_name:
154
+ if self.config.trace_capture_all:
155
+ capture = True
156
+ else:
157
+ for item in self.config.trace_capture_start_with: # capture if the module starts with
158
+ if item: # prevent empty queries (which will always be true)
159
+ if module.startswith(item) or item =='*':
160
+ capture = True
161
+ break
162
+ for item in self.config.trace_capture_contains: # capture if module of func_name contains
163
+ if item: # prevent empty queries (which will always be true)
164
+ if item in module or item in func_name:
165
+ capture = True
166
+ break
167
+ if self.config.trace_show_internals is False and func_name.startswith('_'): # Skip private functions
168
+ capture = False
169
+
170
+ for item in self.config.trace_ignore_start_with: # Check if the module should be ignored
171
+ if module.startswith(item):
172
+ capture = False
173
+ break
174
+ return capture
175
+
176
+ def stack_json__parse_node(self, stack_node: Trace_Call__Stack_Node):
177
+ node = stack_node.data()
178
+ new_children = []
179
+ for child in node.get('children'):
180
+ new_children.append(self.stack_json__parse_node(child))
181
+ node['children'] = new_children
182
+ return node
183
+
184
+ def stack_top(self):
185
+ if self.stack:
186
+ return self.stack[-1]
187
+
188
+ def trace_calls(self, frame, event, arg):
189
+ if event == 'call':
190
+ self.stats.calls +=1
191
+ self.handle_event__call(frame) # todo: handle bug with locals which need to be serialised, since it's value will change
192
+ elif event == 'return':
193
+ self.stats.returns += 1
194
+ self.handle_event__return(frame, arg)
195
+ elif event == 'exception':
196
+ self.stats.exceptions +=1 # for now don't handle exception events
197
+ elif event == 'line':
198
+ self.handle_event__line(frame)
199
+ self.stats.lines +=1
200
+ else:
201
+ self.stats.unknowns += 1
202
+
203
+ return self.trace_calls
204
+
205
+
206
+
207
+ def traces(self):
208
+ def map_traces(node, all_traces):
209
+ if node:
210
+ all_traces.append(node)
211
+ for child in node.children:
212
+ map_traces(child, all_traces)
213
+ result = []
214
+ map_traces(self.stack.root_node, result)
215
+ return result