24
24
+------------------+---------------+------------------------+
25
25
"""
26
26
27
+ from .utils .files import file_lock , file_unlock , identifier_name_test , file_name_wash
27
28
import os
28
29
import json
29
30
import re
32
33
import shutil
33
34
import time
34
35
35
- from .utils .files import file_lock , file_unlock , identifier_name_test , file_name_wash
36
+ import logging
37
+
38
+ logging .basicConfig (
39
+ format = "%(levelname)s: %(message)s" ,
40
+ )
41
+
36
42
37
43
# TODO: new binary json format
38
44
# _FORMAT_BSON = 0x09 #https://en.wikipedia.org/wiki/BSON
43
49
44
50
45
51
class Rocketstore :
46
-
47
52
# Constants
48
53
_ORDER = 0x01 # Sort ASC
49
54
_ORDER_DESC = 0x02 # Sort DESC
@@ -73,18 +78,28 @@ def __init__(self, **set_option) -> None:
73
78
self .options (** set_option )
74
79
75
80
def options (self , ** options ) -> None :
76
- '''
81
+ """
77
82
inital setup of Rocketstore
78
83
@Sample:
79
84
from Rocketstore import Rocketstore
80
85
rs.options(**{
81
86
"data_storage_area": "./",
82
87
"data_format": Rocketstore._FORMAT_NATIVE
83
88
})
84
- '''
89
+ """
90
+
91
+ if "debug" in options and isinstance (options ["debug" ], bool ):
92
+ if options ["debug" ] == True :
93
+ logging .getLogger ().setLevel (logging .DEBUG )
94
+ else :
95
+ logging .getLogger ().setLevel (logging .ERROR )
85
96
86
97
if "data_format" in options :
87
- if options ["data_format" ] in [self ._FORMAT_JSON , self ._FORMAT_XML , self ._FORMAT_NATIVE ]:
98
+ if options ["data_format" ] in [
99
+ self ._FORMAT_JSON ,
100
+ self ._FORMAT_XML ,
101
+ self ._FORMAT_NATIVE ,
102
+ ]:
88
103
self .data_format = options .get (
89
104
"data_format" , self ._FORMAT_JSON )
90
105
else :
@@ -95,23 +110,29 @@ def options(self, **options) -> None:
95
110
if isinstance (options .get ("data_storage_area" ), str ):
96
111
self .data_storage_area = options ["data_storage_area" ]
97
112
try :
98
- os .makedirs (os .path .abspath (self .data_storage_area ),
99
- mode = 0o775 , exist_ok = True )
113
+ os .makedirs (
114
+ os .path .abspath (self .data_storage_area ),
115
+ mode = 0o775 ,
116
+ exist_ok = True ,
117
+ )
100
118
except OSError as e :
101
119
if e .errno != errno .EEXIST :
102
120
raise Exception (
103
- f"Unable to create data storage directory '{ self .data_storage_area } ': { e } " )
121
+ f"Unable to create data storage directory '{ self .data_storage_area } ': { e } "
122
+ )
104
123
else :
105
124
raise ValueError ("Data storage area must be a directory path" )
106
125
107
- if "lock_retry_interval" in options and isinstance (options ["lock_retry_interval" ], int ):
126
+ if "lock_retry_interval" in options and isinstance (
127
+ options ["lock_retry_interval" ], int
128
+ ):
108
129
self .lock_retry_interval = options .get ("lock_retry_interval" , 13 )
109
130
110
131
if "lock_files" in options and isinstance (options ["lock_files" ], bool ):
111
132
self .lock_files = options .get ("lock_files" , True )
112
133
113
134
def post (self , collection = None , key = None , record = None , flags = 0 ) -> any :
114
- '''
135
+ """
115
136
Post a data record (Insert or overwrite)
116
137
If keyCache exists for the given collection, entries are added.
117
138
@collection: collection name
@@ -123,20 +144,19 @@ def post(self, collection=None, key=None, record=None, flags=0) -> any:
123
144
{'key': '6-test-1', 'count': 1}
124
145
_ADD_GUID: add Globally Unique IDentifier to key
125
146
{'key': '5e675199-7680-4000-856b--test-1', 'count': 1}
126
- '''
147
+ """
127
148
collection = str (collection or "" ) if collection else ""
128
149
129
150
if len (collection ) < 1 or not collection or collection == "" :
130
151
raise ValueError ("No valid collection name given" )
131
152
132
153
if identifier_name_test (collection ) == False :
133
- raise ValueError (
134
- "Collection name contains illegal characters" )
154
+ raise ValueError ("Collection name contains illegal characters" )
135
155
136
156
# Remove wildcards (unix only)
137
157
if isinstance (key , int ):
138
158
key = file_name_wash (
139
- str (key )+ "" ).replace (r"[\*\?]" , "" ) if key else ""
159
+ str (key ) + "" ).replace (r"[\*\?]" , "" ) if key else ""
140
160
141
161
flags = flags if isinstance (flags , int ) else 0
142
162
@@ -168,35 +188,44 @@ def post(self, collection=None, key=None, record=None, flags=0) -> any:
168
188
raise ValueError ("Sorry, that data format is not supported" )
169
189
170
190
# Store key in cash
171
- if isinstance (self .key_cache .get (collection ), list ) and key not in self .key_cache [collection ]:
191
+ if (
192
+ isinstance (self .key_cache .get (collection ), list )
193
+ and key not in self .key_cache [collection ]
194
+ ):
172
195
self .key_cache [collection ].append (key )
173
196
174
197
return {"key" : key , "count" : 1 }
175
198
176
- def get (self , collection = None , key = None , flags = 0 , min_time = None , max_time = None ) -> any :
177
- '''
178
- * Get one or more records or list all collections (or delete it)
199
+ def get (
200
+ self , collection = None , key = None , flags = 0 , min_time = None , max_time = None
201
+ ) -> any :
202
+ """
203
+ * Get one or more records or list all collections (or delete it)
179
204
180
- Generate a list:
181
- get collection key => list of one key
182
- get collection key wildcard => read to cash, filter to list
183
- get collections => no collection + key wildcard => read (no cashing), filter to list.
205
+ Generate a list:
206
+ get collection key => list of one key
207
+ get collection key wildcard => read to cash, filter to list
208
+ get collections => no collection + key wildcard => read (no cashing), filter to list.
184
209
185
- Cashing:
186
- Whenever readdir is called, keys are stores in keyCache, pr. collection.
187
- The keyCache is maintained whenever a record is deleted or added.
188
- One exception are searches in the root (list of collections etc.), which must be read each time.
210
+ Cashing:
211
+ Whenever readdir is called, keys are stores in keyCache, pr. collection.
212
+ The keyCache is maintained whenever a record is deleted or added.
213
+ One exception are searches in the root (list of collections etc.), which must be read each time.
189
214
190
- NB: Files may have been removed manually and should be removed from the cache
191
- '''
215
+ NB: Files may have been removed manually and should be removed from the cache
216
+ """
192
217
keys = []
193
218
uncache = []
194
219
records = []
195
220
count = 0
196
221
197
222
collection = str (collection or "" ) if collection else ""
198
223
199
- if collection and len (collection ) > 0 and identifier_name_test (collection ) == False :
224
+ if (
225
+ collection
226
+ and len (collection ) > 0
227
+ and identifier_name_test (collection ) == False
228
+ ):
200
229
raise ValueError ("Collection name contains illegal characters" )
201
230
202
231
# Check key validity
@@ -219,7 +248,11 @@ def get(self, collection=None, key=None, flags=0, min_time=None, max_time=None)
219
248
try :
220
249
_list = os .listdir (scan_dir )
221
250
222
- # Update cahce
251
+ # Remove .DS_Store files
252
+ _list = [
253
+ e for e in _list if not e .lower ().endswith (".ds_store" )]
254
+
255
+ # Update cache
223
256
if collection and len (_list ) > 0 :
224
257
self .key_cache [collection ] = _list
225
258
except FileNotFoundError as f :
@@ -233,62 +266,101 @@ def get(self, collection=None, key=None, flags=0, min_time=None, max_time=None)
233
266
234
267
# Wildcard search
235
268
if key and key != "*" :
236
- haystack = self .key_cache [collection ] if collection in self .key_cache else _list
269
+ haystack = (
270
+ self .key_cache [collection ]
271
+ if collection in self .key_cache
272
+ else _list
273
+ )
237
274
keys = [k for k in haystack if glob .fnmatch .fnmatch (k , key )]
238
275
else :
239
276
keys = _list
240
277
241
278
# Order by key value
242
- if flags & (self ._ORDER | self ._ORDER_DESC ) and keys and len (keys ) > 1 and not (flags & (self ._DELETE | (flags & self ._COUNT ))):
279
+ if (
280
+ flags & (self ._ORDER | self ._ORDER_DESC )
281
+ and keys
282
+ and len (keys ) > 1
283
+ and not (flags & (self ._DELETE | (flags & self ._COUNT )))
284
+ ):
243
285
keys .sort ()
244
286
if flags & self ._ORDER_DESC :
245
287
keys .reverse ()
246
288
else :
247
- if collection and isinstance (self .key_cache .get (collection ), list ) and key not in self .key_cache [collection ]:
289
+ if (
290
+ collection
291
+ and isinstance (self .key_cache .get (collection ), list )
292
+ and key not in self .key_cache [collection ]
293
+ ):
248
294
keys = []
249
295
elif key :
250
296
keys = [key ]
251
297
252
298
count = len (keys )
253
299
254
- if len (keys ) > 0 and collection and not (flags & (self ._KEYS | self ._COUNT | self ._DELETE )):
300
+ if (
301
+ len (keys ) > 0
302
+ and collection
303
+ and not (flags & (self ._KEYS | self ._COUNT | self ._DELETE ))
304
+ ):
255
305
records = [None ] * len (keys )
256
306
257
307
for i in range (len (keys )):
258
308
file_name = os .path .join (scan_dir , keys [i ])
259
309
310
+ # Skip .DS_Store files
311
+ if not file_name .lower ().endswith (".ds_store" ):
312
+ continue
313
+
260
314
# Read JSON record file
261
315
if self .data_format & self ._FORMAT_JSON :
262
316
try :
263
- with open (file_name , 'r' ) as file :
317
+ with open (file_name , "r" ) as file :
318
+ logging .info (f">[269] File open { file_name } " )
264
319
records [i ] = json .load (file )
265
320
except FileNotFoundError :
266
321
uncache .append (keys [i ])
267
322
records [i ] = "*deleted*"
268
323
count -= 1
324
+ logging .warning (f">[269] File not found{ file_name } " )
269
325
except json .JSONDecodeError :
270
- records [i ] = ""
326
+ records [i ] = "*format*"
327
+ logging .warning (f">[272] Not JSON format { file_name } " )
271
328
else :
272
329
raise ValueError (
273
330
"Sorry, that data format is not supported" )
274
331
275
332
elif flags & self ._DELETE :
276
333
# DELETE RECORDS
277
- print (f"276 DELETE: c({ collection } ) k({ key } )" )
278
-
279
- if not collection and not key or collection == "" and not key or collection == "" and key == "" :
280
- print ("# Delete database (all collections) return count 1" )
334
+ logging .info (f"276 DELETE: c({ collection } ) k({ key } )" )
335
+
336
+ if (
337
+ not collection
338
+ and not key
339
+ or collection == ""
340
+ and not key
341
+ or collection == ""
342
+ and key == ""
343
+ ):
344
+ logging .info (
345
+ "# Delete database (all collections) return count 1" )
281
346
try :
282
347
if os .path .exists (self .data_storage_area ):
283
348
shutil .rmtree (self .data_storage_area )
284
349
self .key_cache = {}
285
350
count = 1
286
351
except Exception as e :
287
- print (f"Error deleting directory: { e } " )
352
+ logging . info (f"Error deleting directory: { e } " )
288
353
raise e
289
354
290
- elif collection and not key or collection == "" and not key or collection and key == "" :
291
- print ("# Delete complete collection" )
355
+ elif (
356
+ collection
357
+ and not key
358
+ or collection == ""
359
+ and not key
360
+ or collection
361
+ and key == ""
362
+ ):
363
+ logging .info ("# Delete complete collection" )
292
364
fileName = os .path .join (self .data_storage_area , collection )
293
365
fileNameSeq = os .path .join (
294
366
self .data_storage_area , f"{ collection } _seq" )
@@ -314,7 +386,7 @@ def get(self, collection=None, key=None, flags=0, min_time=None, max_time=None)
314
386
315
387
# Delete records and ( collection and sequences found with wildcards )
316
388
elif keys :
317
- print ("delete wildcat" )
389
+ logging . info ("delete wildcat" )
318
390
for key in keys :
319
391
# Remove files with regexp
320
392
if "*" in key or "?" in key :
@@ -332,8 +404,8 @@ def get(self, collection=None, key=None, flags=0, min_time=None, max_time=None)
332
404
else :
333
405
uncache .extend ([key ])
334
406
335
- elif re .search (r' [\*\?]' , key ):
336
- print ("WILD con caracteres especiales" )
407
+ elif re .search (r" [\*\?]" , key ):
408
+ logging . info ("WILD con caracteres especiales" )
337
409
fileNamesWild = glob .glob (os .path .join (scan_dir , key ))
338
410
for file in fileNamesWild :
339
411
os .remove (file )
@@ -343,32 +415,34 @@ def get(self, collection=None, key=None, flags=0, min_time=None, max_time=None)
343
415
if uncache :
344
416
if collection in self .key_cache :
345
417
self .key_cache [collection ] = [
346
- e for e in self .key_cache [collection ] if e not in uncache ]
418
+ e for e in self .key_cache [collection ] if e not in uncache
419
+ ]
347
420
348
421
if keys != self .key_cache .get (collection ):
349
422
keys = [e for e in keys if e not in uncache ]
350
423
351
424
if records :
352
- records = [e for e in records if e != "*deleted*" ]
425
+ records = [e for e in records if e !=
426
+ "*deleted*" or e != "*format*" ]
353
427
354
- result = {' count' : count }
355
- if result [' count' ] and keys and not (flags & (self ._COUNT | self ._DELETE )):
356
- result [' key' ] = keys
428
+ result = {" count" : count }
429
+ if result [" count" ] and keys and not (flags & (self ._COUNT | self ._DELETE )):
430
+ result [" key" ] = keys
357
431
if records :
358
- result [' result' ] = records
432
+ result [" result" ] = records
359
433
360
434
return result
361
435
362
436
def delete (self , collection = None , key = None ):
363
- '''
437
+ """
364
438
Delete one or more records or collections
365
- '''
439
+ """
366
440
return self .get (collection = collection , key = key , flags = self ._DELETE )
367
441
368
442
def sequence (self , seq_name : str ) -> int :
369
- '''
443
+ """
370
444
Get and auto incremented sequence or create it
371
- '''
445
+ """
372
446
if not seq_name :
373
447
raise ValueError ("Sequence name is invalid" )
374
448
@@ -400,10 +474,10 @@ def sequence(self, seq_name: str) -> int:
400
474
file .write ("1" )
401
475
sequence = 1
402
476
except Exception as e :
403
- print (f"Error creating file: { e } " )
477
+ logging . warning (f"Error creating file: { e } " )
404
478
raise e
405
479
except Exception as e :
406
- print (f"Error reading/writing file: { e } " )
480
+ logging . warning (f"Error reading/writing file: { e } " )
407
481
raise e
408
482
409
483
if self .lock_files :
0 commit comments