1
+ from collections import defaultdict
2
+
3
+ from app .log import log_err
4
+
5
+
6
+ class Directory (object ):
7
+ """ This class stores values and notifies callbacks which were registered to be executed as soon
8
+ as some value is changed. This class works as DB cache mostly """
9
+ def __init__ (self ):
10
+ self .data = defaultdict (dict ) # storage. A key is a slot name, a value is a dictionary with data
11
+ self .notify = defaultdict (lambda : defaultdict (list )) # registered callbacks: slot -> path -> handlers[]
12
+
13
+ @staticmethod
14
+ def get_slot_name (db , table ):
15
+ """ Convert db, table pair into a slot name """
16
+ return db + "__" + table
17
+
18
+ def path_traverse (self , slot , path ):
19
+ """
20
+ Traverse a path in the storage.
21
+ If the path is an empty string, it returns a value as it is.
22
+ If the path is not an empty string, the method will traverse through the dictionary value.
23
+ Example:
24
+ self.data["key_1"] = { "abc": { "cde": { "fgh": "val_1", "ijk": "val_2" } } }
25
+ self.path_traverse("key_1", "abc/cde") will return True, { "fgh": "val_1", "ijk": "val_2" }
26
+ :param slot: storage key
27
+ :param path: storage path as a string where each internal key is separated by '/'
28
+ :return: a pair: True if the path was found, object if it was found
29
+ """
30
+ if slot not in self .data :
31
+ return False , None
32
+ elif path == '' :
33
+ return True , self .data [slot ]
34
+ d = self .data [slot ]
35
+ for p in path .split ("/" ):
36
+ if p not in d :
37
+ return False , None
38
+ d = d [p ]
39
+ return True , d
40
+
41
+ def path_exist (self , db , table , path ):
42
+ """
43
+ Check if the path exists in the storage
44
+ :param db: db name
45
+ :param table: table name
46
+ :param path: requested path
47
+ :return: True if the path is available, False otherwise
48
+ """
49
+ slot = self .get_slot_name (db , table )
50
+ return self .path_traverse (slot , path )[0 ]
51
+
52
+ def get_path (self , db , table , path ):
53
+ """
54
+ Return the requested path from the storage
55
+ :param db: db name
56
+ :param table: table name
57
+ :param path: requested path
58
+ :return: object if the path was found, None otherwise
59
+ """
60
+ slot = self .get_slot_name (db , table )
61
+ return self .path_traverse (slot , path )[1 ]
62
+
63
+ def put (self , db , table , key , value ):
64
+ """
65
+ Put information into the storage. Notify handlers which are dependant to the information
66
+ :param db: db name
67
+ :param table: table name
68
+ :param key: key to change
69
+ :param value: value to put
70
+ :return:
71
+ """
72
+ slot = self .get_slot_name (db , table )
73
+ self .data [slot ][key ] = value
74
+ if slot in self .notify :
75
+ for path in self .notify [slot ].keys ():
76
+ if self .path_exist (db , table , path ):
77
+ for handler in self .notify [slot ][path ]:
78
+ handler ()
79
+
80
+ def get (self , db , table , key ):
81
+ """
82
+ Get a value from the storage
83
+ :param db: db name
84
+ :param table: table name
85
+ :param key: ket to get
86
+ :return: value for the key
87
+ """
88
+ slot = self .get_slot_name (db , table )
89
+ return self .data [slot ][key ]
90
+
91
+ def get_slot (self , db , table ):
92
+ """
93
+ Get an object from the storage
94
+ :param db: db name
95
+ :param table: table name
96
+ :return: object for the slot
97
+ """
98
+ slot = self .get_slot_name (db , table )
99
+ return self .data [slot ]
100
+
101
+ def remove (self , db , table , key ):
102
+ """
103
+ Remove a value from the storage
104
+ :param db: db name
105
+ :param table: table name
106
+ :param key: key to remove
107
+ """
108
+ slot = self .get_slot_name (db , table )
109
+ if slot in self .data :
110
+ if key in self .data [slot ]:
111
+ del self .data [slot ][key ]
112
+ else :
113
+ log_err ("Directory: Can't remove key '%s' from slot '%s'. The key doesn't exist" % (key , slot ))
114
+ else :
115
+ log_err ("Directory: Can't remove key '%s' from slot '%s'. The slot doesn't exist" % (key , slot ))
116
+
117
+ def remove_slot (self , db , table ):
118
+ """
119
+ Remove an object from the storage
120
+ :param db: db name
121
+ :param table: table name
122
+ """
123
+ slot = self .get_slot_name (db , table )
124
+ if slot in self .data :
125
+ del self .data [slot ]
126
+ else :
127
+ log_err ("Directory: Can't remove slot '%s'. The slot doesn't exist" % slot )
128
+
129
+ def available (self , db , table ):
130
+ """
131
+ Check if the table is available
132
+ :param db: db name
133
+ :param table: table name
134
+ :return: True if the slot is available, False if not
135
+ """
136
+ slot = self .get_slot_name (db , table )
137
+ return slot in self .data
138
+
139
+ def available_deps (self , deps ):
140
+ """
141
+ Check if all items from the deps list is available in the storage
142
+ :param deps: list of dependencies
143
+ :return: True if all dependencies are presented, False otherwise
144
+ """
145
+ res = True
146
+ for db , table , path in deps :
147
+ res = res and self .path_exist (db , table , path )
148
+ return res
149
+
150
+ def subscribe (self , deps , handler ):
151
+ """
152
+ Subscribe the handler to be run as soon as all dependencies are presented
153
+ :param deps:
154
+ :param handler:
155
+ :return:
156
+ """
157
+ for db , table , path in deps :
158
+ slot = self .get_slot_name (db , table )
159
+ self .notify [slot ][path ].append (handler )
0 commit comments