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,85 @@
1
+ from osbot_utils.utils.Lists import list_sorted
2
+ from osbot_utils.base_classes.Kwargs_To_Self import Kwargs_To_Self
3
+ from osbot_utils.utils.Misc import ansi_text_visible_length
4
+ from osbot_utils.helpers.trace.Trace_Call__Config import Trace_Call__Config
5
+ from osbot_utils.helpers.trace.Trace_Call__Print_Traces import text_grey, text_bold_green, text_olive, text_light_grey
6
+
7
+
8
+ class Trace_Call__Print_Lines(Kwargs_To_Self):
9
+
10
+ config : Trace_Call__Config
11
+ view_model : list
12
+
13
+ def lines(self):
14
+ lines = []
15
+ for trace in self.view_model:
16
+ items = trace.get('lines')
17
+ leading_spaces = 0
18
+ if items:
19
+ line = items[0].get('line')
20
+ leading_spaces = len(line) - len(line.lstrip())
21
+
22
+ for line_data in items:
23
+ depth_padding = ' ' + ' ' * (line_data.get('stack_size') - 2) * 6 # this helps to align the code with the current depth (i.e. column alignment of code)
24
+ line_data['line'] = depth_padding + line_data.get('line')[leading_spaces:]
25
+ lines.append(line_data)
26
+ return list_sorted(lines, 'index')
27
+
28
+ def max_fields_length(self, items, *fields):
29
+ max_length = 0
30
+ for item in items:
31
+ method_sig = '.'.join(str(item.get(field, '')) for field in fields)
32
+ if len(method_sig) > max_length:
33
+ max_length = len(method_sig)
34
+ return max_length
35
+
36
+ def max_fields_value(self, items, *fields):
37
+ max_value = 0
38
+ for item in items:
39
+ values = [int(item.get(field, 0)) for field in fields if field in item and isinstance(item.get(field), int)] # This will create a list of integers for the given fields in the line
40
+ line_max = max(values) if values else 0 # Now find the max value from these integers
41
+ if line_max > max_value:
42
+ max_value = line_max
43
+ return max_value
44
+
45
+ def print_lines(self, ):
46
+ lines = self.lines()
47
+ print("--------- CALL TRACER (Lines)----------")
48
+ print(f"Here are the {len(lines)} lines captured\n")
49
+
50
+ max_length__sig = self.max_fields_length(lines, 'module', 'func_name') + 2
51
+ max_length__line = self.max_fields_length(lines, 'line' ) + self.max_fields_value (lines, 'stack_size' ) + 5 # this + 5 helps with the alignment of the larger line (so that it doesn't overflow the table)
52
+ max_length__self = self.max_fields_length(lines, 'self_local' )
53
+ print( '┌─────â”Ŧ──────â”Ŧ─' + '─' * max_length__line +'──â”Ŧ─' + '─' * max_length__sig + '─â”Ŧ─' + '─' * max_length__self + '─â”Ŧ───────┐ ')
54
+ print(f"│ # │ Line │ {'Source code':<{max_length__line}} │ {'Method Class and Name':<{max_length__sig}} │ {'Self object':<{max_length__self}} │ Depth │ ")
55
+ print( '├─────â”ŧ──────â”ŧ─' + '─' * max_length__line +'──â”ŧ─' + '─'* max_length__sig + '─â”ŧ─' + '─' * max_length__self + '─â”ŧ───────┤ ')
56
+ for line_data in lines:
57
+ index = line_data.get('index')
58
+ func_name = line_data.get('func_name')
59
+ line_number = line_data.get('line_number')
60
+ module = line_data.get('module')
61
+ event = line_data.get('event')
62
+ line = line_data.get('line')
63
+ self_local = line_data.get('self_local') or ''
64
+ method_sig = f"{module}.{func_name}"
65
+ stack_size = line_data.get('stack_size') -1
66
+
67
+ text_depth = f'{stack_size:5}'
68
+ text_depth_padding = ' ' * ((stack_size-1) * 2)
69
+ text_index = f'{text_grey(index):12}'
70
+ text_line_no = f'{line_number:4}'
71
+ text_method_sig = f'{method_sig:{max_length__sig}}'
72
+
73
+ if event == 'call':
74
+ text_line = f'{text_bold_green(line)}'
75
+ else:
76
+ text_line = f'{text_light_grey(line)}'
77
+
78
+ text_line_padding = ' ' * (max_length__line - ansi_text_visible_length(text_line) - len(text_depth_padding))
79
+ text_source_code = f'{text_depth_padding}{text_line} {text_line_padding}'
80
+
81
+ print(f"│ {text_index} │ {text_line_no} │ {text_source_code} │ {text_method_sig} │ {self_local:<{max_length__self}} │ {text_depth} │")
82
+
83
+ print('└─────┴──────┴──' + '─' * max_length__line + '─┴─' + '─' * max_length__sig + '─┴──' + '─' * max_length__self + '┴───────┘')
84
+
85
+
@@ -0,0 +1,170 @@
1
+ from osbot_utils.base_classes.Kwargs_To_Self import Kwargs_To_Self
2
+ from osbot_utils.utils.Dev import pformat
3
+ from osbot_utils.helpers.trace.Trace_Call__Config import Trace_Call__Config
4
+
5
+ # ANSI escape codes #todo: refactor this color support to separate colors class
6
+ dark_mode = False
7
+
8
+ if dark_mode:
9
+ BOLD = "\033[1m\033[48;2;30;31;34m\033[38;2;255;255;255m" # dark mode
10
+ BLUE = "\033[48;2;30;31;34m\033[94m"
11
+ GREEN = "\033[48;2;30;31;34m\033[92m"
12
+ LIGHT_GREY = "\033[48;2;30;31;34m\033[38;2;130;130;130m"
13
+ OLIVE = "\033[48;2;30;31;34m\033[38;2;118;138;118m"
14
+ GREY = "\033[48;2;30;31;34m\033[90m"
15
+
16
+ else:
17
+ BOLD = "\033[1m"
18
+ BLUE = "\033[94m"
19
+ GREEN = "\033[92m"
20
+ LIGHT_GREY = "\033[38;2;120;120;120m"
21
+ OLIVE = "\033[38;2;138;148;138m" #"\033[38;2;118;138;118m"
22
+ GREY = "\033[90m"
23
+
24
+ RED = "\033[91m"
25
+ WHITE = "\033[97m"
26
+ RESET = "\033[0m"
27
+
28
+ text_blue = lambda text: f"{BLUE}{text}{RESET}"
29
+ text_bold = lambda text: f"{BOLD}{text}{RESET}"
30
+ text_bold_red = lambda text: f"{BOLD}{RED}{text}{RESET}"
31
+ text_bold_green = lambda text: f"{BOLD}{GREEN}{text}{RESET}"
32
+ text_bold_blue = lambda text: f"{BOLD}{BLUE}{text}{RESET}"
33
+ text_green = lambda text: f"{GREEN}{text}{RESET}"
34
+ text_grey = lambda text: f"{GREY}{text}{RESET}"
35
+ text_light_grey = lambda text: f"{BOLD}{LIGHT_GREY}{text}{RESET}"
36
+ text_olive = lambda text: f"{OLIVE}{text}{RESET}"
37
+ text_red = lambda text: f"{RED}{text}{RESET}"
38
+ text_white = lambda text: f"{WHITE}{text}{RESET}"
39
+ text_none = lambda text: f"{text}"
40
+ text_color = lambda text, color: f"{color}{text}{RESET}"
41
+
42
+
43
+
44
+ class Trace_Call__Print_Traces(Kwargs_To_Self):
45
+
46
+ config: Trace_Call__Config
47
+
48
+ def __init__(self, **kwargs):
49
+ super().__init__(**kwargs)
50
+
51
+ def formatted_local_data(self, local_data, formatted_line, emoji = '🔖'):
52
+ if local_data:
53
+ formatted_data = {}
54
+ max_key_length = 0 # Variable to store the length of the longest key
55
+
56
+ # First pass to format data and find the length of the longest key
57
+ for key, value in local_data.items():
58
+ if key.startswith('_'): # don't show internal methods
59
+ continue
60
+ # Convert objects to their type name
61
+ if isinstance(value, dict):
62
+ value = pformat(value) # convert dicts to string (so that they are impacted by self.self.print_max_string_length)
63
+ if not isinstance(value, (int, float, bool, str, dict)):
64
+ formatted_data[key] = (type(value).__name__, BLUE)
65
+ elif isinstance(value, str) and len(value) > self.config.print_max_string_length:
66
+ formatted_data[key] = (value[:self.config.print_max_string_length] + "...", GREEN) # Trim large strings
67
+ else:
68
+ formatted_data[key] = (value, GREEN)
69
+
70
+ # Update the maximum key length
71
+ if len(key) > max_key_length:
72
+ max_key_length = len(key)
73
+
74
+ def format_multiline(value, left_padding):
75
+ lines = str(value).split('\n')
76
+ indented_lines = [lines[0]] + [" " * (left_padding +1) + line for line in lines[1:]]
77
+ return '\n│'.join(indented_lines)
78
+
79
+ padding = " " * len(formatted_line)
80
+ for key, (value, color) in formatted_data.items():
81
+ # Calculate the number of spaces needed for alignment
82
+ spaces = " " * (max_key_length - len(key))
83
+ var_name = f"{padding} {emoji} {text_light_grey(key)}{spaces} = "
84
+ value = format_multiline(value, len(var_name)- len(text_light_grey(''))) # this logic makes sure that the local's values are column aligned
85
+ print(f'│{var_name}{color}{value}{RESET}')
86
+
87
+ def print_lines(self, lines, formatted_line):
88
+ if lines:
89
+ padding = " " * len(formatted_line)
90
+ for line in lines:
91
+ index = line.get('index')
92
+ #func_name = line.get('func_name')
93
+ #module = line.get('module')
94
+ event = line.get('event')
95
+ line = line.get('line')
96
+ if event == 'call':
97
+ print(f"{padding} {text_grey(index):12} {text_bold_green(line)}")
98
+ else:
99
+ print(f"{padding} {text_grey(index):12} {text_olive(line)}")
100
+
101
+ def print_traces(self, view_model):
102
+ print()
103
+ print("--------- CALL TRACER ----------")
104
+ print(f"Here are the {len(view_model)} traces captured\n")
105
+ for idx, item in enumerate(view_model):
106
+ emoji = item.get('emoji' , '' )
107
+ extra_data = item.get('extra_data' , {} )
108
+ locals = item.get('locals' , {} )
109
+ method_name = item.get('method_name' , '' )
110
+ method_parent = item.get('method_parent' , '' )
111
+ parent_info = item.get('parent_info' , '' )
112
+ prefix = item.get('prefix' , '' )
113
+ tree_branch = item.get('tree_branch' , '' )
114
+ source_code = item.get('source_code' , '' )
115
+ source_code_caller = item.get('source_code_caller', '' )
116
+ #source_code_location = item.get('source_code_location') or ''
117
+
118
+ if self.config.show_method_class:
119
+ if self.config.show_parent_info:
120
+ method_name = f'{text_olive(parent_info)}.{text_bold(method_name)}'
121
+ else:
122
+ method_name = f'{text_olive(method_parent)}.{text_bold(method_name)}'
123
+
124
+
125
+ node_text = source_code or method_name
126
+ formatted_line = f"{prefix}{tree_branch}{emoji} {node_text}"
127
+ if self.config.print_duration:
128
+ duration = item.get('duration',0) * 1000 # todo: see if this can be optimised with the similar call below
129
+ duration_rounded = round(duration, 3)
130
+ padding_duration = self.config.print_padding_duration - len(formatted_line)
131
+ duration_text = "{:>{},.3f}ms".format(duration_rounded, padding_duration)
132
+ formatted_line += f' {text_grey(duration_text)} '
133
+
134
+ if self.config.with_duration_bigger_than:
135
+ duration = item.get('duration', 0)
136
+ if duration < self.config.with_duration_bigger_than:
137
+ continue
138
+
139
+ if False and self.config.trace_capture_source_code: # todo: fix show caller funcionality
140
+
141
+ if self.config.show_caller:
142
+ print(f"{prefix}{tree_branch}đŸ”ŧī¸{text_bold(source_code_caller)}")
143
+ print(f"{prefix}{tree_branch}âžĄī¸{emoji} {text_grey(node_text)}")
144
+ else:
145
+ print(f"{prefix}{tree_branch}âžĄī¸{emoji} {text_bold(node_text)}")
146
+
147
+ # if self.config.show_source_code_path:
148
+ #
149
+ # raise Exception("to implement path_source_code_root")
150
+ # path_source_code_root = ...
151
+ #
152
+ # print(f" " * len(prefix), end=" ")
153
+ # fixed_source_code_location = source_code_location.replace(path_source_code_root, '')
154
+ # print(fixed_source_code_location)
155
+ else:
156
+ if idx == 0 or (self.config.show_parent_info is False or self.config.show_method_class is True): # Handle the first line and conditional parent info differently
157
+ print(f"{text_bold(formatted_line)}") # Don't add "|" to the first line
158
+ else:
159
+ padding = " " * (self.config.print_padding_parent_info - len(formatted_line))
160
+
161
+ print(f"{text_bold(formatted_line)} {padding} {parent_info}")
162
+
163
+ if self.config.trace_capture_lines:
164
+ self.print_lines(item.get('lines'), f'{prefix}{tree_branch}')
165
+
166
+ if self.config.print_locals:
167
+ self.formatted_local_data(locals, f'{prefix}{tree_branch}')
168
+
169
+ if self.config.capture_extra_data:
170
+ self.formatted_local_data(extra_data, f'{prefix}{tree_branch}', emoji='✨')
@@ -0,0 +1,166 @@
1
+ import linecache
2
+ import time
3
+ from copy import deepcopy
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_Node import Trace_Call__Stack_Node
7
+
8
+
9
+ class Trace_Call__Stack(Kwargs_To_Self):
10
+ call_index : int
11
+ stack_data : list
12
+ config : Trace_Call__Config
13
+ root_node : Trace_Call__Stack_Node
14
+ line_index : int
15
+
16
+ def __eq__(self, target):
17
+ if self is target:
18
+ return True
19
+ return self.stack_data == target
20
+
21
+ def __init__(self, **kwargs):
22
+ super().__init__(**kwargs)
23
+
24
+ def __iter__(self):
25
+ return iter(self.stack_data)
26
+
27
+ def __getitem__(self, index):
28
+ if -len(self.stack_data) <= index < len(self.stack_data):
29
+ return self.stack_data[index]
30
+
31
+ def __len__(self):
32
+ return self.size()
33
+
34
+ def add_frame(self, frame):
35
+ if frame and frame.__class__.__name__=='frame':
36
+ self.call_index += 1 # Increment the call index
37
+ code = frame.f_code # Get code object from frame
38
+ func_name = code.co_name # Get function name
39
+ module = frame.f_globals.get("__name__", "") # Get module name
40
+
41
+ source_code = self.map_source_code(frame)
42
+ full_name = self.map_full_name(frame, module, func_name)
43
+ new_node = self.create_stack_node(frame, full_name, source_code, self.call_index)
44
+ if self.add_stack_node(new_node, frame):
45
+ return new_node
46
+
47
+
48
+ def add_node(self, title: str):
49
+ new_node = self.new_stack_node(title)
50
+ if self.config.capture_duration:
51
+ new_node.call_start = time.perf_counter()
52
+ if self.add_stack_node(new_node):
53
+ return new_node
54
+
55
+ def add_stack_node(self, stack_node : Trace_Call__Stack_Node, frame=None):
56
+ if type(stack_node) is Trace_Call__Stack_Node:
57
+ if self.stack_data: # if there are items in the stack
58
+ self.top().children.append(stack_node) # add an xref to the new node to the children of the top node
59
+ else:
60
+ self.root_node = stack_node # if not this is the first node and capture it as a root node
61
+ self.stack_data.append(stack_node) # append the new node to the stack
62
+ return True
63
+ return False
64
+
65
+ def bottom(self):
66
+ if self.stack_data:
67
+ return self.stack_data[0]
68
+
69
+ def create_stack_node(self, frame, full_name, source_code, call_index):
70
+ new_node = Trace_Call__Stack_Node(call_index=call_index, name=full_name)
71
+ if frame:
72
+ code = frame.f_code
73
+ new_node.func_name = code.co_name # Get function name
74
+ new_node.module = frame.f_globals.get("__name__", "") # Get module name
75
+ if source_code:
76
+ new_node.source_code = source_code.get('source_code' )
77
+ new_node.source_code_caller = source_code.get('source_code_caller' )
78
+ new_node.source_code_location = source_code.get('source_code_location' )
79
+
80
+ if self.config.capture_frame:
81
+ new_node.frame = frame
82
+ if self.config.capture_locals:
83
+ if self.config.deep_copy_locals:
84
+ try:
85
+ new_node.locals = deepcopy(frame.f_locals)
86
+ except Exception as error:
87
+ new_node.locals = {'error': f'error in deepcopy: {error}'}
88
+ else:
89
+ new_node.locals = frame.f_locals
90
+
91
+ if self.config.capture_duration:
92
+ new_node.call_start = time.perf_counter()
93
+ return new_node
94
+
95
+ def empty_stack(self): # use to make sure the stack is empty (usally called at the end of a trace sessions)
96
+ for node in reversed(list(self.stack_data)):
97
+ self.pop(node)
98
+ return self
99
+
100
+ def map_source_code(self, frame):
101
+ if self.config.trace_capture_source_code:
102
+ filename = frame.f_code.co_filename
103
+ lineno = frame.f_lineno
104
+ source_code = linecache.getline(filename, lineno).strip()
105
+
106
+ caller_filename = frame.f_back.f_code.co_filename
107
+ caller_lineno = frame.f_back.f_lineno
108
+ source_code_caller = linecache.getline(caller_filename, caller_lineno).strip()
109
+ source_code_location = f'{filename}:{lineno}'
110
+ else:
111
+ source_code = ''
112
+ source_code_caller = ''
113
+ source_code_location = ''
114
+
115
+ return dict(source_code = source_code ,
116
+ source_code_caller = source_code_caller ,
117
+ source_code_location = source_code_location )
118
+
119
+ def map_full_name(self, frame, module, func_name):
120
+ if frame and module and func_name:
121
+ instance = frame.f_locals.get("self", None) # Get instance if available
122
+ try:
123
+ class_name = instance.__class__.__name__ if instance else ""
124
+ except Exception: # note: this will trigger this exception: ansi_text_visible_length("some text")
125
+ class_name = "<unavailable>"
126
+ if class_name:
127
+ full_name = f"{module}.{class_name}.{func_name}"
128
+ else:
129
+ full_name = f"{module}.{func_name}"
130
+ return full_name
131
+
132
+ def new_stack_node(self, name):
133
+ return Trace_Call__Stack_Node(call_index=self.call_index, name=name)
134
+
135
+ def nodes(self):
136
+ return self.stack_data
137
+
138
+ def remove_from_top(self, top_node, extra_data: dict):
139
+ if self.config.capture_duration:
140
+ top_node.call_end = time.perf_counter()
141
+ top_node.call_duration = top_node.call_end - top_node.call_start
142
+ if type(extra_data) is dict:
143
+ top_node.extra_data.update(extra_data)
144
+ self.stack_data.pop()
145
+ return True
146
+
147
+ def pop(self, target, extra_data: dict = None):
148
+ top_node = self.top()
149
+ if target and top_node :
150
+ if type(target) is Trace_Call__Stack_Node: # handle the case when target is Trace_Call__Stack_Node
151
+ if target == top_node: # if they match, pop the stack (since we are only capturing a subset of the stack)
152
+ return self.remove_from_top(top_node, extra_data)
153
+ elif target is top_node.frame: # if not assume target is a frame
154
+ return self.remove_from_top(top_node, extra_data) # if they match, pop the stack (since we are only capturing a subset of the stack)
155
+ return False
156
+
157
+ def push(self, frame):
158
+ return self.add_frame(frame)
159
+
160
+
161
+ def top(self):
162
+ if self.stack_data:
163
+ return self.stack_data[-1]
164
+
165
+ def size(self):
166
+ return self.stack_data.__len__()
@@ -0,0 +1,59 @@
1
+ from osbot_utils.utils.Misc import random_id
2
+
3
+ from osbot_utils.utils.Dev import pprint
4
+
5
+ from osbot_utils.base_classes.Kwargs_To_Self import Kwargs_To_Self
6
+
7
+ EXTRA_DATA__RETURN_VALUE = '(return_value)'
8
+
9
+ class Trace_Call__Stack_Node(Kwargs_To_Self):
10
+ call_duration : float
11
+ call_end : float
12
+ call_index : int
13
+ call_start : float
14
+ children : list
15
+ extra_data : dict
16
+ locals : dict
17
+ frame : None
18
+ func_name : str
19
+ lines : list
20
+ key : str
21
+ module : str
22
+ name : str
23
+ source_code : str
24
+ source_code_caller : str
25
+ source_code_location: str
26
+
27
+ # def __init__(self, **kwargs):
28
+ # super().__init__(**kwargs)
29
+ # #self.key = random_id()
30
+
31
+ def __eq__(self, other):
32
+ if not isinstance(other, Trace_Call__Stack_Node):
33
+ return False
34
+ if self is other:
35
+ return True
36
+ return self.data() == other.data()
37
+
38
+ def __repr__(self):
39
+ return f'Trace_Call__Stack_Node (call_index={self.call_index})'
40
+
41
+ def all_children(self):
42
+ all_children = self.children.copy() # Initialize the list with the current node's children
43
+ for child in self.children: # Recursively add the children of each child node
44
+ all_children.extend(child.all_children())
45
+ return all_children
46
+
47
+ def info(self):
48
+ return f'Stack_Node: call_index:{self.call_index} | name: {self.name} | children: {len(self.children)} | source_code: {self.source_code is not None}'
49
+
50
+ def data(self):
51
+ return self.__locals__()
52
+
53
+ def print(self):
54
+ pprint(self.data())
55
+
56
+ def print_info(self):
57
+ pprint(self.info())
58
+
59
+
@@ -0,0 +1,71 @@
1
+ from collections import defaultdict, Counter
2
+ from copy import copy
3
+
4
+ from osbot_utils.utils.Dev import pprint
5
+
6
+ from osbot_utils.base_classes.Kwargs_To_Self import Kwargs_To_Self
7
+
8
+
9
+ class Trace_Call__Stats(Kwargs_To_Self):
10
+
11
+ calls : int
12
+ calls_skipped : int
13
+ exceptions : int
14
+ lines : int
15
+ returns : int
16
+ unknowns : int # to use for extra events that are not being captured
17
+ raw_call_stats : list
18
+
19
+ def __repr__(self):
20
+ return str(self.stats())
21
+
22
+ def __eq__(self, target):
23
+ if self is target:
24
+ return True
25
+ return self.stats() == target
26
+
27
+ def log_frame(self, frame):
28
+ code = frame.f_code
29
+ func_name = code.co_name
30
+ module = frame.f_globals.get("__name__", "")
31
+ self.raw_call_stats.append((module, func_name))
32
+ return self
33
+
34
+ def frames_stats__build_tree(self, d, path, function_name):
35
+ parts = path.split('.')
36
+ current_level = d
37
+ for part in parts[:-1]: # Go up to the second-to-last element
38
+ if part not in current_level:
39
+ current_level[part] = {}
40
+ current_level = current_level[part]
41
+ if parts[-1] not in current_level:
42
+ current_level[parts[-1]] = Counter()
43
+ current_level[parts[-1]][function_name] += 1
44
+
45
+ def frames_stats(self):
46
+ processed_frame_stats = self.frames_stats__process_raw_data()
47
+ return self.to_standard_dict(processed_frame_stats)
48
+
49
+ def frames_stats__process_raw_data(self):
50
+ tree = defaultdict(dict)
51
+ for module, function_name in self.raw_call_stats:
52
+ self.frames_stats__build_tree(tree, module, function_name)
53
+ return tree
54
+
55
+
56
+ def to_standard_dict(self, d):
57
+ if isinstance(d, defaultdict):
58
+ d = {k: self.to_standard_dict(v) for k, v in d.items()}
59
+ if isinstance(d, Counter):
60
+ d = dict(d)
61
+ if isinstance(d, dict):
62
+ return {k: self.to_standard_dict(v) for k, v in d.items()}
63
+ return d
64
+
65
+ def stats(self):
66
+ stats = copy(self.__locals__())
67
+ del stats['raw_call_stats']
68
+ return stats
69
+
70
+ def print(self):
71
+ pprint(self.stats())
@@ -0,0 +1,75 @@
1
+ from osbot_utils.utils.Dev import pprint
2
+
3
+
4
+ class Trace_Call__View_Model:
5
+
6
+ def __init__(self):
7
+ self.view_model = []
8
+
9
+ def create(self, stack):
10
+ root_node = stack.root_node
11
+ if root_node:
12
+ target = [root_node]
13
+ self.view_model = self.create_view_model(target)
14
+ self.fix_view_mode() # Fix the view mode for the last node
15
+ return self.view_model
16
+
17
+ # todo: rename view_model so that it is not confused with self.view_model
18
+ def create_view_model(self, json_list, level=0, prefix="", view_model=None):
19
+ if view_model is None:
20
+ view_model = [] # Initialize view model if None
21
+ for idx, node in enumerate(json_list): # Iterate over each node in the JSON list to populate the view model
22
+ components = node.name.split('.')
23
+ duration = node.call_duration
24
+ extra_data = node.extra_data
25
+ frame_locals = node.locals
26
+ lines = node.lines
27
+ source_code = node.source_code
28
+ source_code_caller = node.source_code_caller
29
+ source_code_location = node.source_code_location
30
+ method_name = components[-1]
31
+ if len(components) > 1:
32
+ method_parent = f"{components[-2]}"
33
+ else:
34
+ method_parent = ""
35
+ if method_name == "__init__": # Adjust the method_name based on special method names like __init__ and __call__
36
+ method_name = f"{method_parent}.{method_name}"
37
+ elif method_name == "__call__":
38
+ method_name = f"{method_parent}.{method_name}"
39
+ elif method_name == "<module>":
40
+ method_name = f"{method_parent}.{method_name}"
41
+
42
+ pruned_parents = [comp for comp in components]
43
+ parent_info = '.'.join(pruned_parents[:-1])
44
+
45
+ if level == 0: # Handle tree representation at level 0
46
+ emoji = "đŸ“Ļ "
47
+ tree_branch = ""
48
+ else:
49
+ is_last_sibling = (idx == len(json_list) - 1) # Check if the node is the last sibling
50
+ tree_branch = "└── " if is_last_sibling else "├── "
51
+ emoji = "đŸ§Šī¸" if not node.children else "đŸ”—ī¸"
52
+
53
+ view_model.append({ 'duration' : duration ,
54
+ 'emoji' : emoji ,
55
+ 'extra_data' : extra_data ,
56
+ 'method_name' : method_name ,
57
+ 'method_parent' : method_parent ,
58
+ 'lines' : lines ,
59
+ 'locals' : frame_locals , # todo finish refactoring use of locals to frame_locals
60
+ 'parent_info' : parent_info ,
61
+ 'prefix' : prefix ,
62
+ 'source_code' : source_code ,
63
+ 'source_code_caller' : source_code_caller ,
64
+ 'source_code_location': source_code_location ,
65
+ 'tree_branch' : tree_branch ,})
66
+ next_prefix = prefix + (" " if tree_branch == "└── " else "│ ") # Calculate the prefix for the next level
67
+ self.create_view_model(node.children, level + 1, prefix=next_prefix, view_model=view_model)
68
+
69
+ return view_model
70
+
71
+ def fix_view_mode(self):
72
+ if len(self.view_model) > 0: # these changes will provide a nice end of tree, for example replacing "│ ├──" with "└────────── "
73
+ last_node = self.view_model[-1] # Get the last node in the view model
74
+ last_node['prefix'] = last_node['prefix'].replace(' ', '─').replace('│', '└') # Update the prefix for the last node
75
+ last_node['tree_branch'] = '─── '
@@ -0,0 +1,33 @@
1
+ from osbot_utils.helpers.trace.Trace_Call import Trace_Call
2
+
3
+
4
+ class Trace_Files(Trace_Call):
5
+
6
+ files: list
7
+
8
+ def __init__(self, **kwargs):
9
+ super().__init__(**kwargs)
10
+
11
+ # def trace_calls(self, frame, event, arg):
12
+ # if event == 'call':
13
+ # self.files.append(frame.f_code.co_filename)
14
+ #
15
+ # # if event != 'call':
16
+ # # return
17
+ # #
18
+ # #
19
+ # # # Get the function object being called
20
+ # # func = frame.f_globals.get(frame.f_code.co_name, None)
21
+ # #
22
+ # # # Retrieve the source code if the function object is available
23
+ # # if func:
24
+ # # try:
25
+ # # source_code = inspect.getsource(func)
26
+ # # print(f"Source code of {func.__name__}:\n{source_code}\n")
27
+ # # except TypeError:
28
+ # # pass # Handle cases where source code can't be retrieved
29
+ # #
30
+ #
31
+ # return super().trace_calls(frame, event, arg)
32
+
33
+
File without changes