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.
- osbot_utils/__init__.py +1 -0
- osbot_utils/base_classes/Cache_Pickle.py +129 -0
- osbot_utils/base_classes/Kwargs_To_Disk.py +27 -0
- osbot_utils/base_classes/Kwargs_To_Self.py +308 -0
- osbot_utils/base_classes/Type_Safe__List.py +14 -0
- osbot_utils/base_classes/__init__.py +0 -0
- osbot_utils/context_managers/__init__.py +0 -0
- osbot_utils/context_managers/capture_duration.py +33 -0
- osbot_utils/decorators/__init__.py +0 -0
- osbot_utils/decorators/classes/__init__.py +0 -0
- osbot_utils/decorators/classes/singleton.py +9 -0
- osbot_utils/decorators/lists/__init__.py +0 -0
- osbot_utils/decorators/lists/filter_list.py +12 -0
- osbot_utils/decorators/lists/group_by.py +21 -0
- osbot_utils/decorators/lists/index_by.py +27 -0
- osbot_utils/decorators/methods/__init__.py +0 -0
- osbot_utils/decorators/methods/cache.py +19 -0
- osbot_utils/decorators/methods/cache_on_function.py +56 -0
- osbot_utils/decorators/methods/cache_on_self.py +78 -0
- osbot_utils/decorators/methods/cache_on_tmp.py +71 -0
- osbot_utils/decorators/methods/capture_exception.py +37 -0
- osbot_utils/decorators/methods/capture_status.py +20 -0
- osbot_utils/decorators/methods/catch.py +13 -0
- osbot_utils/decorators/methods/context.py +11 -0
- osbot_utils/decorators/methods/depreciated.py +79 -0
- osbot_utils/decorators/methods/function_type_check.py +62 -0
- osbot_utils/decorators/methods/obj_as_context.py +6 -0
- osbot_utils/decorators/methods/remove_return_value.py +22 -0
- osbot_utils/decorators/methods/required_fields.py +19 -0
- osbot_utils/fluent/Fluent_Dict.py +19 -0
- osbot_utils/fluent/Fluent_List.py +44 -0
- osbot_utils/fluent/__init__.py +1 -0
- osbot_utils/graphs/__init__.py +0 -0
- osbot_utils/graphs/mermaid/Mermaid.py +75 -0
- osbot_utils/graphs/mermaid/Mermaid__Edge.py +49 -0
- osbot_utils/graphs/mermaid/Mermaid__Graph.py +93 -0
- osbot_utils/graphs/mermaid/Mermaid__Node.py +69 -0
- osbot_utils/graphs/mermaid/Mermaid__Renderer.py +54 -0
- osbot_utils/graphs/mermaid/configs/Mermaid__Edge__Config.py +7 -0
- osbot_utils/graphs/mermaid/configs/Mermaid__Node__Config.py +9 -0
- osbot_utils/graphs/mermaid/configs/Mermaid__Render__Config.py +7 -0
- osbot_utils/graphs/mermaid/examples/Mermaid_Examples__FlowChart.py +98 -0
- osbot_utils/graphs/mermaid/models/Mermaid__Diagram_Direction.py +9 -0
- osbot_utils/graphs/mermaid/models/Mermaid__Diagram__Type.py +17 -0
- osbot_utils/graphs/mermaid/models/Mermaid__Node__Shape.py +30 -0
- osbot_utils/graphs/mgraph/MGraph.py +53 -0
- osbot_utils/graphs/mgraph/MGraph__Config.py +7 -0
- osbot_utils/graphs/mgraph/MGraph__Data.py +139 -0
- osbot_utils/graphs/mgraph/MGraph__Edge.py +27 -0
- osbot_utils/graphs/mgraph/MGraph__Node.py +33 -0
- osbot_utils/graphs/mgraph/MGraph__Random_Graphs.py +27 -0
- osbot_utils/graphs/mgraph/MGraph__Serializer.py +43 -0
- osbot_utils/graphs/mgraph/MGraphs.py +17 -0
- osbot_utils/graphs/mgraph/__init__.py +0 -0
- osbot_utils/helpers/CPrint.py +98 -0
- osbot_utils/helpers/Dict_To_Attr.py +7 -0
- osbot_utils/helpers/Local_Cache.py +111 -0
- osbot_utils/helpers/Local_Caches.py +54 -0
- osbot_utils/helpers/Print_Table.py +369 -0
- osbot_utils/helpers/Python_Audit.py +45 -0
- osbot_utils/helpers/Random_Seed.py +27 -0
- osbot_utils/helpers/SCP.py +58 -0
- osbot_utils/helpers/SSH.py +151 -0
- osbot_utils/helpers/Type_Registry.py +16 -0
- osbot_utils/helpers/__init__.py +0 -0
- osbot_utils/helpers/ast/Ast.py +35 -0
- osbot_utils/helpers/ast/Ast_Base.py +124 -0
- osbot_utils/helpers/ast/Ast_Data.py +28 -0
- osbot_utils/helpers/ast/Ast_Load.py +62 -0
- osbot_utils/helpers/ast/Ast_Merge.py +26 -0
- osbot_utils/helpers/ast/Ast_Node.py +117 -0
- osbot_utils/helpers/ast/Ast_Visit.py +85 -0
- osbot_utils/helpers/ast/Call_Tree.py +38 -0
- osbot_utils/helpers/ast/__init__.py +145 -0
- osbot_utils/helpers/ast/nodes/Ast_Add.py +6 -0
- osbot_utils/helpers/ast/nodes/Ast_Alias.py +6 -0
- osbot_utils/helpers/ast/nodes/Ast_And.py +6 -0
- osbot_utils/helpers/ast/nodes/Ast_Argument.py +7 -0
- osbot_utils/helpers/ast/nodes/Ast_Arguments.py +10 -0
- osbot_utils/helpers/ast/nodes/Ast_Assert.py +7 -0
- osbot_utils/helpers/ast/nodes/Ast_Assign.py +8 -0
- osbot_utils/helpers/ast/nodes/Ast_Attribute.py +9 -0
- osbot_utils/helpers/ast/nodes/Ast_Aug_Assign.py +9 -0
- osbot_utils/helpers/ast/nodes/Ast_Bin_Op.py +8 -0
- osbot_utils/helpers/ast/nodes/Ast_Bool_Op.py +7 -0
- osbot_utils/helpers/ast/nodes/Ast_Break.py +7 -0
- osbot_utils/helpers/ast/nodes/Ast_Call.py +17 -0
- osbot_utils/helpers/ast/nodes/Ast_Class_Def.py +9 -0
- osbot_utils/helpers/ast/nodes/Ast_Compare.py +9 -0
- osbot_utils/helpers/ast/nodes/Ast_Comprehension.py +10 -0
- osbot_utils/helpers/ast/nodes/Ast_Constant.py +6 -0
- osbot_utils/helpers/ast/nodes/Ast_Continue.py +7 -0
- osbot_utils/helpers/ast/nodes/Ast_Dict.py +8 -0
- osbot_utils/helpers/ast/nodes/Ast_Eq.py +6 -0
- osbot_utils/helpers/ast/nodes/Ast_Except_Handler.py +9 -0
- osbot_utils/helpers/ast/nodes/Ast_Expr.py +7 -0
- osbot_utils/helpers/ast/nodes/Ast_For.py +10 -0
- osbot_utils/helpers/ast/nodes/Ast_Function_Def.py +17 -0
- osbot_utils/helpers/ast/nodes/Ast_Generator_Exp.py +8 -0
- osbot_utils/helpers/ast/nodes/Ast_Gt.py +7 -0
- osbot_utils/helpers/ast/nodes/Ast_GtE.py +7 -0
- osbot_utils/helpers/ast/nodes/Ast_If.py +9 -0
- osbot_utils/helpers/ast/nodes/Ast_If_Exp.py +9 -0
- osbot_utils/helpers/ast/nodes/Ast_Import.py +7 -0
- osbot_utils/helpers/ast/nodes/Ast_Import_From.py +7 -0
- osbot_utils/helpers/ast/nodes/Ast_In.py +6 -0
- osbot_utils/helpers/ast/nodes/Ast_Is.py +6 -0
- osbot_utils/helpers/ast/nodes/Ast_Is_Not.py +7 -0
- osbot_utils/helpers/ast/nodes/Ast_Keyword.py +8 -0
- osbot_utils/helpers/ast/nodes/Ast_Lambda.py +8 -0
- osbot_utils/helpers/ast/nodes/Ast_List.py +8 -0
- osbot_utils/helpers/ast/nodes/Ast_List_Comp.py +8 -0
- osbot_utils/helpers/ast/nodes/Ast_Load.py +6 -0
- osbot_utils/helpers/ast/nodes/Ast_Lt.py +7 -0
- osbot_utils/helpers/ast/nodes/Ast_LtE.py +7 -0
- osbot_utils/helpers/ast/nodes/Ast_Mod.py +6 -0
- osbot_utils/helpers/ast/nodes/Ast_Module.py +20 -0
- osbot_utils/helpers/ast/nodes/Ast_Mult.py +6 -0
- osbot_utils/helpers/ast/nodes/Ast_Name.py +6 -0
- osbot_utils/helpers/ast/nodes/Ast_Not.py +7 -0
- osbot_utils/helpers/ast/nodes/Ast_Not_Eq.py +7 -0
- osbot_utils/helpers/ast/nodes/Ast_Not_In.py +6 -0
- osbot_utils/helpers/ast/nodes/Ast_Or.py +7 -0
- osbot_utils/helpers/ast/nodes/Ast_Pass.py +7 -0
- osbot_utils/helpers/ast/nodes/Ast_Pow.py +7 -0
- osbot_utils/helpers/ast/nodes/Ast_Raise.py +8 -0
- osbot_utils/helpers/ast/nodes/Ast_Return.py +7 -0
- osbot_utils/helpers/ast/nodes/Ast_Set.py +7 -0
- osbot_utils/helpers/ast/nodes/Ast_Slice.py +8 -0
- osbot_utils/helpers/ast/nodes/Ast_Starred.py +8 -0
- osbot_utils/helpers/ast/nodes/Ast_Store.py +7 -0
- osbot_utils/helpers/ast/nodes/Ast_Sub.py +7 -0
- osbot_utils/helpers/ast/nodes/Ast_Subscript.py +8 -0
- osbot_utils/helpers/ast/nodes/Ast_Try.py +9 -0
- osbot_utils/helpers/ast/nodes/Ast_Tuple.py +9 -0
- osbot_utils/helpers/ast/nodes/Ast_Unary_Op.py +7 -0
- osbot_utils/helpers/ast/nodes/Ast_While.py +8 -0
- osbot_utils/helpers/ast/nodes/Ast_With.py +7 -0
- osbot_utils/helpers/ast/nodes/Ast_With_Item.py +7 -0
- osbot_utils/helpers/ast/nodes/Ast_Yield.py +7 -0
- osbot_utils/helpers/ast/nodes/__init__.py +0 -0
- osbot_utils/helpers/html/Dict_To_Css.py +20 -0
- osbot_utils/helpers/html/Dict_To_Html.py +59 -0
- osbot_utils/helpers/html/Dict_To_Tags.py +88 -0
- osbot_utils/helpers/html/Html_To_Dict.py +75 -0
- osbot_utils/helpers/html/Html_To_Tag.py +20 -0
- osbot_utils/helpers/html/Tag__Base.py +91 -0
- osbot_utils/helpers/html/Tag__Body.py +5 -0
- osbot_utils/helpers/html/Tag__Div.py +5 -0
- osbot_utils/helpers/html/Tag__H.py +9 -0
- osbot_utils/helpers/html/Tag__HR.py +5 -0
- osbot_utils/helpers/html/Tag__Head.py +32 -0
- osbot_utils/helpers/html/Tag__Html.py +42 -0
- osbot_utils/helpers/html/Tag__Link.py +17 -0
- osbot_utils/helpers/html/Tag__Style.py +25 -0
- osbot_utils/helpers/html/__init__.py +0 -0
- osbot_utils/helpers/pubsub/Event__Queue.py +95 -0
- osbot_utils/helpers/pubsub/PubSub__Client.py +53 -0
- osbot_utils/helpers/pubsub/PubSub__Room.py +13 -0
- osbot_utils/helpers/pubsub/PubSub__Server.py +94 -0
- osbot_utils/helpers/pubsub/PubSub__Sqlite.py +24 -0
- osbot_utils/helpers/pubsub/__init__.py +0 -0
- osbot_utils/helpers/pubsub/schemas/Schema__Event.py +15 -0
- osbot_utils/helpers/pubsub/schemas/Schema__Event__Connect.py +7 -0
- osbot_utils/helpers/pubsub/schemas/Schema__Event__Disconnect.py +7 -0
- osbot_utils/helpers/pubsub/schemas/Schema__Event__Join_Room.py +8 -0
- osbot_utils/helpers/pubsub/schemas/Schema__Event__Leave_Room.py +8 -0
- osbot_utils/helpers/pubsub/schemas/Schema__Event__Message.py +7 -0
- osbot_utils/helpers/pubsub/schemas/Schema__PubSub__Client.py +8 -0
- osbot_utils/helpers/pubsub/schemas/__init__.py +0 -0
- osbot_utils/helpers/sqlite/Capture_Sqlite_Error.py +51 -0
- osbot_utils/helpers/sqlite/Sqlite__Cursor.py +87 -0
- osbot_utils/helpers/sqlite/Sqlite__Database.py +137 -0
- osbot_utils/helpers/sqlite/Sqlite__Field.py +70 -0
- osbot_utils/helpers/sqlite/Sqlite__Globals.py +5 -0
- osbot_utils/helpers/sqlite/Sqlite__Table.py +293 -0
- osbot_utils/helpers/sqlite/Sqlite__Table__Create.py +96 -0
- osbot_utils/helpers/sqlite/Temp_Sqlite__Database__Disk.py +17 -0
- osbot_utils/helpers/sqlite/Temp_Sqlite__Table.py +23 -0
- osbot_utils/helpers/sqlite/__init__.py +0 -0
- osbot_utils/helpers/sqlite/domains/Sqlite__Cache__Requests.py +214 -0
- osbot_utils/helpers/sqlite/domains/Sqlite__Cache__Requests__Patch.py +63 -0
- osbot_utils/helpers/sqlite/domains/Sqlite__DB__Files.py +23 -0
- osbot_utils/helpers/sqlite/domains/Sqlite__DB__Graph.py +47 -0
- osbot_utils/helpers/sqlite/domains/Sqlite__DB__Json.py +83 -0
- osbot_utils/helpers/sqlite/domains/Sqlite__DB__Local.py +20 -0
- osbot_utils/helpers/sqlite/domains/Sqlite__DB__Requests.py +39 -0
- osbot_utils/helpers/sqlite/domains/__init__.py +0 -0
- osbot_utils/helpers/sqlite/domains/schemas/Schema__Table__Requests.py +12 -0
- osbot_utils/helpers/sqlite/domains/schemas/__init__.py +0 -0
- osbot_utils/helpers/sqlite/models/Sqlite__Field__Type.py +37 -0
- osbot_utils/helpers/sqlite/models/__init__.py +0 -0
- osbot_utils/helpers/sqlite/sample_data/Sqlite__Sample_Data__Chinook.py +116 -0
- osbot_utils/helpers/sqlite/sample_data/__init__.py +0 -0
- osbot_utils/helpers/sqlite/sql_builder/SQL_Builder.py +159 -0
- osbot_utils/helpers/sqlite/sql_builder/SQL_Builder__Select.py +12 -0
- osbot_utils/helpers/sqlite/sql_builder/__init__.py +0 -0
- osbot_utils/helpers/sqlite/tables/Sqlite__Table__Config.py +63 -0
- osbot_utils/helpers/sqlite/tables/Sqlite__Table__Edges.py +46 -0
- osbot_utils/helpers/sqlite/tables/Sqlite__Table__Files.py +45 -0
- osbot_utils/helpers/sqlite/tables/Sqlite__Table__Nodes.py +52 -0
- osbot_utils/helpers/sqlite/tables/__init__.py +0 -0
- osbot_utils/helpers/trace/Trace_Call.py +120 -0
- osbot_utils/helpers/trace/Trace_Call__Config.py +94 -0
- osbot_utils/helpers/trace/Trace_Call__Graph.py +26 -0
- osbot_utils/helpers/trace/Trace_Call__Handler.py +215 -0
- osbot_utils/helpers/trace/Trace_Call__Print_Lines.py +85 -0
- osbot_utils/helpers/trace/Trace_Call__Print_Traces.py +170 -0
- osbot_utils/helpers/trace/Trace_Call__Stack.py +166 -0
- osbot_utils/helpers/trace/Trace_Call__Stack_Node.py +59 -0
- osbot_utils/helpers/trace/Trace_Call__Stats.py +71 -0
- osbot_utils/helpers/trace/Trace_Call__View_Model.py +75 -0
- osbot_utils/helpers/trace/Trace_Files.py +33 -0
- osbot_utils/helpers/trace/__init__.py +0 -0
- osbot_utils/testing/Catch.py +54 -0
- osbot_utils/testing/Duration.py +69 -0
- osbot_utils/testing/Hook_Method.py +118 -0
- osbot_utils/testing/Log_To_Queue.py +46 -0
- osbot_utils/testing/Log_To_String.py +37 -0
- osbot_utils/testing/Logging.py +81 -0
- osbot_utils/testing/Patch_Print.py +52 -0
- osbot_utils/testing/Profiler.py +89 -0
- osbot_utils/testing/Stderr.py +19 -0
- osbot_utils/testing/Stdout.py +19 -0
- osbot_utils/testing/Temp_File.py +46 -0
- osbot_utils/testing/Temp_Folder.py +114 -0
- osbot_utils/testing/Temp_Sys_Path.py +13 -0
- osbot_utils/testing/Temp_Web_Server.py +83 -0
- osbot_utils/testing/Temp_Zip.py +45 -0
- osbot_utils/testing/Temp_Zip_In_Memory.py +90 -0
- osbot_utils/testing/Unit_Test.py +34 -0
- osbot_utils/testing/Unzip_File.py +30 -0
- osbot_utils/testing/__init__.py +0 -0
- osbot_utils/utils/Assert.py +52 -0
- osbot_utils/utils/Call_Stack.py +187 -0
- osbot_utils/utils/Csv.py +32 -0
- osbot_utils/utils/Dev.py +47 -0
- osbot_utils/utils/Exceptions.py +7 -0
- osbot_utils/utils/Files.py +528 -0
- osbot_utils/utils/Functions.py +113 -0
- osbot_utils/utils/Http.py +136 -0
- osbot_utils/utils/Int.py +6 -0
- osbot_utils/utils/Json.py +171 -0
- osbot_utils/utils/Json_Cache.py +59 -0
- osbot_utils/utils/Lists.py +198 -0
- osbot_utils/utils/Misc.py +496 -0
- osbot_utils/utils/Objects.py +341 -0
- osbot_utils/utils/Png.py +29 -0
- osbot_utils/utils/Process.py +73 -0
- osbot_utils/utils/Python_Logger.py +301 -0
- osbot_utils/utils/Status.py +79 -0
- osbot_utils/utils/Str.py +63 -0
- osbot_utils/utils/Version.py +16 -0
- osbot_utils/utils/Zip.py +97 -0
- osbot_utils/utils/__init__.py +16 -0
- osbot_utils/version +1 -0
- osbot_utils-1.7.7.dist-info/LICENSE +201 -0
- osbot_utils-1.7.7.dist-info/METADATA +46 -0
- osbot_utils-1.7.7.dist-info/RECORD +260 -0
- osbot_utils-1.7.7.dist-info/WHEEL +4 -0
@@ -0,0 +1,94 @@
|
|
1
|
+
import queue
|
2
|
+
from threading import Thread
|
3
|
+
from queue import Queue
|
4
|
+
|
5
|
+
from osbot_utils.helpers.pubsub.Event__Queue import Event__Queue
|
6
|
+
from osbot_utils.helpers.pubsub.PubSub__Client import PubSub__Client
|
7
|
+
from osbot_utils.helpers.pubsub.PubSub__Room import PubSub__Room
|
8
|
+
from osbot_utils.helpers.pubsub.schemas.Schema__Event import Schema__Event
|
9
|
+
from osbot_utils.helpers.pubsub.schemas.Schema__Event__Connect import Schema__Event__Connect
|
10
|
+
from osbot_utils.helpers.pubsub.schemas.Schema__Event__Leave_Room import Schema__Event__Leave_Room
|
11
|
+
from osbot_utils.helpers.pubsub.schemas.Schema__Event__Disconnect import Schema__Event__Disconnect
|
12
|
+
from osbot_utils.helpers.pubsub.schemas.Schema__Event__Join_Room import Schema__Event__Join_Room
|
13
|
+
from osbot_utils.helpers.pubsub.schemas.Schema__Event__Message import Schema__Event__Message
|
14
|
+
from osbot_utils.testing.Logging import Logging
|
15
|
+
from osbot_utils.utils.Dev import pprint
|
16
|
+
|
17
|
+
|
18
|
+
class PubSub__Server(Event__Queue):
|
19
|
+
#pubsub_db: PubSub__Sqlite
|
20
|
+
clients : dict
|
21
|
+
clients_connected: set[PubSub__Client]
|
22
|
+
rooms : dict[str, PubSub__Room]
|
23
|
+
logging : Logging
|
24
|
+
|
25
|
+
def __init__ (self):
|
26
|
+
super().__init__()
|
27
|
+
|
28
|
+
# def db_table_clients(self):
|
29
|
+
# return self.pubsub_db.table_clients() # todo refactor to class that uses this as a base and uses sqlite to capture connections
|
30
|
+
|
31
|
+
def add_client(self, client: PubSub__Client):
|
32
|
+
client_id = client.client_id
|
33
|
+
if client_id:
|
34
|
+
self.clients[client_id] = client
|
35
|
+
return self
|
36
|
+
|
37
|
+
def client_connect(self, client):
|
38
|
+
self.clients_connected.add(client)
|
39
|
+
|
40
|
+
def client_disconnect(self, client):
|
41
|
+
self.clients_connected.discard(client)
|
42
|
+
|
43
|
+
def client_join_room(self, client, event):
|
44
|
+
room_name = event.room_name
|
45
|
+
if room_name:
|
46
|
+
self.room(room_name).clients.add(client)
|
47
|
+
|
48
|
+
def client_message(self, client, event):
|
49
|
+
pass
|
50
|
+
|
51
|
+
def client_leave_room(self, client, event):
|
52
|
+
room_name = event.room_name
|
53
|
+
if room_name:
|
54
|
+
self.room(room_name).clients.discard(client)
|
55
|
+
|
56
|
+
def get_client(self, client_id):
|
57
|
+
return self.clients.get(client_id)
|
58
|
+
|
59
|
+
def handle_event(self, event: Schema__Event):
|
60
|
+
event_type = type(event)
|
61
|
+
client = self.clients.get(event.connection_id)
|
62
|
+
if client:
|
63
|
+
if event_type is Schema__Event__Connect : self.client_connect (client)
|
64
|
+
elif event_type is Schema__Event__Disconnect : self.client_disconnect(client)
|
65
|
+
elif event_type is Schema__Event__Join_Room : self.client_join_room (client, event)
|
66
|
+
elif event_type is Schema__Event__Leave_Room : self.client_leave_room(client, event)
|
67
|
+
elif event_type is Schema__Event__Message : self.client_message (client, event)
|
68
|
+
else:
|
69
|
+
return False
|
70
|
+
|
71
|
+
if self.log_events:
|
72
|
+
self.events.append(event)
|
73
|
+
return True
|
74
|
+
|
75
|
+
def log(self, message):
|
76
|
+
self.logging.debug(message)
|
77
|
+
return self
|
78
|
+
|
79
|
+
def new_client(self):
|
80
|
+
client = PubSub__Client(event_queue = self)
|
81
|
+
self.add_client(client)
|
82
|
+
return client
|
83
|
+
|
84
|
+
def room(self, room_name):
|
85
|
+
if room_name not in self.rooms:
|
86
|
+
new_room = PubSub__Room(room_name=room_name)
|
87
|
+
self.rooms[room_name] = new_room
|
88
|
+
|
89
|
+
return self.rooms.get(room_name)
|
90
|
+
|
91
|
+
|
92
|
+
|
93
|
+
|
94
|
+
|
@@ -0,0 +1,24 @@
|
|
1
|
+
from osbot_utils.decorators.methods.cache_on_self import cache_on_self
|
2
|
+
from osbot_utils.helpers.pubsub.schemas.Schema__PubSub__Client import Schema__PubSub__Clients
|
3
|
+
from osbot_utils.helpers.sqlite.Sqlite__Database import Sqlite__Database
|
4
|
+
|
5
|
+
TABLE_NAME__PUB_SUB__CLIENTS = 'pubsub_clients'
|
6
|
+
TABLE_SCHEMA__PUB_SUB__CLIENTS = Schema__PubSub__Clients
|
7
|
+
|
8
|
+
class PubSub__Sqlite(Sqlite__Database):
|
9
|
+
|
10
|
+
@cache_on_self
|
11
|
+
def table_clients(self):
|
12
|
+
return self.table(TABLE_NAME__PUB_SUB__CLIENTS)
|
13
|
+
|
14
|
+
def table_clients__create(self):
|
15
|
+
with self.table_clients() as _:
|
16
|
+
_.row_schema = TABLE_SCHEMA__PUB_SUB__CLIENTS
|
17
|
+
if _.exists() is False:
|
18
|
+
_.create() # create if it doesn't exist
|
19
|
+
return True
|
20
|
+
return False
|
21
|
+
|
22
|
+
def setup(self):
|
23
|
+
self.table_clients__create()
|
24
|
+
return self
|
File without changes
|
@@ -0,0 +1,15 @@
|
|
1
|
+
from osbot_utils.base_classes.Kwargs_To_Self import Kwargs_To_Self
|
2
|
+
from osbot_utils.utils.Misc import random_guid
|
3
|
+
|
4
|
+
|
5
|
+
class Schema__Event(Kwargs_To_Self):
|
6
|
+
connection_id: str
|
7
|
+
event_id : str
|
8
|
+
event_data : dict
|
9
|
+
event_message: str
|
10
|
+
event_target : str
|
11
|
+
event_type : str
|
12
|
+
timestamp : int
|
13
|
+
|
14
|
+
def __repr__(self):
|
15
|
+
return f'<{self.__class__.__name__}: {self.event_id}>'
|
@@ -0,0 +1,7 @@
|
|
1
|
+
from osbot_utils.base_classes.Kwargs_To_Self import Kwargs_To_Self
|
2
|
+
from osbot_utils.helpers.pubsub.schemas.Schema__Event import Schema__Event
|
3
|
+
from osbot_utils.utils.Misc import random_guid
|
4
|
+
|
5
|
+
|
6
|
+
class Schema__Event__Connect(Schema__Event):
|
7
|
+
event_type : str = 'connect'
|
@@ -0,0 +1,7 @@
|
|
1
|
+
from osbot_utils.base_classes.Kwargs_To_Self import Kwargs_To_Self
|
2
|
+
from osbot_utils.helpers.pubsub.schemas.Schema__Event import Schema__Event
|
3
|
+
from osbot_utils.utils.Misc import random_guid
|
4
|
+
|
5
|
+
|
6
|
+
class Schema__Event__Disconnect(Schema__Event):
|
7
|
+
event_type : str = 'disconnect'
|
@@ -0,0 +1,8 @@
|
|
1
|
+
from osbot_utils.base_classes.Kwargs_To_Self import Kwargs_To_Self
|
2
|
+
from osbot_utils.helpers.pubsub.schemas.Schema__Event import Schema__Event
|
3
|
+
from osbot_utils.utils.Misc import random_guid
|
4
|
+
|
5
|
+
|
6
|
+
class Schema__Event__Join_Room(Schema__Event):
|
7
|
+
event_type : str = 'join-room'
|
8
|
+
room_name : str
|
@@ -0,0 +1,8 @@
|
|
1
|
+
from osbot_utils.base_classes.Kwargs_To_Self import Kwargs_To_Self
|
2
|
+
from osbot_utils.helpers.pubsub.schemas.Schema__Event import Schema__Event
|
3
|
+
from osbot_utils.utils.Misc import random_guid
|
4
|
+
|
5
|
+
|
6
|
+
class Schema__Event__Leave_Room(Schema__Event):
|
7
|
+
event_type : str = 'leave-room'
|
8
|
+
room_name : str
|
File without changes
|
@@ -0,0 +1,51 @@
|
|
1
|
+
import sqlite3
|
2
|
+
from functools import wraps
|
3
|
+
from osbot_utils.utils.Dev import pprint
|
4
|
+
|
5
|
+
def capture_sqlite_error(func):
|
6
|
+
@wraps(func)
|
7
|
+
def wrapper(*args, **kwargs):
|
8
|
+
return_value = None
|
9
|
+
with Capture_Sqlite_Error() as error_capture:
|
10
|
+
try:
|
11
|
+
return_value = func(*args, **kwargs)
|
12
|
+
except Exception as e:
|
13
|
+
raise e
|
14
|
+
if error_capture.error_details:
|
15
|
+
print()
|
16
|
+
pprint('****** SQLITE ERROR DETECTED ******')
|
17
|
+
pprint(error_capture.error_details)
|
18
|
+
return return_value
|
19
|
+
|
20
|
+
return wrapper
|
21
|
+
|
22
|
+
class Capture_Sqlite_Error:
|
23
|
+
def __init__(self):
|
24
|
+
self.error_details = {}
|
25
|
+
|
26
|
+
def __enter__(self):
|
27
|
+
return self
|
28
|
+
|
29
|
+
def __exit__(self, exc_type, exc_val, exc_tb):
|
30
|
+
if exc_val: # Check if an exception occurred
|
31
|
+
if isinstance(exc_val, sqlite3.ProgrammingError): # Check for SQLite ProgrammingError
|
32
|
+
self.process_programming_error(exc_val)
|
33
|
+
return True # Prevent the exception from propagating
|
34
|
+
elif isinstance(exc_val, sqlite3.Error): # Handle other generic sqlite3 errors
|
35
|
+
self.process_generic_sqlite_error(exc_val)
|
36
|
+
return True
|
37
|
+
return False # Allow exceptions not handled here to propagate
|
38
|
+
|
39
|
+
def process_programming_error(self, exc_val):
|
40
|
+
error_message = str(exc_val)
|
41
|
+
self.error_details = {
|
42
|
+
'error_code': 'ProgrammingError',
|
43
|
+
'error_message': error_message
|
44
|
+
}
|
45
|
+
|
46
|
+
def process_generic_sqlite_error(self, exc_val):
|
47
|
+
error_message = str(exc_val)
|
48
|
+
self.error_details = {
|
49
|
+
'error_code': type(exc_val).__name__,
|
50
|
+
'error_message': error_message
|
51
|
+
}
|
@@ -0,0 +1,87 @@
|
|
1
|
+
from osbot_utils.base_classes.Kwargs_To_Self import Kwargs_To_Self
|
2
|
+
from osbot_utils.decorators.methods.cache import cache
|
3
|
+
from osbot_utils.decorators.methods.cache_on_self import cache_on_self
|
4
|
+
from osbot_utils.helpers.sqlite.Sqlite__Database import Sqlite__Database
|
5
|
+
from osbot_utils.utils.Status import status_ok, status_error, status_exception
|
6
|
+
|
7
|
+
|
8
|
+
class Sqlite__Cursor(Kwargs_To_Self):
|
9
|
+
database : Sqlite__Database
|
10
|
+
|
11
|
+
@cache_on_self
|
12
|
+
def cursor(self):
|
13
|
+
return self.connection().cursor()
|
14
|
+
|
15
|
+
def commit(self):
|
16
|
+
self.connection().commit()
|
17
|
+
return self
|
18
|
+
|
19
|
+
def connection(self):
|
20
|
+
return self.database.connection()
|
21
|
+
|
22
|
+
def execute(self, sql_query, *params):
|
23
|
+
try:
|
24
|
+
self.cursor().execute(sql_query, *params)
|
25
|
+
return status_ok()
|
26
|
+
except Exception as error:
|
27
|
+
return status_exception(error=f'{error}')
|
28
|
+
|
29
|
+
def execute_and_commit(self, sql_query, *params): # todo: refactor this with the execute method
|
30
|
+
try:
|
31
|
+
self.cursor().execute(sql_query, *params)
|
32
|
+
self.connection().commit()
|
33
|
+
return status_ok()
|
34
|
+
except Exception as error:
|
35
|
+
return status_exception(error=f'{error}')
|
36
|
+
|
37
|
+
def execute__fetch_all(self,sql_query, *params):
|
38
|
+
self.execute(sql_query,*params)
|
39
|
+
return self.cursor().fetchall()
|
40
|
+
|
41
|
+
def execute__fetch_one(self,sql_query, *params):
|
42
|
+
self.execute(sql_query, *params)
|
43
|
+
return self.cursor().fetchone()
|
44
|
+
|
45
|
+
def fetch_all(self):
|
46
|
+
return self.cursor().fetchall()
|
47
|
+
|
48
|
+
def fetch_one(self):
|
49
|
+
return self.cursor().fetchone()
|
50
|
+
|
51
|
+
def table_create(self, table_name, fields):
|
52
|
+
if table_name and fields:
|
53
|
+
sql_query = f"CREATE TABLE {table_name} ({', '.join(fields)})"
|
54
|
+
return self.execute(sql_query=sql_query)
|
55
|
+
return status_error(message='table_name, fields cannot be empty')
|
56
|
+
|
57
|
+
def table_delete(self, table_name):
|
58
|
+
sql_query = f"DROP TABLE IF EXISTS {table_name};"
|
59
|
+
return self.execute(sql_query=sql_query)
|
60
|
+
|
61
|
+
def table_exists(self, table_name):
|
62
|
+
self.execute("SELECT name FROM sqlite_master WHERE type='table' AND name=?", (table_name,))
|
63
|
+
return self.cursor().fetchone() is not None
|
64
|
+
|
65
|
+
def table_schema(self, table_name):
|
66
|
+
sql_query = f"PRAGMA table_info({table_name});"
|
67
|
+
self.execute(sql_query)
|
68
|
+
columns = self.cursor().fetchall()
|
69
|
+
return columns
|
70
|
+
|
71
|
+
def table__sqlite_master(self): # todo: refactor into separate class
|
72
|
+
sql_query = "SELECT * FROM sqlite_master"
|
73
|
+
self.execute(sql_query)
|
74
|
+
return self.cursor().fetchall()
|
75
|
+
|
76
|
+
def tables(self):
|
77
|
+
sql_query = "SELECT * FROM sqlite_master WHERE type='table';" # Query to select all table names from the sqlite_master table
|
78
|
+
self.execute(sql_query)
|
79
|
+
return self.cursor().fetchall()
|
80
|
+
|
81
|
+
def tables_names(self):
|
82
|
+
cell_name = 'name'
|
83
|
+
sql_query = f"SELECT {cell_name} FROM sqlite_master WHERE type='table';" # Query to select all table names from the sqlite_master table
|
84
|
+
self.execute(sql_query)
|
85
|
+
all_rows = self.cursor().fetchall()
|
86
|
+
all_values = [cell.get(cell_name) for cell in all_rows]
|
87
|
+
return all_values
|
@@ -0,0 +1,137 @@
|
|
1
|
+
import sqlite3
|
2
|
+
|
3
|
+
from osbot_utils.base_classes.Kwargs_To_Self import Kwargs_To_Self
|
4
|
+
from osbot_utils.decorators.methods.cache import cache
|
5
|
+
from osbot_utils.decorators.methods.cache_on_self import cache_on_self
|
6
|
+
|
7
|
+
from osbot_utils.utils.Files import current_temp_folder, path_combine, folder_create, file_exists, file_delete
|
8
|
+
from osbot_utils.utils.Misc import random_filename
|
9
|
+
|
10
|
+
SQLITE_DATABASE_PATH__IN_MEMORY = ':memory:'
|
11
|
+
FOLDER_NAME_TEMP_DATABASES = '_temp_sqlite_databases'
|
12
|
+
TEMP_DATABASE__FILE_NAME_PREFIX = 'random_sqlite_db__'
|
13
|
+
TEMP_DATABASE__FILE_EXTENSION = '.sqlite'
|
14
|
+
|
15
|
+
class Sqlite__Database(Kwargs_To_Self):
|
16
|
+
db_path : str = None
|
17
|
+
closed : bool = False
|
18
|
+
connected : bool = False
|
19
|
+
deleted : bool = False
|
20
|
+
in_memory : bool = True # default to an in-memory database
|
21
|
+
auto_schema_row : bool = False # option to map the table's schema_row when creating an table object
|
22
|
+
|
23
|
+
def __init__(self, **kwargs):
|
24
|
+
super().__init__(**kwargs)
|
25
|
+
if self.db_path: # if self.db_path is set via the ctor (then it means that this is not in memory)
|
26
|
+
self.in_memory = False # todo: see if this is not better done with a direct method for a getter/setter
|
27
|
+
|
28
|
+
def close(self):
|
29
|
+
if self.closed is False:
|
30
|
+
self.connection().close()
|
31
|
+
self.closed = True
|
32
|
+
self.connected = False
|
33
|
+
return True
|
34
|
+
return False
|
35
|
+
|
36
|
+
# def config(self, key):
|
37
|
+
# return self.table_config().data().get(key)
|
38
|
+
|
39
|
+
@cache_on_self
|
40
|
+
def connect(self):
|
41
|
+
connection_string = self.connection_string()
|
42
|
+
connection = sqlite3.connect(connection_string)
|
43
|
+
connection.row_factory = self.dict_factory # this returns a dict as the row value of every query
|
44
|
+
self.connected = True
|
45
|
+
return connection
|
46
|
+
|
47
|
+
def connection(self):
|
48
|
+
return self.connect()
|
49
|
+
|
50
|
+
def connection_string(self):
|
51
|
+
if self.in_memory:
|
52
|
+
return SQLITE_DATABASE_PATH__IN_MEMORY
|
53
|
+
if not self.db_path:
|
54
|
+
self.db_path = self.path_temp_database()
|
55
|
+
return self.db_path
|
56
|
+
|
57
|
+
@cache_on_self
|
58
|
+
def cursor(self):
|
59
|
+
from osbot_utils.helpers.sqlite.Sqlite__Cursor import Sqlite__Cursor
|
60
|
+
return Sqlite__Cursor(database=self)
|
61
|
+
|
62
|
+
def delete(self):
|
63
|
+
if self.in_memory: # can't delete an in-memory database
|
64
|
+
return False
|
65
|
+
if self.deleted:
|
66
|
+
return False
|
67
|
+
self.close()
|
68
|
+
if file_delete(self.db_path):
|
69
|
+
self.deleted = True
|
70
|
+
return True
|
71
|
+
return False
|
72
|
+
|
73
|
+
def dict_factory(self, cursor, row): # from https://docs.python.org/3/library/sqlite3.html#how-to-create-and-use-row-factories
|
74
|
+
fields = [column[0] for column in cursor.description]
|
75
|
+
return {key: value for key, value in zip(fields, row)}
|
76
|
+
|
77
|
+
def exists(self):
|
78
|
+
if self.in_memory:
|
79
|
+
return True
|
80
|
+
return file_exists(self.db_path)
|
81
|
+
|
82
|
+
def path_temp_database(self, file_name=None):
|
83
|
+
if file_name is None:
|
84
|
+
file_name = TEMP_DATABASE__FILE_NAME_PREFIX + random_filename(extension=TEMP_DATABASE__FILE_EXTENSION)
|
85
|
+
return path_combine(self.path_temp_databases(), file_name)
|
86
|
+
|
87
|
+
def path_temp_databases(self):
|
88
|
+
path_temp_databases = path_combine(current_temp_folder(), FOLDER_NAME_TEMP_DATABASES) # use current temp folder has the parent folder
|
89
|
+
folder_create(path_temp_databases) # make sure it exists
|
90
|
+
return path_temp_databases
|
91
|
+
|
92
|
+
def save_to(self, path):
|
93
|
+
connection = self.connection()
|
94
|
+
file_conn = sqlite3.connect(path)
|
95
|
+
connection.backup(file_conn)
|
96
|
+
file_conn.close()
|
97
|
+
return path
|
98
|
+
|
99
|
+
|
100
|
+
def table(self, table_name):
|
101
|
+
from osbot_utils.helpers.sqlite.Sqlite__Table import Sqlite__Table # need to import here due to circular imports
|
102
|
+
table = Sqlite__Table(database=self, table_name=table_name)
|
103
|
+
if self.auto_schema_row:
|
104
|
+
table.row_schema__set_from_field_types() # todo: see if we shouldn't just propagate the auto_schema_row to the Sqlite__Table and do that on the ctor
|
105
|
+
return table
|
106
|
+
|
107
|
+
# def table_config(self):
|
108
|
+
# from osbot_utils.helpers.sqlite.tables.Sqlite__Table__Config import Sqlite__Table__Config
|
109
|
+
# table_config = Sqlite__Table__Config(database=self)
|
110
|
+
# table_config.setup()
|
111
|
+
# return table_config
|
112
|
+
|
113
|
+
def table__sqlite_master(self):
|
114
|
+
return self.table('sqlite_master')
|
115
|
+
|
116
|
+
def tables(self):
|
117
|
+
tables = []
|
118
|
+
for table_data in self.tables_raw():
|
119
|
+
table_name = table_data.get('name')
|
120
|
+
table = self.table(table_name)
|
121
|
+
tables.append(table)
|
122
|
+
return tables
|
123
|
+
|
124
|
+
def tables_raw(self):
|
125
|
+
return self.cursor().tables()
|
126
|
+
|
127
|
+
def tables_names(self, include_sqlite_master=False):
|
128
|
+
table_names = self.table__sqlite_master().select_field_values('name')
|
129
|
+
if include_sqlite_master:
|
130
|
+
table_names.append('sqlite_master')
|
131
|
+
return table_names
|
132
|
+
|
133
|
+
|
134
|
+
|
135
|
+
|
136
|
+
|
137
|
+
|
@@ -0,0 +1,70 @@
|
|
1
|
+
import inspect
|
2
|
+
import typing
|
3
|
+
from decimal import Decimal
|
4
|
+
from enum import auto, Enum
|
5
|
+
from typing import Optional, Union
|
6
|
+
|
7
|
+
from osbot_utils.base_classes.Kwargs_To_Self import Kwargs_To_Self
|
8
|
+
from osbot_utils.helpers.sqlite.models.Sqlite__Field__Type import Sqlite__Field__Type
|
9
|
+
|
10
|
+
|
11
|
+
class Sqlite__Field(Kwargs_To_Self):
|
12
|
+
cid : int
|
13
|
+
name : str
|
14
|
+
type : Sqlite__Field__Type
|
15
|
+
notnull : bool
|
16
|
+
dflt_value : Optional[Union[int, str, float, bytes]]
|
17
|
+
pk : bool
|
18
|
+
autoincrement : bool
|
19
|
+
unique : bool
|
20
|
+
is_foreign_key : bool # Indicates if the field is a foreign key
|
21
|
+
references_table : Optional[str] # The table the foreign key references
|
22
|
+
references_column: Optional[str] # The column in the referenced table
|
23
|
+
on_delete_action : Optional[str] # Action on delete (e.g., CASCADE, SET NULL)
|
24
|
+
precision : Optional[int] # Precision for decimal types
|
25
|
+
scale : Optional[int] # Scale for decimal types
|
26
|
+
check_constraint : Optional[str] # Check constraint expression
|
27
|
+
|
28
|
+
def text_for_create_table(self):
|
29
|
+
parts = [self.name] # Start with name
|
30
|
+
|
31
|
+
if self.type == Sqlite__Field__Type.DECIMAL and self.precision is not None and self.scale is not None:
|
32
|
+
parts.append(f"DECIMAL({self.precision}, {self.scale})")
|
33
|
+
else:
|
34
|
+
parts.append(self.type.name)
|
35
|
+
if self.pk:
|
36
|
+
parts.append("PRIMARY KEY")
|
37
|
+
if self.autoincrement:
|
38
|
+
parts.append("AUTOINCREMENT")
|
39
|
+
if self.unique:
|
40
|
+
parts.append("UNIQUE")
|
41
|
+
if self.notnull:
|
42
|
+
parts.append("NOT NULL")
|
43
|
+
|
44
|
+
|
45
|
+
if self.check_constraint:
|
46
|
+
parts.append(f"CHECK ({self.check_constraint})")
|
47
|
+
|
48
|
+
if self.is_foreign_key and self.references_table and self.references_column:
|
49
|
+
fk_constraint = f"FOREIGN KEY ({self.name}) REFERENCES {self.references_table} ({self.references_column})"
|
50
|
+
if self.on_delete_action:
|
51
|
+
fk_constraint += f" ON DELETE {self.on_delete_action}"
|
52
|
+
return fk_constraint
|
53
|
+
|
54
|
+
return " ".join(parts)
|
55
|
+
|
56
|
+
@classmethod
|
57
|
+
def fix_from_json_data(cls, json_data):
|
58
|
+
if type(json_data) is dict:
|
59
|
+
type_type = json_data.get('type')
|
60
|
+
mapped_type = Sqlite__Field__Type.type_map().get(type_type)
|
61
|
+
if mapped_type:
|
62
|
+
json_data['type'] = mapped_type
|
63
|
+
return mapped_type
|
64
|
+
|
65
|
+
|
66
|
+
@classmethod
|
67
|
+
def from_json(cls, json_data):
|
68
|
+
cls.fix_from_json_data(json_data)
|
69
|
+
return super(Sqlite__Field, cls).from_json(json_data)
|
70
|
+
|