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,20 @@
1
+ from osbot_utils.base_classes.Kwargs_To_Self import Kwargs_To_Self
2
+
3
+
4
+ class Dict_To_Css(Kwargs_To_Self):
5
+ css: dict
6
+
7
+
8
+ def add_css_entry(self, selector, data):
9
+ self.css[selector] = data
10
+ return self
11
+
12
+ def convert(self, indent=''):
13
+ css_lines = [] # List to hold each line of CSS
14
+ for selector, properties in self.css.items():
15
+ css_line = f"{indent}{selector} {{" # Start the selector line
16
+ for prop, value in properties.items(): # Add each property and value to the selector's CSS
17
+ css_line += f"\n{indent} {prop}: {value};"
18
+ css_line += '\n' + indent + "}" # Close the selector's CSS block
19
+ css_lines.append(css_line) # Add the completed selector's CSS to the list
20
+ return "\n".join(css_lines) # Join all selector CSS blocks with a newline and return
@@ -0,0 +1,59 @@
1
+
2
+ HTML_SELF_CLOSING_TAGS = {'area', 'base', 'br', 'col', 'command', 'embed', 'hr', 'img', 'input', 'link', 'meta', 'param', 'source', 'track', 'wbr'}
3
+
4
+ class Dict_To_Html:
5
+ def __init__(self, root):
6
+ # Define a list of self-closing tags
7
+ self.self_closing_tags =HTML_SELF_CLOSING_TAGS # {'area', 'base', 'br', 'col', 'command', 'embed', 'hr', 'img', 'input', 'link', 'meta', 'param', 'source', 'track', 'wbr'}
8
+ self.root = root
9
+
10
+ def convert(self):
11
+ return self.convert_element(self.root, 0) # Start conversion with the root element and initial indentation level 0
12
+
13
+ def convert_attrs(self, attrs):
14
+ attrs_str_parts = [] # List to hold each attribute's string representation
15
+ for key, value in attrs.items():
16
+ if '"' in value: # Check if the attribute value contains double quotes
17
+ escaped_value = """.join(value.split("\"")) # If so, escape double quotes and format the attribute string
18
+ attr_str = f'{key}="{escaped_value}"'
19
+ else:
20
+ attr_str = f'{key}="{value}"' # If not, simply format the attribute string
21
+ attrs_str_parts.append(attr_str)
22
+
23
+ attrs_str = ' '.join(attrs_str_parts) # Join the parts into the final attributes string
24
+
25
+ if attrs_str:
26
+ attrs_str = " " + attrs_str # Prepend a space if there are attributes
27
+ return attrs_str
28
+
29
+ def convert_element(self, element, indent_level):
30
+ """Recursively converts a dictionary to an HTML string with indentation."""
31
+ tag = element.get("tag")
32
+ attrs = element.get("attrs", {})
33
+ children = element.get("children", [])
34
+ data = element.get("data", "")
35
+
36
+ attrs_str = self.convert_attrs(attrs) # Convert attributes dictionary to a string
37
+ indent = " " * indent_level # Indentation for the current level, assuming 4 spaces per indent level
38
+
39
+ if tag in self.self_closing_tags: # Check if the tag is self-closing
40
+ return f"{indent}<{tag}{attrs_str} />\n"
41
+
42
+ html = f"{indent}<{tag}{attrs_str}>" # Opening tag with indentation
43
+ if not data:
44
+ html += '\n'
45
+
46
+ for child in children: # Process children with incremented indent level
47
+ html += self.convert_element(child, indent_level + 1)
48
+
49
+ if data: # Add data if present
50
+ html += data
51
+
52
+ if children: # Closing tag for non-self-closing tags, with indentation
53
+ html += f"{indent}</{tag}>\n" # Add closing tag on a new line if there are children
54
+ elif data:
55
+ html += f"</{tag}>\n"
56
+ else: # Place closing tag directly after opening tag if there are no children or data
57
+ html = f"{indent}<{tag}{attrs_str}></{tag}>\n"
58
+
59
+ return html
@@ -0,0 +1,88 @@
1
+ from osbot_utils.helpers.html.Dict_To_Html import HTML_SELF_CLOSING_TAGS
2
+ from osbot_utils.helpers.html.Tag__Base import Tag__Base
3
+ from osbot_utils.helpers.html.Tag__Body import Tag__Body
4
+ from osbot_utils.helpers.html.Tag__Head import Tag__Head
5
+ from osbot_utils.helpers.html.Tag__Html import Tag__Html
6
+ from osbot_utils.helpers.html.Tag__Link import Tag__Link
7
+ from osbot_utils.utils.Dev import pprint
8
+
9
+
10
+ class Dict_To_Tags:
11
+
12
+ def __init__(self, root):
13
+ self.root = root
14
+
15
+ def convert(self):
16
+ return self.convert_element(self.root)
17
+
18
+ def convert_element(self, element):
19
+ tag_name = element.get("tag" )
20
+ attrs = element.get("attrs" , {} )
21
+ children = element.get("children" , [] )
22
+ data = element.get("data" , "" )
23
+ if tag_name == 'html':
24
+ return self.convert_to__tag__html(element)
25
+ if tag_name == 'head':
26
+ return self.convert_to__tag__head(element)
27
+ if tag_name == 'link':
28
+ return self.convert_to__tag__link(element)
29
+
30
+ def convert_to__tag(self, target_tag, element, indent):
31
+ tag_name = element.get("tag" )
32
+ attrs = element.get("attrs" , {} )
33
+ children = element.get("children" , [] )
34
+ data = element.get("data" , '' )
35
+ end_tag = tag_name not in HTML_SELF_CLOSING_TAGS
36
+ tag_indent = indent + 1
37
+ tag_kwargs = dict(tag_name = tag_name ,
38
+ attributes = attrs ,
39
+ end_tag = end_tag ,
40
+ indent = tag_indent ,
41
+ inner_html = data )
42
+ tag = target_tag(**tag_kwargs)
43
+ for child in children:
44
+ child_tag = self.convert_to__tag(Tag__Base, child, tag_indent)
45
+ tag.elements.append(child_tag)
46
+
47
+ return tag
48
+
49
+ def convert_to__tag__head(self, element, indent):
50
+ attrs = element.get("attrs" , {} )
51
+ children = element.get("children" , [] )
52
+ print()
53
+ head_indent = indent+1
54
+ tag_head = Tag__Head(indent=head_indent, **attrs)
55
+ for child in children:
56
+ tag_name = child.get("tag" )
57
+ data = child.get("data", "")
58
+ if tag_name == 'title':
59
+ tag_head.title = data
60
+ elif tag_name == 'link':
61
+ tag_head.links.append(self.convert_to__tag__link(child))
62
+ elif tag_name == 'meta':
63
+ tag_head.elements.append((self.convert_to__tag(Tag__Base, child, indent=head_indent)))
64
+ elif tag_name == 'style':
65
+ tag_head.elements.append((self.convert_to__tag(Tag__Base, child, indent=head_indent))) # todo: add proper roundtrip of css data into Tag__Style (which already exists)
66
+ else:
67
+ print(f'[convert_to__tag__head] Unknown tag: {tag_name}')
68
+ #pprint(tag_head.__locals__())
69
+ return tag_head
70
+
71
+ def convert_to__tag__html(self, element):
72
+ attrs = element.get("attrs" , {} )
73
+ children = element.get("children" , [] )
74
+ tag_html = Tag__Html(**attrs)
75
+ for child in children:
76
+ tag_name = child.get("tag" )
77
+ if tag_name == 'head':
78
+ tag_html.head = self.convert_to__tag__head(child, tag_html.indent)
79
+ elif tag_name == 'body':
80
+ tag_html.body = self.convert_to__tag(Tag__Body, child, tag_html.indent)
81
+ else:
82
+ print(f'unknown tag: {tag_name}')
83
+ return tag_html
84
+
85
+ def convert_to__tag__link(self, element):
86
+ attrs = element.get("attrs", {})
87
+ tag_link = Tag__Link(**attrs)
88
+ return tag_link
@@ -0,0 +1,75 @@
1
+ from html.parser import HTMLParser
2
+
3
+ class Html_To_Dict(HTMLParser):
4
+ def __init__(self, html):
5
+ super().__init__()
6
+ self.root = None # No root initially
7
+ self.current = None # No current node at the start
8
+ self.stack = [] # Empty stack for hierarchy management
9
+ self.html = html or ''
10
+
11
+ def convert(self):
12
+ self.feed(self.html)
13
+ return self.root
14
+
15
+ def handle_starttag(self, tag, attrs):
16
+ new_tag = {"tag": tag, "attrs": dict(attrs), "children": [], "data": ""}
17
+
18
+ if self.current is None:
19
+ # When the first tag is encountered, it becomes the root
20
+ self.root = new_tag
21
+ self.current = new_tag
22
+ else:
23
+ # Otherwise, append the new tag as a child of the current tag
24
+ self.current["children"].append(new_tag)
25
+
26
+ # Update the stack and current pointers
27
+ self.stack.append(new_tag)
28
+ self.current = new_tag
29
+
30
+ def handle_endtag(self, tag):
31
+ if len(self.stack) > 1:
32
+ self.stack.pop()
33
+ self.current = self.stack[-1]
34
+
35
+ def handle_data(self, data):
36
+ if data.strip(): # Ignore whitespace
37
+ if "data" in self.current:
38
+ self.current["data"] += data
39
+ else:
40
+ self.current["data"] = data
41
+
42
+ def print__generate_lines(self, node, indent="", last=True, is_root=True):
43
+ lines = []
44
+
45
+ prefix = "" if is_root else ("└── " if last else "├── ")
46
+
47
+ tag = node.get("tag")
48
+ attrs = node.get("attrs", {})
49
+ children = node.get("children", [])
50
+ attrs_str = ' '.join(f'{key}="{value}"' for key, value in attrs.items())
51
+ attrs_str = f' ({attrs_str})' if attrs_str else ''
52
+
53
+ lines.append(f"{indent}{prefix}{tag}{attrs_str}")
54
+
55
+ child_indent = indent + (" " if last else "│ ")
56
+
57
+ for i, child in enumerate(children):
58
+ is_last = i == len(children) - 1
59
+ child_lines = self.print__generate_lines(child, indent=child_indent, last=is_last, is_root=False)
60
+ lines.extend(child_lines if isinstance(child_lines, list) else [child_lines])
61
+
62
+ return lines if is_root else "\n".join(lines)
63
+
64
+ def print(self, just_return_lines=False):
65
+ if self.root:
66
+ lines = self.print__generate_lines(self.root, is_root=True)
67
+ if just_return_lines:
68
+ return lines
69
+ else:
70
+ self.print__lines(lines)
71
+ return self
72
+
73
+ def print__lines(self, lines):
74
+ for line in lines:
75
+ print(line)
@@ -0,0 +1,20 @@
1
+ from osbot_utils.helpers.html.Dict_To_Tags import Dict_To_Tags
2
+
3
+ from osbot_utils.helpers.html.Html_To_Dict import Html_To_Dict
4
+
5
+
6
+ class Html_To_Tag:
7
+
8
+ def __init__(self,html):
9
+ self.html_to_dict = Html_To_Dict(html)
10
+
11
+ def __enter__(self):
12
+ return self.convert()
13
+
14
+ def __exit__(self, exc_type, exc_val, exc_tb):
15
+ return
16
+
17
+ def convert(self):
18
+ html_dict = self.html_to_dict.convert()
19
+ html_tag = Dict_To_Tags(html_dict).convert()
20
+ return html_tag
@@ -0,0 +1,91 @@
1
+ from collections import defaultdict
2
+
3
+ from osbot_utils.base_classes.Kwargs_To_Self import Kwargs_To_Self
4
+ from osbot_utils.utils.Files import file_create
5
+
6
+ INDENT_SIZE = 4
7
+
8
+ class Tag__Base(Kwargs_To_Self):
9
+ attributes : dict
10
+ elements : list
11
+ end_tag : bool = True
12
+ indent : int
13
+ tag_name : str
14
+ tag_classes : list
15
+ inner_html : str
16
+ new_line_before_elements : bool = True
17
+
18
+ def __init__(self, **kwargs):
19
+ super().__init__(**kwargs)
20
+ #self.locked() # lock the object so that it is not possible to add new attributes via normal assigment
21
+
22
+ def append(self, *elements):
23
+ self.elements.extend(elements)
24
+ return self
25
+
26
+ def attributes_values(self, *attributes_names):
27
+ attributes = {}
28
+ for attribute_name in attributes_names:
29
+ if hasattr(self, attribute_name):
30
+ attribute_value = getattr(self, attribute_name)
31
+ if attribute_value:
32
+ attributes[attribute_name] = attribute_value
33
+ return attributes
34
+
35
+ def elements__by_tag_name(self):
36
+ result = defaultdict(list)
37
+ for element in self.elements:
38
+ result[element.tag_name].append(element)
39
+ return dict(result)
40
+
41
+ def elements__with_tag_name(self, tag_name):
42
+ return self.elements__by_tag_name().get(tag_name)
43
+
44
+ def save(self, file_path):
45
+ return file_create(file_path, self.render())
46
+
47
+ def render_attributes(self):
48
+ attributes = self.attributes.copy()
49
+ if self.tag_classes:
50
+ attributes['class'] = ' '.join(self.tag_classes)
51
+
52
+ html_attributes = ' '.join([f'{key}="{value}"' for key, value in attributes.items()])
53
+ return html_attributes
54
+
55
+ def render_element(self):
56
+ html_attributes = self.render_attributes()
57
+ html_elements = self.render_elements()
58
+ element_indent = " " * self.indent * INDENT_SIZE
59
+
60
+ html = f"{element_indent}<{self.tag_name}"
61
+ if html_attributes:
62
+ html += f" {html_attributes}"
63
+ if self.end_tag:
64
+ html += ">"
65
+ if self.inner_html:
66
+ html += self.inner_html
67
+ if html_elements:
68
+ if self.new_line_before_elements:
69
+ html += "\n"
70
+ html += f"{html_elements}"
71
+ if self.new_line_before_elements:
72
+ html += "\n"
73
+ html += element_indent
74
+ html += f"</{self.tag_name}>"
75
+ else:
76
+ html += "/>"
77
+
78
+ return html
79
+
80
+ def render_elements(self):
81
+ html_elements = ""
82
+ for index, element in enumerate(self.elements):
83
+ if index:
84
+ html_elements += '\n'
85
+ element.indent = self.indent + 1 # set the indent of the child element based on the current one
86
+ html_element = element.render()
87
+ html_elements += html_element
88
+ return html_elements
89
+
90
+ def render(self):
91
+ return self.render_element()
@@ -0,0 +1,5 @@
1
+ from osbot_utils.helpers.html.Tag__Base import Tag__Base
2
+
3
+
4
+ class Tag__Body(Tag__Base):
5
+ tag_name = 'body'
@@ -0,0 +1,5 @@
1
+ from osbot_utils.helpers.html.Tag__Base import Tag__Base
2
+
3
+
4
+ class Tag__Div(Tag__Base):
5
+ tag_name = 'div'
@@ -0,0 +1,9 @@
1
+ from osbot_utils.helpers.html.Tag__Base import Tag__Base
2
+
3
+
4
+ class Tag__H(Tag__Base):
5
+ def __init__(self, size, value):
6
+ tag_name = f'h{size}'
7
+ inner_html = value
8
+ init__kwargs = dict(tag_name=tag_name, inner_html=inner_html)
9
+ super().__init__(**init__kwargs)
@@ -0,0 +1,5 @@
1
+ from osbot_utils.helpers.html.Tag__Base import Tag__Base
2
+
3
+
4
+ class Tag__HR(Tag__Base):
5
+ tag_name = 'hr'
@@ -0,0 +1,32 @@
1
+ from osbot_utils.helpers.html.Tag__Base import Tag__Base
2
+ from osbot_utils.helpers.html.Tag__Link import Tag__Link
3
+ from osbot_utils.helpers.html.Tag__Style import Tag__Style
4
+ from osbot_utils.utils.Dev import pprint
5
+
6
+
7
+ class Tag__Head(Tag__Base):
8
+ title : str
9
+ links : list[Tag__Link]
10
+ meta : list[Tag__Base]
11
+ style : Tag__Style
12
+
13
+ def __init__(self, **kwargs):
14
+ super().__init__(**kwargs)
15
+ self.tag_name = 'head'
16
+
17
+ def add_css_bootstrap(self):
18
+ link = Tag__Link(href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css", rel="stylesheet")
19
+ self.links.append(link)
20
+ return self
21
+
22
+ def render(self):
23
+ if self.title:
24
+ title_element = Tag__Base(tag_name='title', inner_html=self.title, indent=self.indent + 1)
25
+ self.elements.append(title_element)
26
+ for link in self.links:
27
+ link.indent = self.indent + 1
28
+ self.elements.append(link)
29
+ if self.style.css():
30
+ self.elements.append(self.style)
31
+ html = self.render_element()
32
+ return html
@@ -0,0 +1,42 @@
1
+ from osbot_utils.helpers.html.Tag__Base import Tag__Base
2
+ from osbot_utils.helpers.html.Tag__Body import Tag__Body
3
+ from osbot_utils.helpers.html.Tag__Head import Tag__Head
4
+ from osbot_utils.utils.Misc import str_to_bytes
5
+
6
+ ATTRIBUTES_NAMES__LINK = ['lang']
7
+
8
+
9
+ class Tag__Html(Tag__Base):
10
+ doc_type: bool = True
11
+ body : Tag__Body
12
+ head : Tag__Head
13
+ lang : str
14
+
15
+ def __init__(self, **kwargs):
16
+ super().__init__(**kwargs)
17
+ self.head.indent = self.indent + 1
18
+ self.body.indent = self.indent + 1
19
+ self.tag_name = 'html'
20
+
21
+ def render(self):
22
+ self.elements = [self.head, self.body]
23
+ self.attributes = self.attributes_values(*ATTRIBUTES_NAMES__LINK)
24
+ if self.doc_type:
25
+ html = "<!DOCTYPE html>\n"
26
+ else:
27
+ html = ""
28
+ attributes = {}
29
+ if self.lang:
30
+ attributes['lang'] = self.lang
31
+
32
+ html += self.render_element()
33
+ return html
34
+
35
+ def render_to_bytes(self):
36
+ return str_to_bytes(self.render())
37
+
38
+ def set_inner_html(self, value):
39
+ self.body.inner_html = value
40
+ return self
41
+
42
+
@@ -0,0 +1,17 @@
1
+ from osbot_utils.helpers.html.Tag__Base import Tag__Base
2
+
3
+ ATTRIBUTES_NAMES__LINK = ['href', 'rel', 'integrity', 'crossorigin']
4
+
5
+ class Tag__Link(Tag__Base):
6
+ end_tag : bool = False
7
+ tag_name : str = 'link'
8
+ crossorigin: str
9
+ href : str
10
+ rel : str
11
+ integrity : str
12
+
13
+
14
+ def render(self):
15
+ self.attributes = self.attributes_values(*ATTRIBUTES_NAMES__LINK)
16
+
17
+ return self.render_element()
@@ -0,0 +1,25 @@
1
+ from osbot_utils.helpers.html.Dict_To_Css import Dict_To_Css
2
+ from osbot_utils.helpers.html.Tag__Base import Tag__Base, INDENT_SIZE
3
+
4
+
5
+ class Tag__Style(Tag__Base):
6
+ tag_name : str = 'style'
7
+ dict_to_css : Dict_To_Css
8
+
9
+ def add_css_entry(self, selector, data):
10
+ self.dict_to_css.add_css_entry(selector,data)
11
+ return self
12
+
13
+ def css(self):
14
+ return self.dict_to_css.css
15
+
16
+ def render(self):
17
+ if self.dict_to_css.css:
18
+ css_indent = self.indent +1
19
+ css_indent_text = " " * css_indent * INDENT_SIZE
20
+ element_indent_text = " " * self.indent * INDENT_SIZE
21
+ self.inner_html = '\n' + self.dict_to_css.convert(indent=css_indent_text) + '\n' + element_indent_text
22
+ return super().render()
23
+
24
+ def set_css(self, css):
25
+ self.dict_to_css.css = css
File without changes
@@ -0,0 +1,95 @@
1
+ import time
2
+ from enum import Enum
3
+ from queue import Queue, Empty
4
+ from threading import Thread
5
+ from typing import Any
6
+
7
+ from osbot_utils.helpers.pubsub.schemas.Schema__Event import Schema__Event
8
+ from osbot_utils.helpers.pubsub.schemas.Schema__Event__Message import Schema__Event__Message
9
+ from osbot_utils.utils import Misc
10
+ from osbot_utils.base_classes.Kwargs_To_Self import Kwargs_To_Self
11
+ from osbot_utils.utils.Misc import random_text, wait_for, timestamp_utc_now, random_guid
12
+
13
+ QUEUE_WAIT_TIMEOUT = 1.0 # todo: see if this value is a good one to use here
14
+
15
+ class Event__Queue(Kwargs_To_Self):
16
+ events : list
17
+ event_class : type
18
+ log_events : bool = False
19
+ queue : Queue
20
+ queue_name : str = random_text('event_queue')
21
+ queue_timeout: float = QUEUE_WAIT_TIMEOUT
22
+ running : bool
23
+ thread : Thread = None
24
+
25
+
26
+ def __init__(self, **kwargs):
27
+ self.event_class = Schema__Event
28
+ super().__init__(**kwargs)
29
+
30
+ def __enter__(self):
31
+ self.start()
32
+ return self
33
+
34
+ def __exit__(self, exc_type, exc_val, exc_tb):
35
+ self.stop()
36
+ return False
37
+
38
+ def new_event_obj(self, **kwargs):
39
+ return self.event_class(**kwargs)
40
+
41
+ def handle_event(self, event):
42
+ if self.log_events:
43
+ self.events.append(event)
44
+ return True
45
+
46
+ def send_event(self, event: Schema__Event):
47
+ if isinstance(event, Schema__Event):
48
+ if not event.timestamp:
49
+ event.timestamp = timestamp_utc_now()
50
+ if not event.event_id:
51
+ event.event_id = random_guid()
52
+ self.queue.put(event)
53
+ return True
54
+ return False
55
+
56
+ def send_data(self, event_data, **kwargs):
57
+ if type(event_data) is not dict:
58
+ event_data = {'data': event_data}
59
+ new_event = Schema__Event__Message(event_data=event_data, **kwargs)
60
+ if self.send_event(new_event):
61
+ return new_event
62
+
63
+ def send_message(self, message, **kwargs):
64
+ new_event = Schema__Event__Message(event_message=str(message), **kwargs)
65
+ if self.send_event(new_event):
66
+ return new_event
67
+
68
+ def start(self):
69
+ self.running = True
70
+ self.thread = Thread(target=self.run_thread, daemon=True)
71
+ self.thread.start()
72
+ return self
73
+
74
+ def stop(self):
75
+ self.running = False
76
+ return self
77
+
78
+ def run_thread(self):
79
+ while self.running:
80
+ try:
81
+ event = self.queue.get(timeout=self.queue_timeout)
82
+ if isinstance(event, self.event_class):
83
+ self.handle_event(event)
84
+ except Empty:
85
+ continue
86
+ except Exception as e: # todo: add way to handle this (which are errors in the handle_event), may call an on_event_handler_exceptions method
87
+ continue
88
+
89
+ def wait_micro_seconds(self, value=10):
90
+ time.sleep(0.000001 * value)
91
+
92
+
93
+ def wait_for_thread_ends(self):
94
+ self.thread.join()
95
+ return self
@@ -0,0 +1,53 @@
1
+ from queue import Queue
2
+
3
+ from osbot_utils.base_classes.Kwargs_To_Self import Kwargs_To_Self
4
+ from osbot_utils.helpers.pubsub.Event__Queue import Event__Queue
5
+ from osbot_utils.helpers.pubsub.schemas.Schema__Event import Schema__Event
6
+ from osbot_utils.helpers.pubsub.schemas.Schema__Event__Connect import Schema__Event__Connect
7
+ from osbot_utils.helpers.pubsub.schemas.Schema__Event__Disconnect import Schema__Event__Disconnect
8
+ from osbot_utils.helpers.pubsub.schemas.Schema__Event__Join_Room import Schema__Event__Join_Room
9
+ from osbot_utils.helpers.pubsub.schemas.Schema__Event__Leave_Room import Schema__Event__Leave_Room
10
+ from osbot_utils.utils.Misc import random_guid
11
+
12
+
13
+ class PubSub__Client(Kwargs_To_Self):
14
+ event_queue : Event__Queue
15
+ client_id : str
16
+ received_messages : list[str] # todo: fix this to be Events/Messages received via event_queue
17
+
18
+ def __init__(self, **kwargs):
19
+ self.client_id = kwargs.get('client_id') or random_guid()
20
+ super().__init__(**kwargs)
21
+
22
+ def connect(self):
23
+ event_connect = Schema__Event__Connect(connection_id=self.client_id)
24
+ self.send_event(event_connect)
25
+ return self
26
+
27
+ def disconnect(self):
28
+ event_connect = Schema__Event__Disconnect(connection_id=self.client_id)
29
+ self.send_event(event_connect)
30
+ return self
31
+
32
+ def join_room(self, room_name):
33
+ event = Schema__Event__Join_Room(connection_id=self.client_id, room_name=room_name)
34
+ self.send_event(event)
35
+ return self
36
+
37
+ def leave_room(self, room_name):
38
+ event = Schema__Event__Leave_Room(connection_id=self.client_id, room_name=room_name)
39
+ self.send_event(event)
40
+ return self
41
+
42
+ def send_data(self, event_data, **kwargs):
43
+ return self.event_queue.send_data(event_data, connection_id=self.client_id, **kwargs)
44
+
45
+ def send_event(self, event : Schema__Event):
46
+ event.connection_id = self.client_id
47
+ return self.event_queue.send_event(event)
48
+
49
+ def send_message(self, message, **kwargs):
50
+ return self.event_queue.send_message(message, connection_id=self.client_id, **kwargs)
51
+
52
+ def receive_message(self, message):
53
+ self.received_messages.append(message) # todo: fix this to be Events/Messages received via event_queue
@@ -0,0 +1,13 @@
1
+ from osbot_utils.base_classes.Kwargs_To_Self import Kwargs_To_Self
2
+ from osbot_utils.helpers.pubsub.Event__Queue import Event__Queue
3
+ from osbot_utils.helpers.pubsub.PubSub__Client import PubSub__Client
4
+
5
+
6
+ class PubSub__Room(Kwargs_To_Self):
7
+ event_queue: Event__Queue
8
+ room_name : str
9
+ clients : set[PubSub__Client]
10
+
11
+ def send_to_clients__message(self, message):
12
+ for client in self.clients:
13
+ client.receive_message(message)