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,54 @@
1
+ from pprint import pprint
2
+
3
+
4
+ class Catch:
5
+ """
6
+ Helper class for cases when the native Python exception traces is too noisy
7
+ """
8
+ def __init__(self, log_exception=False, log_headers=True, logger=None, expected_error=None, catch_exception=True, expect_exception=False):
9
+ self.log_exception = log_exception
10
+ self.log_headers = log_headers
11
+ self.logger = logger or print
12
+ self.exception_type = None
13
+ self.exception_value = None
14
+ self.exception_traceback = None
15
+ self.execution_complete = False
16
+ self.expected_error = expected_error
17
+ self.catch_exception = catch_exception
18
+ self.expect_exception = expect_exception # this is useful for Unit tests
19
+
20
+ def __repr__(self):
21
+ if self.execution_complete:
22
+ return f'Catch: {self.exception_type} : {self.exception_value}'
23
+ else:
24
+ return f'Catch: (not executed yet)'
25
+
26
+ def __enter__(self):
27
+ return self
28
+
29
+ def __exit__(self, exception_type, exception_value, exception_traceback):
30
+ self.exception_type = exception_type
31
+ self.exception_value = exception_value
32
+ self.exception_traceback = exception_traceback
33
+ self.execution_complete = True
34
+ if self.log_exception:
35
+ if exception_type is not None:
36
+ if self.log_headers:
37
+ self.log()
38
+ self.log("********* Catch ***********")
39
+ self.log(exception_type)
40
+ self.log()
41
+ self.log(exception_value)
42
+ if self.expected_error:
43
+ self.assert_error_is(self.expected_error)
44
+ if self.expect_exception and exception_type is None:
45
+ raise Exception(f'Expected exception: {self.expected_error} but no exception was raised')
46
+ if self.catch_exception:
47
+ return True # returning true here will prevent the exception to be propagated (which is the objective of this class :) )
48
+ return False
49
+
50
+ def assert_error_is(self, expected_error):
51
+ assert str(self) == expected_error , str(self)
52
+
53
+ def log(self, message=''):
54
+ self.logger(message)
@@ -0,0 +1,69 @@
1
+ import inspect
2
+ from datetime import timedelta
3
+ from functools import wraps
4
+
5
+ from osbot_utils.utils.Call_Stack import Call_Stack
6
+
7
+ from osbot_utils.utils.Misc import date_time_now, time_delta_to_str
8
+
9
+
10
+ def duration(func):
11
+ if inspect.iscoroutinefunction(func):
12
+ # It's an async function
13
+ @wraps(func)
14
+ async def async_wrapper(*args, **kwargs):
15
+ with Duration(prefix=f'.{func.__name__} took'):
16
+ return await func(*args, **kwargs)
17
+ return async_wrapper
18
+ else:
19
+ # It's a regular function
20
+ @wraps(func)
21
+ def sync_wrapper(*args, **kwargs):
22
+ with Duration(prefix=f'.{func.__name__} took'):
23
+ return func(*args, **kwargs)
24
+ return sync_wrapper
25
+
26
+ class Duration:
27
+ """
28
+ Helper class for to capture time duration
29
+ """
30
+ def __init__(self, prefix="\nDuration:", print_result=True, use_utc=True, print_stack=False):
31
+ self.use_utc = use_utc
32
+ self.print_result = print_result
33
+ self.prefix = prefix
34
+ self.start_time = None
35
+ self.end_time = None
36
+ self.duration = None
37
+ self.print_stack = print_stack
38
+ if True or print_stack:
39
+ self.call_stack = Call_Stack()
40
+
41
+ def __enter__(self):
42
+ if self.print_stack:
43
+ self.call_stack.capture()
44
+ self.start()
45
+ return self
46
+
47
+ def __exit__(self, exception_type, exception_value, exception_traceback):
48
+ self.end()
49
+
50
+ def start(self):
51
+ self.start_time = date_time_now(use_utc=self.use_utc, return_str=False)
52
+
53
+ def end(self):
54
+ self.end_time = date_time_now(use_utc=self.use_utc, return_str=False)
55
+ self.duration = self.end_time - self.start_time
56
+ if self.print_result:
57
+ print(f"{self.prefix} {time_delta_to_str(self.duration)}")
58
+ if self.print_stack:
59
+ self.call_stack.print()
60
+
61
+ def milliseconds(self):
62
+ return self.duration.total_seconds() * 1000
63
+
64
+ def seconds(self):
65
+ return self.duration.total_seconds()
66
+
67
+ def set_duration(self,seconds:int):
68
+ self.duration = timedelta(seconds=seconds)
69
+ return self
@@ -0,0 +1,118 @@
1
+ from osbot_utils.helpers.Print_Table import Print_Table
2
+ from osbot_utils.utils.Call_Stack import Call_Stack
3
+
4
+ from osbot_utils.testing.Duration import Duration
5
+
6
+ class Hook_Method:
7
+
8
+ def __init__(self, target_module, target_method):
9
+ self.target_module = target_module
10
+ self.target_method = target_method
11
+ self.target = getattr(target_module, target_method)
12
+ self.wrapper_method = None
13
+ self.calls = []
14
+ self.on_before_call = []
15
+ self.on_after_call = []
16
+ self.mock_call = None
17
+
18
+ def __enter__(self):
19
+ self.wrap()
20
+ return self
21
+
22
+ def __exit__(self, type, value, traceback):
23
+ self.unwrap()
24
+
25
+ def add_on_after_call(self, on_after_call):
26
+ """
27
+ method to be after before the Hooked call
28
+
29
+ method signature: def on_after_call(return_value, *args, **kwargs):
30
+ should return: return_value
31
+ """
32
+ self.on_after_call.append(on_after_call)
33
+ return self
34
+
35
+ def add_on_before_call(self, on_before_call):
36
+ """
37
+ method to be called before the Hooked call
38
+
39
+ method signature: def on_after_call(*args, **kwargs):
40
+ should return: (args, kwargs)
41
+ """
42
+ self.on_before_call.append(on_before_call)
43
+ return self
44
+
45
+ def calls_count(self):
46
+ return len(self.calls)
47
+
48
+ def calls_last_one(self):
49
+ if len(self.calls) > 0:
50
+ return self.calls[-1]
51
+
52
+ def after_call(self, return_value, *args, **kwargs):
53
+ """
54
+ call all methods added via `add_on_after_call` with the params: return_value, *args, **kwargs
55
+ return value from each on_after_call will on override existing return_value
56
+ """
57
+ for method in self.on_after_call:
58
+ return_value = method(return_value, *args, **kwargs)
59
+ return return_value
60
+
61
+ def before_call(self, *args, **kwargs):
62
+ """
63
+ call all methods added via `add_on_before_call` with the params: *args, **kwargs
64
+ return value is expected to be args and kwargs on each on_after_call which will on override existing args and kwargs values
65
+ """
66
+ for method in self.on_before_call:
67
+ (args, kwargs) = method(*args, **kwargs)
68
+ return (args, kwargs)
69
+
70
+ def print(self):
71
+ print()
72
+ print()
73
+ with Print_Table() as _:
74
+ _.print(self.calls)
75
+
76
+ def set_mock_call(self, mock_call):
77
+ """
78
+ Use this to simulate a call to the Hooked Method (the
79
+ """
80
+ self.mock_call = mock_call
81
+
82
+ def wrap(self):
83
+
84
+ def wrapper_method(*args, **kwargs):
85
+ call_stack= Call_Stack().capture()
86
+ with Duration(print_result=False) as duration:
87
+ exception = None
88
+ if self.mock_call:
89
+ return_value = self.mock_call(*args,**kwargs)
90
+ else:
91
+ (args, kwargs) = self.before_call(*args, **kwargs)
92
+ try:
93
+ return_value = self.target(*args, **kwargs)
94
+ return_value = self.after_call(return_value, args, kwargs)
95
+ except Exception as error:
96
+ return_value = None
97
+ exception = error
98
+ #raise error
99
+
100
+ call = {
101
+ 'args' : args ,
102
+ 'call_stack' : call_stack ,
103
+ 'exception' : exception ,
104
+ 'kwargs' : kwargs ,
105
+ 'return_value': return_value ,
106
+ 'index' : len(self.calls) ,
107
+ 'duration' : int(duration.seconds()*1000)
108
+ }
109
+ self.calls.append(call)
110
+ return call['return_value']
111
+
112
+ self.wrapper_method = wrapper_method
113
+ setattr(self.target_module, self.target_method, self.wrapper_method)
114
+ return self.wrapper_method
115
+
116
+ def unwrap(self):
117
+ setattr(self.target_module, self.target_method, self.target)
118
+
@@ -0,0 +1,46 @@
1
+ # todo: finish code below so that log entries are stored in the queue
2
+ # expand to other capabilites of QueueHandler (namely using multiple handlers)
3
+ # import logging
4
+ # import queue
5
+ # from io import StringIO
6
+ # from logging.handlers import QueueListener, QueueHandler
7
+ #
8
+ #
9
+ # class Log_To_Queue():
10
+ #
11
+ # def __init__(self,logger):
12
+ # self.logger = logger
13
+ # self.queue = None
14
+ # self.queue_listener = None
15
+ # self.queue_handler = None
16
+ #
17
+ # def __enter__(self):
18
+ # self.add_handler()
19
+ # return self
20
+ #
21
+ # def __exit__(self, exception_type, exception_value, exception_traceback):
22
+ # self.remove_handler()
23
+ #
24
+ # def add_handler(self):
25
+ # self.queue = queue.Queue(-1)
26
+ # self.queue_listener = QueueListener(self.queue) # not using any handlers
27
+ # self.queue_handler = QueueHandler (self.queue)
28
+ # self.logger.addHandler(self.queue_handler)
29
+ # self.queue_listener.start()
30
+ #
31
+ # def contents(self):
32
+ # return "self.string_stream.getvalue()"
33
+ #
34
+ # def set_level(self, level):
35
+ # self.logger.setLevel(level)
36
+ # return self
37
+ #
38
+ # def set_level_critical(self): return self.set_level('CRITICAL') # level 50
39
+ # def set_level_debug (self): return self.set_level('DEBUG' ) # level 10
40
+ # def set_level_error (self): return self.set_level('ERROR' ) # level 40
41
+ # def set_level_info (self): return self.set_level('INFO' ) # level 20
42
+ # def set_level_warning (self): return self.set_level('WARNING' ) # level 30
43
+ #
44
+ # def remove_handler(self):
45
+ # self.queue_listener.stop()
46
+ # self.logger.removeHandler(self.queue_handler)
@@ -0,0 +1,37 @@
1
+ import logging
2
+ from io import StringIO
3
+
4
+ class Log_To_String():
5
+
6
+ def __init__(self,logger):
7
+ self.logger = logger
8
+ self.string_stream = None
9
+ self.string_handler = None
10
+
11
+ def __enter__(self):
12
+ self.add_handler()
13
+ return self
14
+
15
+ def __exit__(self, exception_type, exception_value, exception_traceback):
16
+ self.remove_handler()
17
+
18
+ def add_handler(self):
19
+ self.string_stream = StringIO()
20
+ self.string_handler = logging.StreamHandler(self.string_stream)
21
+ self.logger.addHandler(self.string_handler)
22
+
23
+ def contents(self):
24
+ return self.string_stream.getvalue()
25
+
26
+ def set_level(self, level):
27
+ self.logger.setLevel(level)
28
+ return self
29
+
30
+ def set_level_critical(self): return self.set_level('CRITICAL') # level 50
31
+ def set_level_debug (self): return self.set_level('DEBUG' ) # level 10
32
+ def set_level_error (self): return self.set_level('ERROR' ) # level 40
33
+ def set_level_info (self): return self.set_level('INFO' ) # level 20
34
+ def set_level_warning (self): return self.set_level('WARNING' ) # level 30
35
+
36
+ def remove_handler(self):
37
+ self.logger.removeHandler(self.string_handler)
@@ -0,0 +1,81 @@
1
+ import inspect
2
+ import logging
3
+ from io import StringIO
4
+
5
+ import sys
6
+
7
+ from osbot_utils.decorators.methods.cache_on_self import cache_on_self
8
+
9
+ #DEFAULT_LOG_FORMAT = '%(asctime)s.%(msecs)03d %(levelname)s - %(message)s'
10
+ DEFAULT_LOG_FORMAT = '%(levelname)s - %(message)s'
11
+ DEFAULT_LOG_LEVEL = logging.DEBUG
12
+ DEFAULT_DATE_FORMAT = '%M:%S'
13
+ class Logging:
14
+
15
+ def __init__(self, target=None, log_level: int = None, log_format=None, log_to_console=False, date_format=None):
16
+ self.target = target
17
+ self.log_level = log_level or DEFAULT_LOG_LEVEL
18
+ self.log_format = log_format or DEFAULT_LOG_FORMAT
19
+ self.date_format = date_format or DEFAULT_DATE_FORMAT
20
+ if log_to_console:
21
+ self.log_to_sys_stdout()
22
+
23
+ def __enter__(self): return self
24
+ def __exit__ (self, exc_type, exc_val, exc_tb): pass
25
+
26
+ def add_stream_handler(self, stream):
27
+ stream_handler = logging.StreamHandler(stream=stream)
28
+ self.logger().addHandler(stream_handler)
29
+ self.set_logger_level()
30
+ self.set_format(stream_handler)
31
+
32
+ return stream_handler
33
+
34
+ @cache_on_self
35
+ def logger(self):
36
+ if self.target is not None:
37
+ if inspect.isclass(self.target) or inspect.ismodule(self.target):
38
+ self.target = self.target.__name__
39
+ return logging.getLogger(self.target)
40
+
41
+ def enable_log_to_console(self, log_level=logging.INFO):
42
+ self.log_to_sys_stdout()
43
+ self.set_logger_level(log_level)
44
+ return self
45
+
46
+ def enable_pycharm_logging(self):
47
+ if self.is_pycharm_running():
48
+ self.log_to_sys_stdout()
49
+ return self
50
+
51
+ def is_pycharm_running(self) -> bool:
52
+ first_arg = sys.argv[0]
53
+ return ('docrunner.py' in first_arg) or ('pytest_runner.py' in first_arg)
54
+
55
+ def log_to_sys_stdout(self):
56
+ return self.add_stream_handler(sys.stdout)
57
+
58
+ def log_to_string_io(self):
59
+ log_stream = StringIO()
60
+ return self.add_stream_handler(log_stream)
61
+
62
+
63
+ def set_format(self, stream_handler):
64
+ formatter = logging.Formatter(fmt=self.log_format, datefmt=self.date_format)
65
+ stream_handler.setFormatter(formatter)
66
+ return formatter
67
+
68
+ def set_format_on_all_handlers(self):
69
+ for handler in logging.root.handlers[:]:
70
+ handler.setFormatter(logging.Formatter(fmt=self.log_format, datefmt=self.date_format))
71
+ return self
72
+
73
+ def set_logger_level(self, level=None):
74
+ self.logger().setLevel(level or self.log_level)
75
+
76
+
77
+ def info (self,message, *args, **kwargs): self.logger().info (message, *args, **kwargs)
78
+ def warning (self,message, *args, **kwargs): self.logger().warning (message, *args, **kwargs)
79
+ def debug (self,message, *args, **kwargs): self.logger().debug (message, *args, **kwargs)
80
+ def error (self,message, *args, **kwargs): self.logger().error (message, *args, **kwargs)
81
+ def critical(self,message, *args, **kwargs): self.logger().critical (message, *args, **kwargs)
@@ -0,0 +1,52 @@
1
+ from unittest.mock import patch, MagicMock, _patch
2
+ from osbot_utils.utils.Dev import pprint
3
+ from osbot_utils.base_classes.Kwargs_To_Self import Kwargs_To_Self
4
+
5
+
6
+ class Patch_Print(Kwargs_To_Self):
7
+ enabled : bool = True
8
+ expected_calls : list
9
+ mocked_print : MagicMock
10
+ patched_print : _patch
11
+ print_calls : bool
12
+
13
+
14
+ def __init__(self, **kwargs):
15
+ super().__init__(**kwargs)
16
+
17
+ def __enter__(self):
18
+ if self.enabled:
19
+ self.patched_print = patch('builtins.print')
20
+ self.mocked_print = self.patched_print.start()
21
+ return self
22
+
23
+ def __exit__(self, exc_type, exc_val, exc_tb):
24
+ if self.enabled:
25
+ self.patched_print.stop()
26
+
27
+ if self.print_calls:
28
+ pprint(self.calls())
29
+ #print(self.mocked_print.call_args_list)
30
+
31
+ if self.expected_calls:
32
+ assert self.calls() == self.expected_calls
33
+
34
+ def call_args_list(self):
35
+ if self.mocked_print:
36
+ return self.mocked_print.call_args_list
37
+ return []
38
+
39
+ def calls(self):
40
+ calls_data = []
41
+ if self.mocked_print:
42
+ for call in self.mocked_print.call_args_list:
43
+ if len(call.args) == 0 and call.kwargs == {}:
44
+ call_data = ''
45
+ elif len(call.args) == 1 and call.kwargs == {}:
46
+ call_data = call.args[0]
47
+ else:
48
+ call_data = (call.args, call.kwargs)
49
+ calls_data.append(call_data)
50
+ return calls_data
51
+
52
+
@@ -0,0 +1,89 @@
1
+ import sys
2
+
3
+ # see https://explog.in/notes/settrace.html for ideas on how to expand this class
4
+
5
+ class Profiler:
6
+
7
+ def __init__(self):
8
+ self.events = []
9
+ self.profile_options = self.default_profile_options()
10
+ self.previous_profiler = self.current_profiler()
11
+ self.on_event = None
12
+
13
+ def __enter__(self):
14
+ sys.setprofile(self.profiling_function)
15
+ return self
16
+
17
+ def __exit__(self, exception_type, exception_value, exception_traceback):
18
+ sys.setprofile(self.previous_profiler)
19
+
20
+ def add_values(self, profile_options, source):
21
+ item = {}
22
+ for arg_name in set(profile_options):
23
+ option = profile_options.get(arg_name)
24
+ value = getattr(source, arg_name)
25
+ if type(option) is dict:
26
+ item[arg_name] = self.add_values(option, value)
27
+ else:
28
+ if profile_options.get(arg_name):
29
+ if arg_name == 'f_locals':
30
+ item[arg_name] = value.copy() # create a copy of the var
31
+ else:
32
+ item[arg_name] = value
33
+ return item
34
+
35
+ def current_profiler(self):
36
+ return sys.getprofile()
37
+
38
+ def default_profile_options(self):
39
+ return {
40
+ 'f_back' : False,
41
+ 'f_builtins' : False,
42
+ 'f_code' : {
43
+ 'co_argcount' : True ,
44
+ 'co_cellvars' : True ,
45
+ 'co_code' : True ,
46
+ 'co_consts' : True ,
47
+ 'co_filename' : True ,
48
+ 'co_firstlineno' : True ,
49
+ 'co_flags' : True ,
50
+ 'co_freevars' : True ,
51
+ 'co_kwonlyargcount' : True ,
52
+ 'co_lnotab' : True ,
53
+ 'co_name' : True ,
54
+ 'co_names' : True ,
55
+ 'co_nlocals' : True ,
56
+ 'co_posonlyargcount': True ,
57
+ 'co_stacksize' : True ,
58
+ 'co_varnames' : True ,
59
+ } ,
60
+ 'f_globals' : False,
61
+ 'f_lasti' : True ,
62
+ 'f_lineno' : True ,
63
+ 'f_locals' : True ,
64
+ 'f_trace' : True ,
65
+ 'f_trace_lines' : True ,
66
+ 'f_trace_opcodes': True
67
+ }
68
+
69
+ def get_last_event(self):
70
+ return self.events.pop()
71
+
72
+ def get_f_locals(self):
73
+ return self.get_last_event().get('f_locals')
74
+
75
+ def get_f_locals_variable(self, var_name):
76
+ return self.get_f_locals().get(var_name)
77
+
78
+ def profiling_function(self, frame, event, arg):
79
+ if type(frame.f_locals.get('self')) != Profiler: # dont' capture traces of the current (Trace) class
80
+ item = self.add_values(self.profile_options, frame)
81
+ item['arg' ] = arg
82
+ item['event'] = event
83
+ self.events.append(item)
84
+ if self.on_event:
85
+ self.on_event(self,frame, event, arg) # allow the caler to see and modify the data (after its data been captured)
86
+
87
+ def set_on_event(self, on_event):
88
+ self.on_event = on_event
89
+ return self
@@ -0,0 +1,19 @@
1
+ import io
2
+ from contextlib import redirect_stderr
3
+
4
+
5
+ class Stderr:
6
+ def __init__(self):
7
+ self.output = io.StringIO()
8
+ self.redirect_stderr = redirect_stderr(self.output)
9
+
10
+ def __enter__(self):
11
+ self.redirect_stderr.__enter__()
12
+ return self
13
+
14
+ def __exit__(self, exc_type, exc_val, exc_tb):
15
+ self.redirect_stderr.__exit__(exc_type, exc_val, exc_tb)
16
+
17
+ def value(self):
18
+ return self.output.getvalue()
19
+
@@ -0,0 +1,19 @@
1
+ import io
2
+ from contextlib import redirect_stdout
3
+
4
+
5
+ class Stdout:
6
+ def __init__(self):
7
+ self.output = io.StringIO()
8
+ self.redirect_stdout = redirect_stdout(self.output)
9
+
10
+ def __enter__(self):
11
+ self.redirect_stdout.__enter__()
12
+ return self
13
+
14
+ def __exit__(self, exc_type, exc_val, exc_tb):
15
+ self.redirect_stdout.__exit__(exc_type, exc_val, exc_tb)
16
+
17
+ def value(self):
18
+ return self.output.getvalue()
19
+
@@ -0,0 +1,46 @@
1
+ from osbot_utils.utils.Files import Files, file_delete, folder_delete_all, files_list, file_create, file_name, \
2
+ parent_folder, file_exists, file_contents
3
+ from osbot_utils.utils.Misc import random_filename
4
+
5
+
6
+ class Temp_File:
7
+ def __init__(self, contents='...', extension='tmp'):
8
+ self.tmp_file = random_filename(extension)
9
+ self.tmp_folder = None
10
+ self.file_path = None
11
+ self.original_contents = contents
12
+
13
+ def __enter__(self):
14
+ self.tmp_folder = Files.temp_folder(prefix='temp_folder_')
15
+ self.file_path = Files.path_combine(self.tmp_folder, self.tmp_file)
16
+ file_create(self.file_path, self.original_contents)
17
+ return self
18
+
19
+ def __exit__(self, type, value, traceback):
20
+ file_delete (self.file_path)
21
+ folder_delete_all(self.tmp_folder)
22
+
23
+ def contents(self):
24
+ return file_contents(self.file_path)
25
+
26
+ def delete(self):
27
+ return file_delete(self.file_path)
28
+
29
+ def exists(self):
30
+ return file_exists(self.file_path)
31
+
32
+ def file_name(self):
33
+ return file_name(self.path())
34
+
35
+ def files_in_folder(self):
36
+ return files_list(self.tmp_folder)
37
+
38
+ def folder(self):
39
+ return parent_folder(self.path())
40
+
41
+ def path(self):
42
+ return self.file_path
43
+
44
+ def write(self, contents):
45
+ file_create(self.file_path, contents)
46
+ return self