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,301 @@
1
+ import inspect
2
+ import logging
3
+ import sys
4
+ import types
5
+ from logging import Logger, StreamHandler, FileHandler
6
+ from logging.handlers import MemoryHandler
7
+
8
+ from osbot_utils.decorators.lists.group_by import group_by
9
+ from osbot_utils.decorators.lists.index_by import index_by
10
+ from osbot_utils.decorators.methods.cache_on_function import cache_on_function
11
+ from osbot_utils.decorators.methods.cache_on_self import cache_on_self
12
+ from osbot_utils.utils.Misc import random_string
13
+ from osbot_utils.utils.Files import temp_file
14
+ from osbot_utils.utils.Objects import obj_dict
15
+
16
+ DEFAULT_LOG_LEVEL = logging.DEBUG
17
+ DEFAULT_LOG_FORMAT = '%(asctime)s\t|\t%(name)s\t|\t%(levelname)s\t|\t%(message)s'
18
+ MEMORY_LOGGER_CAPACITY = 1024*10
19
+ MEMORY_LOGGER_FLUSH_LEVEL = logging.ERROR
20
+
21
+ # for reference here are the log levels
22
+ # CRITICAL 50
23
+ # ERROR 40
24
+ # WARNING 30
25
+ # INFO 20
26
+ # DEBUG 10
27
+ # NOTSET 0
28
+
29
+ class Python_Logger_Config:
30
+
31
+ def __init__(self):
32
+ self.elastic_host = None
33
+ self.elastic_password = None
34
+ self.elastic_port = None
35
+ self.elastic_username = None
36
+ #self.log_to_aws_s3 = False # todo
37
+ #self.log_to_aws_cloud_trail = False # todo
38
+ #self.log_to_aws_firehose = False # todo
39
+ self.log_to_console = False # todo
40
+ self.log_to_file = False # todo
41
+ #self.log_to_elastic = False # todo
42
+ self.log_to_memory = False
43
+ self.path_logs = None
44
+ self.log_format = DEFAULT_LOG_FORMAT
45
+ self.log_level = DEFAULT_LOG_LEVEL
46
+
47
+
48
+ class Python_Logger:
49
+ config : Python_Logger_Config
50
+ logger : Logger
51
+ logger_name : str
52
+ critical : types.FunctionType # these will be replaced by Python_Logger_Config.setup_log_methods
53
+ debug : types.FunctionType
54
+ error : types.FunctionType
55
+ exception : types.FunctionType
56
+ info : types.FunctionType
57
+ ok : types.FunctionType
58
+ warning : types.FunctionType
59
+
60
+ def __init__(self, logger_name= None, logger_config : Python_Logger_Config = None):
61
+ self.set_logger_name(logger_name)
62
+ #self.logger_name = logger_name or random_string(prefix="Python_Logger_")
63
+ self.set_config(logger_config)
64
+ # self.logger = None
65
+ self.setup() # todo: understand side effect of setting up logger on __init__
66
+
67
+ def disable(self):
68
+ self.logger.disabled = True
69
+ return self
70
+
71
+ def set_logger_name(self, logger_name):
72
+ if logger_name: # if the value is provided, use it
73
+ self.logger_name = logger_name
74
+ return self
75
+ for frame_info in inspect.stack(): # Look for the first frame that is outside this Python_Logger class
76
+ if 'self' in frame_info.frame.f_locals:
77
+ caller_self = frame_info.frame.f_locals['self']
78
+ caller_module = caller_self.__class__.__name__
79
+ if caller_module != 'Python_Logger':
80
+ self.logger_name = 'Python_Logger__' + caller_module
81
+ return self
82
+
83
+ self.logger_name = random_string(prefix="Python_Logger_")
84
+ return self
85
+
86
+ def manager_get_loggers(self):
87
+ return Logger.manager.loggerDict
88
+
89
+ def manager_remove_logger(self):
90
+ logger_dict = Logger.manager.loggerDict
91
+ if self.logger_name in logger_dict: # need to do it manually here since Logger.manager doesn't seem to have a way to remove loggers
92
+ del logger_dict[self.logger_name]
93
+ return True
94
+ return False
95
+
96
+ def setup(self, logger_name=None, log_level=None,add_console_logger=False, add_memory_logger=True):
97
+ if logger_name:
98
+ self.logger_name = logger_name
99
+ self.logger = logging.getLogger(self.logger_name)
100
+ self.setup_log_methods()
101
+ self.set_log_level(log_level)
102
+ if add_console_logger:
103
+ self.add_console_logger()
104
+ if add_memory_logger:
105
+ self.add_handler_memory()
106
+ return self
107
+
108
+ def setup_log_methods(self):
109
+ # adds these helper methods like this so that the filename and function values are accurate
110
+ setattr(self, "critical" , self.logger.critical )
111
+ setattr(self, "debug" , self.logger.debug )
112
+ setattr(self, "error" , self.logger.error )
113
+ setattr(self, "exception" , self.logger.exception )
114
+ setattr(self, "info" , self.logger.info )
115
+ setattr(self, "ok" , self.logger.info )
116
+ setattr(self, "warning" , self.logger.warning )
117
+
118
+
119
+
120
+
121
+
122
+ # self.info = self.logger.info
123
+ # self.warning = self.logger.warning
124
+ # self.error = self.logger.error
125
+ # self.exception = self.logger.exception
126
+ # self.critical = self.logger.critical
127
+
128
+
129
+ # Setters
130
+ def set_config(self, config):
131
+ if type(config) is Python_Logger_Config:
132
+ self.config = config
133
+ else:
134
+ self.config = Python_Logger_Config()
135
+ return self.config
136
+
137
+ def set_log_format(self, log_format):
138
+ if log_format:
139
+ self.config.log_format = log_format
140
+
141
+ def set_log_level(self, level=None):
142
+ level = level or self.config.log_level
143
+ if self.logger:
144
+ self.logger.setLevel(level)
145
+ return True
146
+ return False
147
+
148
+ # Getters
149
+
150
+ def log_handler(self, handler_type):
151
+ for handler in self.log_handlers():
152
+ if type(handler) is handler_type:
153
+ return handler
154
+ return None
155
+
156
+ def log_handler_console(self):
157
+ return self.log_handler(StreamHandler)
158
+
159
+ def log_handler_file(self):
160
+ return self.log_handler(logging.FileHandler)
161
+
162
+ def log_handler_memory(self):
163
+ return self.log_handler(MemoryHandler)
164
+
165
+ def log_handlers(self):
166
+ if self.logger:
167
+ return self.logger.handlers
168
+ return []
169
+
170
+ def log_handlers_remove(self, handler):
171
+ if handler and handler in self.log_handlers():
172
+ self.logger.removeHandler(handler)
173
+ return True
174
+ return False
175
+
176
+ def log_handlers_remove_type(self, handler_type):
177
+ handler = self.log_handler(handler_type)
178
+ return self.log_handlers_remove(handler)
179
+
180
+ def log_formatter(self):
181
+ return logging.Formatter(self.config.log_format)
182
+
183
+ def log_level(self):
184
+ return self.config.log_level
185
+
186
+ # Actions
187
+
188
+ def add_console_logger(self):
189
+ self.config.log_to_console = True
190
+ return self.add_handler_console()
191
+
192
+ def add_memory_logger(self):
193
+ self.config.log_to_memory = True
194
+ return self.add_handler_memory()
195
+
196
+ def add_file_logger(self,path_log_file=None):
197
+ self.config.log_to_file = True
198
+ return self.add_handler_file(path_log_file=path_log_file)
199
+
200
+ def remove_memory_logger(self):
201
+ memory_logger = self.log_handler_memory()
202
+ if self.log_handlers_remove(memory_logger):
203
+ self.config.log_to_file = False
204
+ return True
205
+ return False
206
+
207
+
208
+ # Handlers
209
+ def add_handler_console(self):
210
+ if self.logger and self.config.log_to_console:
211
+ handler = StreamHandler(sys.stdout)
212
+ handler.setLevel(logging.DEBUG)
213
+ handler.setFormatter(self.log_formatter())
214
+ self.logger.addHandler(handler)
215
+ return True
216
+ return False
217
+
218
+ def add_handler_file(self, path_log_file=None):
219
+ if self.logger and self.config.log_to_file:
220
+ if path_log_file is None:
221
+ path_log_file = temp_file(extension='.log')
222
+ handler = FileHandler(path_log_file)
223
+ handler.setLevel(self.log_level())
224
+ handler.setFormatter(self.log_formatter())
225
+ self.logger.addHandler(handler)
226
+ return True
227
+ return False
228
+
229
+ def add_handler_memory(self, memory_capacity=None):
230
+ if self.log_handler_memory() is None:
231
+ if self.logger and self.config.log_to_memory:
232
+ capacity = memory_capacity or MEMORY_LOGGER_CAPACITY
233
+ flush_level = MEMORY_LOGGER_FLUSH_LEVEL
234
+ target = None # we want the messages to only be kept in memory
235
+ memory_handler = MemoryHandler(capacity=capacity, flushLevel=flush_level, target=target,flushOnClose=True)
236
+ memory_handler.setLevel(self.log_level())
237
+ self.logger.addHandler(memory_handler)
238
+ return True
239
+ return False
240
+
241
+ # Utils
242
+ def memory_handler(self) -> MemoryHandler:
243
+ return self.log_handler_memory()
244
+
245
+ def memory_handler_buffer(self):
246
+ if self.config.log_to_memory:
247
+ return self.memory_handler().buffer
248
+ return []
249
+
250
+ def memory_handler_clear(self):
251
+ if self.config.log_to_memory:
252
+ memory_handler = self.memory_handler()
253
+ memory_handler.buffer = []
254
+ return True
255
+ return False
256
+ def memory_handler_exceptions(self):
257
+ return self.memory_handler_logs(index_by='levelname').get('EXCEPTIONS', {})
258
+
259
+ @index_by
260
+ @group_by
261
+ def memory_handler_logs(self):
262
+ logs = []
263
+ for log_record in self.memory_handler_buffer():
264
+ logs.append(obj_dict(log_record))
265
+ return logs
266
+
267
+ def memory_handler_last_log_entry(self):
268
+ memory_buffer = self.memory_handler_buffer()
269
+ if memory_buffer: # Check if the buffer is not empty
270
+ last_log_record = memory_buffer[-1] # get the last record
271
+ return obj_dict(last_log_record) # convert record into a nice json object
272
+ return {}
273
+
274
+ def memory_handler_messages(self):
275
+ return [log_entry.get('message') for log_entry in self.memory_handler_logs()]
276
+
277
+ # Logging methods
278
+
279
+ # def debug (self, msg='', *args, **kwargs): return self._log('debug' , msg, *args, **kwargs)
280
+ # #def info (self, msg='', *args, **kwargs): return self.__log__('info' , msg, *args, **kwargs)
281
+ # def warning (self, msg='', *args, **kwargs): return self._log('warning' , msg, *args, **kwargs)
282
+ # def error (self, msg='', *args, **kwargs): return self._log('error' , msg, *args, **kwargs)
283
+ # def exception(self, msg='', *args, **kwargs): return self._log('exception' , msg, *args, **kwargs)
284
+ # def critical (self, msg='', *args, **kwargs): return self._log('critical' , msg, *args, **kwargs)
285
+ #
286
+ # def __log__(self, level, msg, *args, **kwargs):
287
+ # if self.logger:
288
+ # log_method = getattr(self.logger, level)
289
+ # log_method(msg, *args, **kwargs)
290
+ # return True
291
+ # return False
292
+
293
+ @cache_on_function
294
+ def logger_info():
295
+ python_logger = Python_Logger().setup()
296
+ return python_logger.logger.info
297
+
298
+ @cache_on_function
299
+ def logger_error():
300
+ python_logger = Python_Logger().setup()
301
+ return python_logger.logger.error
@@ -0,0 +1,79 @@
1
+ # todo refactor into Status class
2
+ import traceback
3
+
4
+ from osbot_utils.utils.Python_Logger import Python_Logger
5
+
6
+ class Status:
7
+ def __init__(self):
8
+ self.logger = Python_Logger().setup()
9
+ self.call_logger_method = False
10
+ self.root_logger_handlers = None
11
+ #self.logger.add_memory_logger()
12
+
13
+ def root_logger(self):
14
+ return self.logger.logger.parent
15
+
16
+ def clear_root_logger_handlers(self):
17
+ self.root_logger_handlers = self.root_logger().handlers
18
+ self.root_logger().handlers = []
19
+
20
+ def restore_root_logger_handlers(self):
21
+ if self.root_logger_handlers:
22
+ self.root_logger().handlers = self.root_logger_handlers
23
+ def status_message(self, status, message:str=None, data=None, error=None):
24
+ return { 'data' : data ,
25
+ 'error' : error ,
26
+ 'message': message ,
27
+ 'status' : status
28
+ }
29
+
30
+ def last_message(self):
31
+ return self.logger.memory_handler_last_log_entry()
32
+
33
+ def log_message(self, status, message:str='', data=None, error=None, stacklevel=3): # stacklevel is usually 3 because we want to get the caller of the method that called this on
34
+ logger_message = f'[osbot] [{status}] ' + str(message)
35
+ logger_method = self.logger.__getattribute__(status)
36
+ status_message = self.status_message(status=status, message=message, data=data, error=error)
37
+ if self.call_logger_method:
38
+ kwargs = {} # todo: add option to capture stack trace and other helpful debug data
39
+ if status =='exception':
40
+ kwargs = dict(exc_info=True, stacklevel=stacklevel)
41
+ logger_method(logger_message, **kwargs)
42
+ return status_message
43
+
44
+
45
+
46
+
47
+ osbot_status = Status() # todo map out the performatin implications of doing this
48
+ osbot_logger = osbot_status.logger
49
+
50
+ def status_critical (message:str='', data=None,error=None): return osbot_status.log_message(status='critical' , message=message, data=data, error=error)
51
+ def status_debug (message:str='', data=None,error=None): return osbot_status.log_message(status='debug' , message=message, data=data, error=error)
52
+ def status_error (message:str='', data=None,error=None): return osbot_status.log_message(status='error' , message=message, data=data, error=error)
53
+ def status_exception(message:str='', data=None,error=None): return osbot_status.log_message(status='exception', message=message, data=data, error=error)
54
+ def status_info (message:str='', data=None,error=None): return osbot_status.log_message(status='info' , message=message, data=data, error=error)
55
+ def status_ok (message:str='', data=None,error=None): return osbot_status.log_message(status='ok' , message=message, data=data, error=error)
56
+ def status_warning (message:str='', data=None,error=None): return osbot_status.log_message(status='warning' , message=message, data=data, error=error)
57
+
58
+ log_critical = status_critical # level 50
59
+ log_error = status_error # level 40
60
+ log_exception = status_exception # level 40
61
+ log_warning = status_warning # level 30
62
+ log_info = status_info # level 20
63
+ log_ok = status_ok # level 20
64
+ log_debug = status_debug # level 10
65
+
66
+
67
+ def send_status_to_logger(value: bool = True):
68
+ osbot_status.call_logger_method = value
69
+
70
+ #def log_error (message):#logger().error (message) # level 40
71
+ #def log_info (message): logger().info (message) # level 20
72
+ #def log_warning (message): logger().warning (message) # level 30
73
+
74
+
75
+ # def status_info (message:str='', data=None,error=None): osbot_logger.info ('[osbot] [info] ' + str(message)); return status_message('info', message=message, data=data, error=error)
76
+ # def status_ok (message:str='', data=None,error=None): osbot_logger.info ('[osbot] [ok] ' + str(message)); return status_message('ok', message=message, data=data, error=error)
77
+ # def status_warning(message:str='', data=None,error=None): osbot_logger.warning('[osbot] [warning] ' + str(message)); return status_message('warning', message=message, data=data, error=error)
78
+
79
+ #todo: add status_exception that automatically picks up the exception from the stack trace
@@ -0,0 +1,63 @@
1
+ import textwrap
2
+ from html import escape, unescape
3
+
4
+ from osbot_utils.utils.Files import safe_file_name
5
+
6
+
7
+ def html_escape(value):
8
+ return escape(value)
9
+
10
+ def html_unescape(value):
11
+ return unescape(value)
12
+
13
+ def str_dedent(value, strip=True):
14
+ result = textwrap.dedent(value)
15
+ if strip:
16
+ result = result.strip()
17
+ return result
18
+
19
+ def str_index(target:str, source:str):
20
+ try:
21
+ return target.index(source)
22
+ except:
23
+ return -1
24
+
25
+ def str_join(delimiter, values):
26
+ return delimiter.join(values)
27
+
28
+ def str_max_width(target, value):
29
+ return str(target)[:value]
30
+
31
+ def str_safe(value):
32
+ return safe_file_name(value)
33
+
34
+ def str_starts_with(source, prefix):
35
+ if source is None or prefix is None:
36
+ return False
37
+ else:
38
+ return source.startswith(prefix)
39
+
40
+ def str_unicode_escape(target):
41
+ return str(target).encode('unicode_escape').decode("utf-8")
42
+
43
+ def str_cap_snake_case(snake_str):
44
+ """
45
+ Converts a snake_case string to Capitalized_Snake_Case.
46
+
47
+ Args:
48
+ snake_str (str): The snake_case string to be converted.
49
+
50
+ Returns:
51
+ str: The converted string in Capitalized_Snake_Case.
52
+ """
53
+ return "_".join(word.capitalize() for word in snake_str.split("_"))
54
+
55
+
56
+ def trim(target):
57
+ if type(target) is str:
58
+ return target.strip()
59
+ return ""
60
+
61
+ html_encode = html_escape
62
+ html_decode = html_unescape
63
+ safe_str = str_safe
@@ -0,0 +1,16 @@
1
+ import osbot_utils
2
+ from osbot_utils.utils.Files import file_contents, path_combine
3
+
4
+ class Version:
5
+
6
+ FILE_NAME_VERSION = 'version'
7
+
8
+ def path_code_root(self):
9
+ return osbot_utils.path
10
+
11
+ def path_version_file(self):
12
+ return path_combine(self.path_code_root(), self.FILE_NAME_VERSION)
13
+
14
+ def value(self):
15
+ version = file_contents(self.path_version_file()) or ""
16
+ return version.strip()
@@ -0,0 +1,97 @@
1
+ import io
2
+ import os
3
+ import shutil
4
+ import zipfile
5
+ from os.path import abspath
6
+
7
+ from osbot_utils.utils.Files import temp_folder, folder_files, temp_file, is_file
8
+
9
+
10
+ def unzip_file(zip_file, target_folder=None, format='zip'):
11
+ target_folder = target_folder or temp_folder()
12
+ shutil.unpack_archive(zip_file, extract_dir=target_folder, format=format)
13
+ return target_folder
14
+
15
+ def zip_bytes_add_file(zip_bytes, zip_file_path, file_contents):
16
+ if type(file_contents) is str:
17
+ file_contents = file_contents.encode('utf-8')
18
+ elif type(file_contents) is not bytes:
19
+ return None
20
+ zip_buffer = io.BytesIO(zip_bytes)
21
+ with zipfile.ZipFile(zip_buffer, 'a', zipfile.ZIP_DEFLATED) as zf:
22
+ zf.writestr(zip_file_path, file_contents)
23
+
24
+ return zip_buffer.getvalue()
25
+
26
+ def zip_bytes_get_file(zip_bytes, zip_file_path):
27
+ zip_buffer = io.BytesIO(zip_bytes)
28
+ with zipfile.ZipFile(zip_buffer, 'r') as zf:
29
+ return zf.read(zip_file_path)
30
+
31
+ def zip_bytes_file_list(zip_bytes):
32
+ zip_buffer_from_bytes = io.BytesIO(zip_bytes)
33
+ with zipfile.ZipFile(zip_buffer_from_bytes, 'r') as zf:
34
+ return sorted(zf.namelist())
35
+
36
+ def zip_bytes_to_file(zip_bytes, target_file=None):
37
+ if target_file is None:
38
+ target_file = temp_file(extension='.zip')
39
+ with open(target_file, 'wb') as f:
40
+ f.write(zip_bytes)
41
+ return target_file
42
+
43
+ def zip_files_to_bytes(target_files, root_folder=None):
44
+ zip_buffer = io.BytesIO() # Create a BytesIO buffer to hold the zipped file
45
+ with zipfile.ZipFile(zip_buffer, 'w', zipfile.ZIP_DEFLATED) as zf: # Create a ZipFile object with the buffer as the target
46
+ for entry in target_files:
47
+ if type(entry) is str: # if entry is a string, assume it's a file path
48
+ file_path = entry
49
+ file_root_folder = root_folder
50
+ else:
51
+ file_path = entry.get('file')
52
+ file_root_folder = entry.get('root_folder') or root_folder
53
+ if file_root_folder:
54
+ arcname = file_path.replace(file_root_folder,'') # Define the arcname, which is the name inside the zip file
55
+ else:
56
+ arcname = file_path # if root_path is not provided, use the full file path
57
+ zf.write(file_path, arcname) # Add the file to the zip file
58
+ zip_buffer.seek(0)
59
+ return zip_buffer
60
+
61
+ def zip_folder(root_dir, format='zip'):
62
+ return shutil.make_archive(base_name=root_dir, format=format, root_dir=root_dir)
63
+
64
+ def zip_folder_to_bytes(root_dir): # todo add unit test
65
+ zip_buffer = io.BytesIO() # Create a BytesIO buffer to hold the zipped file
66
+ with zipfile.ZipFile(zip_buffer, 'w', zipfile.ZIP_DEFLATED) as zf: # Create a ZipFile object with the buffer as the target
67
+ for foldername, subfolders, filenames in os.walk(root_dir): # Walk the root_dir and add all files and folders to the zip file
68
+ for filename in filenames:
69
+ absolute_path = os.path.join(foldername, filename) # Create the complete filepath
70
+ arcname = os.path.relpath(absolute_path, root_dir) # Define the arcname, which is the name inside the zip file
71
+ zf.write(absolute_path, arcname) # Add the file to the zip file
72
+ zip_buffer.seek(0) # Reset buffer position
73
+ return zip_buffer
74
+
75
+ def zip_file_list(path):
76
+ if is_file(path):
77
+ with zipfile.ZipFile(path) as zip_file:
78
+ return sorted(zip_file.namelist())
79
+ return []
80
+
81
+ def zip_files(base_folder, file_pattern="*.*", target_file=None):
82
+ base_folder = abspath(base_folder)
83
+ file_list = folder_files(base_folder, file_pattern)
84
+
85
+ if len(file_list): # if there were files found
86
+ target_file = target_file or temp_file(extension='zip')
87
+ with zipfile.ZipFile(target_file,'w') as zip:
88
+ for file_name in file_list:
89
+ zip_file_path = file_name.replace(base_folder,'')
90
+ zip.write(file_name, zip_file_path)
91
+
92
+ return target_file
93
+
94
+
95
+ # extra function's mappings
96
+ file_unzip = unzip_file
97
+ folder_zip = zip_folder
@@ -0,0 +1,16 @@
1
+ """
2
+ In this class you will find the following helper classes:
3
+
4
+ - Assert
5
+ - Dev
6
+ - Files
7
+ - Http
8
+ - Json
9
+ - Lists
10
+ - Misc
11
+ - Png
12
+ - Process
13
+ - Temp_File
14
+ - Unzip_File
15
+ - Zip_Folder
16
+ """
osbot_utils/version ADDED
@@ -0,0 +1 @@
1
+ v1.7.7