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,293 @@
|
|
1
|
+
import re
|
2
|
+
|
3
|
+
from osbot_utils.base_classes.Kwargs_To_Self import Kwargs_To_Self
|
4
|
+
from osbot_utils.decorators.lists.filter_list import filter_list
|
5
|
+
from osbot_utils.decorators.lists.index_by import index_by
|
6
|
+
from osbot_utils.decorators.methods.cache_on_self import cache_on_self
|
7
|
+
from osbot_utils.helpers.Print_Table import Print_Table
|
8
|
+
from osbot_utils.helpers.sqlite.Sqlite__Database import Sqlite__Database
|
9
|
+
from osbot_utils.helpers.sqlite.Sqlite__Globals import DEFAULT_FIELD_NAME__ID, ROW_BASE_CLASS, SQL_TABLE__MODULE_NAME__ROW_SCHEMA
|
10
|
+
from osbot_utils.helpers.sqlite.models.Sqlite__Field__Type import Sqlite__Field__Type
|
11
|
+
from osbot_utils.utils.Dev import pprint
|
12
|
+
from osbot_utils.utils.Json import json_load
|
13
|
+
from osbot_utils.utils.Misc import list_set
|
14
|
+
from osbot_utils.utils.Objects import base_types, default_value, bytes_to_obj, obj_to_bytes
|
15
|
+
from osbot_utils.utils.Str import str_cap_snake_case
|
16
|
+
|
17
|
+
class Sqlite__Table(Kwargs_To_Self):
|
18
|
+
database : Sqlite__Database
|
19
|
+
table_name : str
|
20
|
+
row_schema : type
|
21
|
+
auto_pickle_blob: bool = False
|
22
|
+
|
23
|
+
def __setattr__(self, key, value):
|
24
|
+
if key =='table_name': # SQL injection protection
|
25
|
+
if re.search(r'[^a-zA-Z0-9_-]',value): # make sure table name cannot be used to inject SQL, str_safe uses r'[^a-zA-Z0-9_-]' regex, i.e. only allows letter, numbers and the chars - _
|
26
|
+
raise ValueError( "Invalid table name. Table names can only contain alphanumeric characters, numbers, underscores, and hyphens.")
|
27
|
+
|
28
|
+
super().__setattr__(key, value)
|
29
|
+
|
30
|
+
def _table_create(self): # todo: Sqlite__Table__Create needs to be refactored (since that was created before we had support for table_class )
|
31
|
+
from osbot_utils.helpers.sqlite.Sqlite__Table__Create import Sqlite__Table__Create
|
32
|
+
table_create = Sqlite__Table__Create(self.table_name) # todo: fix this workflow
|
33
|
+
table_create.table = self
|
34
|
+
return table_create # since it is weird to have to overwrite the table vale of Sqlite__Table__Create
|
35
|
+
|
36
|
+
def add_row(self, **row_data):
|
37
|
+
new_row = self.new_row_obj(row_data)
|
38
|
+
return self.row_add(new_row)
|
39
|
+
|
40
|
+
def add_row_and_commit(self, **row_data):
|
41
|
+
new_row = self.new_row_obj(row_data)
|
42
|
+
return self.row_add_and_commit(new_row)
|
43
|
+
|
44
|
+
def clear(self):
|
45
|
+
sql_query = self.sql_builder().command__delete_table()
|
46
|
+
return self.cursor().execute_and_commit(sql_query)
|
47
|
+
|
48
|
+
def create(self):
|
49
|
+
table_create = self._table_create()
|
50
|
+
return table_create.create_table__from_row_schema(self.row_schema)
|
51
|
+
|
52
|
+
def commit(self):
|
53
|
+
return self.cursor().commit()
|
54
|
+
|
55
|
+
def connection(self):
|
56
|
+
return self.database.connection()
|
57
|
+
|
58
|
+
def cursor(self):
|
59
|
+
return self.database.cursor()
|
60
|
+
|
61
|
+
def delete(self):
|
62
|
+
if self.exists() is False: # if table doesn't exist
|
63
|
+
return False # return False
|
64
|
+
self.cursor().table_delete(self.table_name) # delete table
|
65
|
+
return self.exists() is False # confirm table does not exist
|
66
|
+
|
67
|
+
def exists(self):
|
68
|
+
return self.cursor().table_exists(self.table_name)
|
69
|
+
|
70
|
+
def fields(self):
|
71
|
+
return self.schema(index_by='name')
|
72
|
+
|
73
|
+
@cache_on_self
|
74
|
+
def fields__cached(self):
|
75
|
+
return self.fields()
|
76
|
+
|
77
|
+
def fields_data__from_raw_json(self, target_field): # todo: see if this should be refactored into a Select/Data filtering class
|
78
|
+
fields_data = []
|
79
|
+
for raw_field_data in self.select_field_values(target_field):
|
80
|
+
fields_data.append(json_load(raw_field_data))
|
81
|
+
return fields_data
|
82
|
+
|
83
|
+
def fields_types__cached(self, exclude_id=False):
|
84
|
+
fields_types = {}
|
85
|
+
for field_name, field_data in self.fields__cached().items():
|
86
|
+
if exclude_id and field_name == DEFAULT_FIELD_NAME__ID:
|
87
|
+
continue
|
88
|
+
sqlite_field_type = field_data['type']
|
89
|
+
field_type = Sqlite__Field__Type.enum_map().get(sqlite_field_type)
|
90
|
+
fields_types[field_name] = field_type
|
91
|
+
return fields_types
|
92
|
+
|
93
|
+
def fields_names__cached(self, exclude_id=False, include_star_field=False):
|
94
|
+
field_names = list_set(self.fields__cached())
|
95
|
+
if exclude_id:
|
96
|
+
field_names.remove(DEFAULT_FIELD_NAME__ID)
|
97
|
+
if include_star_field:
|
98
|
+
field_names.append('*')
|
99
|
+
return field_names
|
100
|
+
|
101
|
+
def index_create(self, index_field):
|
102
|
+
if index_field not in self.fields_names__cached():
|
103
|
+
raise ValueError(f"in index_create, invalid target_field: {index_field}")
|
104
|
+
|
105
|
+
index_name = self.index_name(index_field)
|
106
|
+
sql_query = f'CREATE INDEX IF NOT EXISTS {index_name} ON {self.table_name}({index_field});'
|
107
|
+
return self.cursor().execute_and_commit(sql_query)
|
108
|
+
|
109
|
+
def index_delete(self, index_name):
|
110
|
+
sql_query = f'DROP INDEX IF EXISTS {index_name};'
|
111
|
+
return self.cursor().execute_and_commit(sql_query)
|
112
|
+
|
113
|
+
def index_exists(self, index_field):
|
114
|
+
index_name = self.index_name(index_field)
|
115
|
+
return index_name in self.indexes()
|
116
|
+
|
117
|
+
def index_name(self, index_field):
|
118
|
+
return f'idx__{self.table_name}__{index_field}'
|
119
|
+
|
120
|
+
def list_of_field_name_from_rows(self, rows, field_name):
|
121
|
+
return [row[field_name] for row in rows]
|
122
|
+
|
123
|
+
def indexes(self):
|
124
|
+
field_name = 'name'
|
125
|
+
return_fields = [field_name]
|
126
|
+
table_sqlite_master = self.database.table__sqlite_master()
|
127
|
+
table_type = 'index'
|
128
|
+
query_conditions = {'type': table_type, 'tbl_name': self.table_name}
|
129
|
+
sql_query, params = table_sqlite_master.sql_builder().query_select_fields_with_conditions(return_fields, query_conditions)
|
130
|
+
rows = table_sqlite_master.cursor().execute__fetch_all(sql_query, params)
|
131
|
+
return table_sqlite_master.list_of_field_name_from_rows(rows, field_name)
|
132
|
+
|
133
|
+
def new_row_obj(self, row_data=None):
|
134
|
+
if self.row_schema:
|
135
|
+
new_obj = self.row_schema()
|
136
|
+
if row_data and ROW_BASE_CLASS in base_types(new_obj):
|
137
|
+
row_data = self.parse_new_row_data(row_data)
|
138
|
+
new_obj.update_from_kwargs(**row_data)
|
139
|
+
return new_obj
|
140
|
+
|
141
|
+
def not_exists(self):
|
142
|
+
return self.exists() is False
|
143
|
+
|
144
|
+
def parse_new_row_data(self, row_data):
|
145
|
+
if row_data:
|
146
|
+
if self.auto_pickle_blob:
|
147
|
+
fields = self.fields__cached()
|
148
|
+
picked_row_data = {}
|
149
|
+
for field_name, field_value in row_data.items():
|
150
|
+
field_type = fields.get(field_name, {}).get('type')
|
151
|
+
if field_type == 'BLOB':
|
152
|
+
picked_row_data[field_name] = obj_to_bytes(field_value)
|
153
|
+
else:
|
154
|
+
picked_row_data[field_name] = field_value
|
155
|
+
return picked_row_data
|
156
|
+
return row_data
|
157
|
+
def parse_row(self, row):
|
158
|
+
if row and self.auto_pickle_blob:
|
159
|
+
fields = self.fields__cached()
|
160
|
+
for field_name, field_value in row.items():
|
161
|
+
field_type = fields.get(field_name, {}).get('type')
|
162
|
+
if field_type == 'BLOB':
|
163
|
+
row[field_name] = bytes_to_obj(field_value)
|
164
|
+
return row
|
165
|
+
|
166
|
+
def parse_rows(self, rows):
|
167
|
+
return [self.parse_row(row) for row in rows]
|
168
|
+
|
169
|
+
def print(self, **kwargs):
|
170
|
+
return Print_Table(**kwargs).print(self.rows())
|
171
|
+
|
172
|
+
def row_add(self, row_obj=None):
|
173
|
+
invalid_reason = self.sql_builder().validate_row_obj(row_obj)
|
174
|
+
if invalid_reason:
|
175
|
+
raise Exception(f"in row_add the provided row_obj is not valid: {invalid_reason}")
|
176
|
+
return self.row_add_record(row_obj.__dict__)
|
177
|
+
|
178
|
+
def row_add_and_commit(self, row_obj=None):
|
179
|
+
if self.row_add(row_obj).get('status') == 'ok':
|
180
|
+
self.commit()
|
181
|
+
return row_obj # this allows the original callers to see the actual object that was added to the table
|
182
|
+
|
183
|
+
def row_add_record(self, record):
|
184
|
+
validation_result = self.validate_record_with_schema(record)
|
185
|
+
if validation_result:
|
186
|
+
raise ValueError(f"row_add_record, validation_result for provided record failed with {validation_result}")
|
187
|
+
|
188
|
+
sql_command,params = self.sql_builder().command_for_insert(record)
|
189
|
+
return self.cursor().execute(sql_command, params) # Execute the SQL statement with the filtered data values
|
190
|
+
|
191
|
+
def row_schema__create_from_current_field_types(self):
|
192
|
+
exclude_field_id = True # don't include the id field since in most cases the row_schema doesn't include it
|
193
|
+
field_types = self.fields_types__cached(exclude_id=exclude_field_id) # mapping with field name to field type (in python)
|
194
|
+
caps_table_name = str_cap_snake_case(self.table_name)
|
195
|
+
dynamic_class_name = f'Row_Schema__{caps_table_name}' # name that we will give to the dynamic class generated
|
196
|
+
dynamic_class_dict = { k: default_value(v) for k, v in field_types.items()} # assign the field values its default value (for that type)
|
197
|
+
dynamic_class_dict['__module__'] = SQL_TABLE__MODULE_NAME__ROW_SCHEMA # set the module name
|
198
|
+
Dynamic_Class = type(dynamic_class_name, (ROW_BASE_CLASS,), dynamic_class_dict) # Create the dynamic class
|
199
|
+
Dynamic_Class.__annotations__ = field_types # Set annotations of the new Dynamic_Class to be the mappings we have from field_types
|
200
|
+
return Dynamic_Class # return the Dynamic class (whose fields should match the field_types)
|
201
|
+
|
202
|
+
def row_schema__set_from_field_types(self):
|
203
|
+
self.row_schema = self.row_schema__create_from_current_field_types()
|
204
|
+
return self
|
205
|
+
|
206
|
+
def row_update(self, update_fields, query_conditions ):
|
207
|
+
sql_query, params = self.sql_builder().sql_query_update_with_conditions(update_fields, query_conditions)
|
208
|
+
return self.cursor().execute_and_commit(sql_query, params)
|
209
|
+
|
210
|
+
def rows(self, fields_names=None, limit=None):
|
211
|
+
sql_query = self.sql_builder(limit=limit).query_for_fields(fields_names)
|
212
|
+
rows = self.cursor().execute__fetch_all(sql_query)
|
213
|
+
return self.parse_rows(rows)
|
214
|
+
|
215
|
+
def rows_add(self, records, commit=True): # todo: refactor to use row_add
|
216
|
+
for record in records:
|
217
|
+
if type(record) is dict:
|
218
|
+
self.row_add_record(record)
|
219
|
+
else:
|
220
|
+
self.row_add(row_obj=record)
|
221
|
+
if commit:
|
222
|
+
self.cursor().commit()
|
223
|
+
return self
|
224
|
+
|
225
|
+
def rows_delete_where(self, **query_conditions):
|
226
|
+
sql_query,params = self.sql_builder().command__delete_where(query_conditions)
|
227
|
+
return self.cursor().execute_and_commit(sql_query,params)
|
228
|
+
|
229
|
+
def select_row_where(self, **kwargs):
|
230
|
+
rows = self.select_rows_where(**kwargs) # execute the query
|
231
|
+
if len(rows) == 1: # only return a result if there is one row
|
232
|
+
return rows[0]
|
233
|
+
return None # return None if there no match or more than one match
|
234
|
+
|
235
|
+
def select_rows_where(self, **kwargs):
|
236
|
+
sql_query, params = self.sql_builder().query_for_select_rows_where(**kwargs)
|
237
|
+
rows = self.cursor().execute__fetch_all(sql_query, params) # Execute the query and return the results
|
238
|
+
return self.parse_rows(rows)
|
239
|
+
|
240
|
+
def select_rows_where_one(self, **kwargs):
|
241
|
+
sql_query, params = self.sql_builder().query_for_select_rows_where(**kwargs)
|
242
|
+
row = self.cursor().execute__fetch_one(sql_query, params) # Execute the query and return the results
|
243
|
+
return self.parse_row(row)
|
244
|
+
|
245
|
+
def select_field_values(self, field_name):
|
246
|
+
if field_name not in self.fields__cached():
|
247
|
+
raise ValueError(f'in select_all_vales_from_field, the provide field_name "{field_name}" does not exist in the current table "{self.table_name}"')
|
248
|
+
|
249
|
+
sql_query = self.sql_builder().query_for_fields([field_name])
|
250
|
+
rows = self.cursor().execute__fetch_all(sql_query) # Execute the SQL query and get all rows
|
251
|
+
all_rows = self.parse_rows(rows)
|
252
|
+
all_values = [row[field_name] for row in all_rows] # Extract the desired field from each row in the result set
|
253
|
+
return all_values
|
254
|
+
|
255
|
+
@index_by
|
256
|
+
def schema(self):
|
257
|
+
return self.cursor().table_schema(self.table_name)
|
258
|
+
|
259
|
+
@filter_list
|
260
|
+
def schema__by_name_type(self):
|
261
|
+
return {item.get('name'): item.get('type') for item in self.schema()}
|
262
|
+
|
263
|
+
def size(self):
|
264
|
+
sql_query = self.sql_builder().query_for_size()
|
265
|
+
result = self.cursor().execute__fetch_one(sql_query)
|
266
|
+
return result.get('size')
|
267
|
+
|
268
|
+
@cache_on_self
|
269
|
+
def sql_builder(self, limit=None):
|
270
|
+
from osbot_utils.helpers.sqlite.sql_builder.SQL_Builder import SQL_Builder
|
271
|
+
return SQL_Builder(table=self, limit=limit)
|
272
|
+
|
273
|
+
def validate_record_with_schema(self, record): # todo: refactor out to a validator class
|
274
|
+
schema = self.fields__cached()
|
275
|
+
|
276
|
+
extra_keys = [key for key in record if key not in schema] # Check for keys in record that are not in the schema
|
277
|
+
if extra_keys:
|
278
|
+
return f'Validation error: Unrecognized keys {extra_keys} in record.'
|
279
|
+
return '' # If we reach here, the record is valid
|
280
|
+
|
281
|
+
# query helpers
|
282
|
+
def contains(self, **kwargs):
|
283
|
+
result = self.where_one(**kwargs)
|
284
|
+
return result is not None
|
285
|
+
|
286
|
+
def not_contains(self, **kwargs):
|
287
|
+
return self.contains(**kwargs) is False
|
288
|
+
|
289
|
+
def where(self, **kwargs):
|
290
|
+
return self.select_rows_where(**kwargs)
|
291
|
+
|
292
|
+
def where_one(self, **kwargs):
|
293
|
+
return self.select_rows_where_one(**kwargs)
|
@@ -0,0 +1,96 @@
|
|
1
|
+
import inspect
|
2
|
+
from osbot_utils.base_classes.Kwargs_To_Self import Kwargs_To_Self
|
3
|
+
from osbot_utils.decorators.lists.filter_list import filter_list
|
4
|
+
from osbot_utils.helpers.sqlite.Sqlite__Field import Sqlite__Field
|
5
|
+
from osbot_utils.helpers.sqlite.Sqlite__Table import Sqlite__Table, DEFAULT_FIELD_NAME__ID
|
6
|
+
from osbot_utils.helpers.sqlite.models.Sqlite__Field__Type import Sqlite__Field__Type
|
7
|
+
|
8
|
+
class Sqlite__Table__Create(Kwargs_To_Self):
|
9
|
+
fields : list[Sqlite__Field]
|
10
|
+
table : Sqlite__Table
|
11
|
+
|
12
|
+
def __init__(self, table_name):
|
13
|
+
super().__init__()
|
14
|
+
self.table.table_name = table_name
|
15
|
+
self.set_default_fields()
|
16
|
+
|
17
|
+
def add_field(self, field_data: dict):
|
18
|
+
sqlite_field = Sqlite__Field.from_json(field_data)
|
19
|
+
if sqlite_field:
|
20
|
+
self.fields.append(sqlite_field)
|
21
|
+
return True
|
22
|
+
return False
|
23
|
+
|
24
|
+
def add_fields(self, fields_data:list[dict]):
|
25
|
+
results = []
|
26
|
+
if fields_data:
|
27
|
+
for field_data in fields_data:
|
28
|
+
results.append(self.add_field(field_data))
|
29
|
+
return results
|
30
|
+
|
31
|
+
def add_fields_from_class(self, table_class):
|
32
|
+
if inspect.isclass(table_class):
|
33
|
+
for field_name, field_type in table_class.__annotations__.items():
|
34
|
+
self.add_field_with_type(field_name, field_type)
|
35
|
+
return self
|
36
|
+
|
37
|
+
def add_field_with_type(self, field_name, field_type):
|
38
|
+
if inspect.isclass(field_type):
|
39
|
+
field_type = Sqlite__Field__Type.type_map().get(field_type)
|
40
|
+
|
41
|
+
return self.add_field(dict(name=field_name, type=field_type))
|
42
|
+
|
43
|
+
def add_field__text(self, field_name):
|
44
|
+
return self.add_field_with_type(field_name=field_name, field_type=str)
|
45
|
+
|
46
|
+
def add_fields__text(self, *fields_name):
|
47
|
+
for field_name in fields_name:
|
48
|
+
self.add_field__text(field_name=field_name)
|
49
|
+
return self
|
50
|
+
|
51
|
+
def create_table(self):
|
52
|
+
sql_query = self.sql_for__create_table()
|
53
|
+
if self.table.not_exists():
|
54
|
+
self.table.cursor().execute(sql_query)
|
55
|
+
return self.table.exists()
|
56
|
+
return False
|
57
|
+
|
58
|
+
def create_table__from_row_schema(self, row_schema): # todo add check if there is an index field (which is now supported since it clashes with the one that is added by default)
|
59
|
+
self.add_fields_from_class(row_schema)
|
60
|
+
self.table.row_schema = row_schema
|
61
|
+
return self.create_table()
|
62
|
+
|
63
|
+
@filter_list
|
64
|
+
def fields_json(self):
|
65
|
+
return [field.json() for field in self.fields]
|
66
|
+
|
67
|
+
def fields__by_name_type(self):
|
68
|
+
return { item.get('name'): item.get('type') for item in self.fields_json() }
|
69
|
+
|
70
|
+
def fields_reset(self):
|
71
|
+
self.fields = []
|
72
|
+
return self
|
73
|
+
|
74
|
+
def database(self):
|
75
|
+
return self.table.database
|
76
|
+
|
77
|
+
def set_default_fields(self):
|
78
|
+
self.add_field(dict(name=DEFAULT_FIELD_NAME__ID, type="INTEGER", pk=True)) # by default every table has an id field
|
79
|
+
return self
|
80
|
+
|
81
|
+
def sql_for__create_table(self):
|
82
|
+
field_definitions = [field.text_for_create_table() for field in self.fields]
|
83
|
+
primary_keys = [field.name for field in self.fields if field.pk]
|
84
|
+
foreign_keys_constraints = [field.text_for_create_table() for field in self.fields if field.is_foreign_key]
|
85
|
+
|
86
|
+
# Handling composite primary keys if necessary
|
87
|
+
if len(primary_keys) > 1:
|
88
|
+
pk_constraint = f"PRIMARY KEY ({', '.join(primary_keys)})"
|
89
|
+
field_definitions.append(pk_constraint)
|
90
|
+
|
91
|
+
# Adding foreign key constraints separately if there are any
|
92
|
+
if foreign_keys_constraints:
|
93
|
+
field_definitions.extend(foreign_keys_constraints)
|
94
|
+
|
95
|
+
table_definition = f"CREATE TABLE {self.table.table_name} ({', '.join(field_definitions)});"
|
96
|
+
return table_definition
|
@@ -0,0 +1,17 @@
|
|
1
|
+
from osbot_utils.base_classes.Kwargs_To_Self import Kwargs_To_Self
|
2
|
+
from osbot_utils.helpers.sqlite.Sqlite__Database import Sqlite__Database, SQLITE_DATABASE_PATH__IN_MEMORY
|
3
|
+
|
4
|
+
|
5
|
+
class Temp_Sqlite__Database__Disk(Kwargs_To_Self):
|
6
|
+
database : Sqlite__Database
|
7
|
+
|
8
|
+
def __init__(self):
|
9
|
+
super().__init__()
|
10
|
+
self.database.in_memory = False
|
11
|
+
|
12
|
+
def __enter__(self):
|
13
|
+
self.database.connect()
|
14
|
+
return self.database
|
15
|
+
|
16
|
+
def __exit__(self, exc_type, exc_val, exc_tb):
|
17
|
+
self.database.delete()
|
@@ -0,0 +1,23 @@
|
|
1
|
+
from osbot_utils.base_classes.Kwargs_To_Self import Kwargs_To_Self
|
2
|
+
from osbot_utils.helpers.sqlite.Sqlite__Database import Sqlite__Database
|
3
|
+
from osbot_utils.helpers.sqlite.Sqlite__Table import Sqlite__Table
|
4
|
+
from osbot_utils.utils.Dev import pprint
|
5
|
+
from osbot_utils.utils.Misc import random_text
|
6
|
+
|
7
|
+
|
8
|
+
class Temp_Sqlite__Table(Kwargs_To_Self):
|
9
|
+
table_name : str = random_text(prefix='random_table')
|
10
|
+
table_fields : list
|
11
|
+
table : Sqlite__Table
|
12
|
+
|
13
|
+
|
14
|
+
def __init__(self):
|
15
|
+
super().__init__()
|
16
|
+
self.table.table_name = self.table_name
|
17
|
+
|
18
|
+
def __enter__(self):
|
19
|
+
self.table.create()
|
20
|
+
return self.table
|
21
|
+
|
22
|
+
def __exit__(self, exc_type, exc_val, exc_tb):
|
23
|
+
self.table.delete()
|
File without changes
|
@@ -0,0 +1,214 @@
|
|
1
|
+
import types
|
2
|
+
from osbot_utils.base_classes.Kwargs_To_Self import Kwargs_To_Self
|
3
|
+
from osbot_utils.helpers.sqlite.domains.Sqlite__DB__Requests import Sqlite__DB__Requests
|
4
|
+
from osbot_utils.utils.Json import json_dumps, json_loads
|
5
|
+
from osbot_utils.utils.Misc import str_sha256, timestamp_utc_now
|
6
|
+
from osbot_utils.utils.Objects import pickle_save_to_bytes, pickle_load_from_bytes
|
7
|
+
|
8
|
+
|
9
|
+
class Sqlite__Cache__Requests(Kwargs_To_Self):
|
10
|
+
add_timestamp : bool = True
|
11
|
+
enabled : bool = True
|
12
|
+
update_mode : bool = False
|
13
|
+
cache_only_mode : bool = False
|
14
|
+
sqlite_requests : Sqlite__DB__Requests = None
|
15
|
+
pickle_response : bool = False
|
16
|
+
capture_exceptions: bool = False # once this is working, it might be more useful to have this set to true
|
17
|
+
exception_classes : list
|
18
|
+
on_invoke_target : types.FunctionType
|
19
|
+
|
20
|
+
def __init__(self, db_path=None, db_name=None, table_name=None):
|
21
|
+
self.sqlite_requests = Sqlite__DB__Requests(db_path=db_path, db_name=db_name, table_name=table_name)
|
22
|
+
super().__init__()
|
23
|
+
|
24
|
+
def cache_add(self, request_data, response_data):
|
25
|
+
new_row_obj = self.create_new_cache_obj(request_data, response_data)
|
26
|
+
return self.cache_table().row_add_and_commit(new_row_obj)
|
27
|
+
|
28
|
+
def cache_delete(self, request_data):
|
29
|
+
request_data = json_dumps(request_data)
|
30
|
+
request_data_sha256 = str_sha256(request_data)
|
31
|
+
return self.cache_table().rows_delete_where(request_hash=request_data_sha256)
|
32
|
+
|
33
|
+
def cache_entries(self):
|
34
|
+
return self.cache_table().rows()
|
35
|
+
|
36
|
+
def cache_entry(self, request_data):
|
37
|
+
request_data = json_dumps(request_data)
|
38
|
+
request_data_sha256 = str_sha256(request_data)
|
39
|
+
data = self.cache_table().select_rows_where(request_hash=request_data_sha256)
|
40
|
+
if len(data) > 0: # todo: add logic to handle (or log), where there are multiple entries with the same hash
|
41
|
+
return data[0]
|
42
|
+
return {}
|
43
|
+
|
44
|
+
def cache_entry_comments(self, model_id, body):
|
45
|
+
cache_entry = self.cache_entry_for_request_params(model_id=model_id, body=body)
|
46
|
+
return cache_entry.get('comments')
|
47
|
+
|
48
|
+
def cache_entry_comments_update(self, model_id, body, new_comments):
|
49
|
+
cache_entry = self.cache_entry_for_request_params(model_id=model_id, body=body)
|
50
|
+
request_hash = cache_entry.get('request_hash')
|
51
|
+
update_fields = dict(comments=new_comments)
|
52
|
+
query_conditions = dict(request_hash=request_hash)
|
53
|
+
result = self.cache_table().row_update(update_fields, query_conditions)
|
54
|
+
return result
|
55
|
+
|
56
|
+
def cache_entry_for_request_params(self, *args, **target_kwargs):
|
57
|
+
request_data = self.cache_request_data(*args, **target_kwargs)
|
58
|
+
return self.cache_entry(request_data)
|
59
|
+
|
60
|
+
def create_new_cache_data(self, request_data, response_data):
|
61
|
+
request_data_json = json_dumps(request_data)
|
62
|
+
request_data_hash = str_sha256(request_data_json)
|
63
|
+
if self.add_timestamp:
|
64
|
+
timestamp = timestamp_utc_now()
|
65
|
+
else:
|
66
|
+
timestamp = 0
|
67
|
+
cache_cata = dict(request_data = request_data_json ,
|
68
|
+
request_hash = request_data_hash ,
|
69
|
+
response_bytes = b'' ,
|
70
|
+
response_data = '' ,
|
71
|
+
response_hash = '' ,
|
72
|
+
timestamp = timestamp )
|
73
|
+
if self.pickle_response:
|
74
|
+
cache_cata['response_bytes'] = response_data
|
75
|
+
else:
|
76
|
+
response_data_json = json_dumps(response_data)
|
77
|
+
response_data_hash = str_sha256(response_data_json)
|
78
|
+
cache_cata['response_data'] = response_data_json
|
79
|
+
cache_cata['response_hash'] = response_data_hash
|
80
|
+
return cache_cata
|
81
|
+
|
82
|
+
def create_new_cache_obj(self, request_data, response_data):
|
83
|
+
new_row_data = self.create_new_cache_data(request_data, response_data)
|
84
|
+
new_row_obj = self.cache_table().new_row_obj(new_row_data)
|
85
|
+
return new_row_obj
|
86
|
+
|
87
|
+
def cache_table(self):
|
88
|
+
return self.sqlite_requests.table_requests()
|
89
|
+
|
90
|
+
def cache_table__clear(self):
|
91
|
+
return self.cache_table().clear()
|
92
|
+
|
93
|
+
def cache_request_data(self, *args, **target_kwargs):
|
94
|
+
return {'args': list(args), 'kwargs': target_kwargs} # convert the args tuple to a list since that is what it will be once it is serialised
|
95
|
+
|
96
|
+
|
97
|
+
def delete_where_request_data(self, request_data): # todo: check if it is ok to use the request_data as a query target, or if we should use the request_hash variable
|
98
|
+
if type(request_data) is dict: # if we get an request_data obj
|
99
|
+
request_data = json_dumps(request_data) # convert it to the json dump
|
100
|
+
if type(request_data) is str: # make sure we have a string
|
101
|
+
if len(self.rows_where__request_data(request_data)) > 0: # make sure there is at least one entry to delete
|
102
|
+
self.cache_table().rows_delete_where(request_data=request_data) # delete it
|
103
|
+
return len(self.rows_where__request_data(request_data)) == 0 # confirm it was deleted
|
104
|
+
return False # if anything was not right, return False
|
105
|
+
|
106
|
+
def disable(self):
|
107
|
+
self.enabled = False
|
108
|
+
return self
|
109
|
+
|
110
|
+
def enable(self):
|
111
|
+
self.enabled = True
|
112
|
+
return self
|
113
|
+
|
114
|
+
def invoke(self, target, target_args, target_kwargs):
|
115
|
+
return self.invoke_with_cache(target, target_args, target_kwargs)
|
116
|
+
|
117
|
+
def invoke_target(self, target, target_args, target_kwargs):
|
118
|
+
if self.on_invoke_target:
|
119
|
+
raw_response = self.on_invoke_target(target, target_args, target_kwargs)
|
120
|
+
else:
|
121
|
+
raw_response = target(*target_args, **target_kwargs)
|
122
|
+
return self.transform_raw_response(raw_response)
|
123
|
+
|
124
|
+
def invoke_with_cache(self, target, target_args, target_kwargs, request_data=None):
|
125
|
+
if self.enabled is False:
|
126
|
+
if self.cache_only_mode:
|
127
|
+
return None
|
128
|
+
return self.invoke_target(target, target_args, target_kwargs)
|
129
|
+
if request_data is None:
|
130
|
+
request_data = self.cache_request_data(*target_args, **target_kwargs)
|
131
|
+
cache_entry = self.cache_entry(request_data)
|
132
|
+
if cache_entry:
|
133
|
+
if self.update_mode is True:
|
134
|
+
self.cache_delete(request_data)
|
135
|
+
else:
|
136
|
+
return self.response_data_deserialize(cache_entry)
|
137
|
+
if self.cache_only_mode is False:
|
138
|
+
return self.invoke_target__and_add_to_cache(request_data, target, target_args, target_kwargs)
|
139
|
+
|
140
|
+
|
141
|
+
def invoke_target__and_add_to_cache(self,request_data, target, target_args, target_kwargs):
|
142
|
+
try:
|
143
|
+
response_data_obj = self.invoke_target(target, target_args, target_kwargs)
|
144
|
+
response_data = self.response_data_serialize(response_data_obj)
|
145
|
+
self.cache_add(request_data=request_data, response_data=response_data)
|
146
|
+
return response_data_obj
|
147
|
+
except Exception as exception:
|
148
|
+
if self.capture_exceptions:
|
149
|
+
response_data = self.response_data_serialize(exception)
|
150
|
+
self.cache_add(request_data=request_data, response_data=response_data)
|
151
|
+
raise exception
|
152
|
+
|
153
|
+
def only_from_cache(self, value=True):
|
154
|
+
self.cache_only_mode = value
|
155
|
+
return self
|
156
|
+
|
157
|
+
def response_data_deserialize(self, cache_entry):
|
158
|
+
if self.pickle_response:
|
159
|
+
response_bytes = cache_entry.get('response_bytes')
|
160
|
+
response_data_obj = pickle_load_from_bytes(response_bytes)
|
161
|
+
else:
|
162
|
+
response_data = cache_entry.get('response_data')
|
163
|
+
response_data_obj = json_loads(response_data)
|
164
|
+
if self.capture_exceptions:
|
165
|
+
if (type(response_data_obj) is Exception or # raise if it is an exception
|
166
|
+
type(response_data_obj) in self.exception_classes): # or if one of the types that have been set as being exception classes
|
167
|
+
raise response_data_obj
|
168
|
+
# else:
|
169
|
+
# pprint(type(response_data_obj))
|
170
|
+
return response_data_obj
|
171
|
+
|
172
|
+
def response_data_serialize(self, response_data):
|
173
|
+
if self.pickle_response:
|
174
|
+
return pickle_save_to_bytes(response_data)
|
175
|
+
return response_data
|
176
|
+
|
177
|
+
def response_data_for__request_hash(self, request_hash):
|
178
|
+
rows = self.rows_where__request_hash(request_hash)
|
179
|
+
if len(rows) > 0:
|
180
|
+
cache_entry = rows[0]
|
181
|
+
response_data_obj = self.response_data_deserialize(cache_entry)
|
182
|
+
return response_data_obj
|
183
|
+
return {}
|
184
|
+
|
185
|
+
def requests_data__all(self):
|
186
|
+
requests_data = []
|
187
|
+
for row in self.cache_table().rows():
|
188
|
+
req_id = row.get('id')
|
189
|
+
request_data = row.get('request_data')
|
190
|
+
request_hash = row.get('request_hash')
|
191
|
+
request_comments = row.get('comments')
|
192
|
+
request_data_obj = json_loads(request_data)
|
193
|
+
request_data_obj['_id' ] = req_id
|
194
|
+
request_data_obj['_hash' ] = request_hash
|
195
|
+
request_data_obj['_comments'] = request_comments
|
196
|
+
|
197
|
+
requests_data.append(request_data_obj)
|
198
|
+
return requests_data
|
199
|
+
|
200
|
+
def rows_where(self, **kwargs):
|
201
|
+
return self.cache_table().select_rows_where(**kwargs)
|
202
|
+
|
203
|
+
def rows_where__request_data(self, request_data):
|
204
|
+
return self.rows_where(request_data=request_data)
|
205
|
+
|
206
|
+
def rows_where__request_hash(self, request_hash):
|
207
|
+
return self.rows_where(request_hash=request_hash)
|
208
|
+
|
209
|
+
def transform_raw_response(self, raw_response):
|
210
|
+
return raw_response
|
211
|
+
|
212
|
+
def update(self, value=True):
|
213
|
+
self.update_mode = value
|
214
|
+
return self
|