Skip to content

Commit baf1413

Browse files
committed
feature: added logging
feature: added filter and skip .DS_Store files
1 parent df1bc6d commit baf1413

File tree

1 file changed

+132
-58
lines changed

1 file changed

+132
-58
lines changed

src/Rocketstore/Rocketstore.py

Lines changed: 132 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
+------------------+---------------+------------------------+
2525
"""
2626

27+
from .utils.files import file_lock, file_unlock, identifier_name_test, file_name_wash
2728
import os
2829
import json
2930
import re
@@ -32,7 +33,12 @@
3233
import shutil
3334
import time
3435

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+
3642

3743
# TODO: new binary json format
3844
# _FORMAT_BSON = 0x09 #https://en.wikipedia.org/wiki/BSON
@@ -43,7 +49,6 @@
4349

4450

4551
class Rocketstore:
46-
4752
# Constants
4853
_ORDER = 0x01 # Sort ASC
4954
_ORDER_DESC = 0x02 # Sort DESC
@@ -73,18 +78,28 @@ def __init__(self, **set_option) -> None:
7378
self.options(**set_option)
7479

7580
def options(self, **options) -> None:
76-
'''
81+
"""
7782
inital setup of Rocketstore
7883
@Sample:
7984
from Rocketstore import Rocketstore
8085
rs.options(**{
8186
"data_storage_area": "./",
8287
"data_format": Rocketstore._FORMAT_NATIVE
8388
})
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)
8596

8697
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+
]:
88103
self.data_format = options.get(
89104
"data_format", self._FORMAT_JSON)
90105
else:
@@ -95,23 +110,29 @@ def options(self, **options) -> None:
95110
if isinstance(options.get("data_storage_area"), str):
96111
self.data_storage_area = options["data_storage_area"]
97112
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+
)
100118
except OSError as e:
101119
if e.errno != errno.EEXIST:
102120
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+
)
104123
else:
105124
raise ValueError("Data storage area must be a directory path")
106125

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+
):
108129
self.lock_retry_interval = options.get("lock_retry_interval", 13)
109130

110131
if "lock_files" in options and isinstance(options["lock_files"], bool):
111132
self.lock_files = options.get("lock_files", True)
112133

113134
def post(self, collection=None, key=None, record=None, flags=0) -> any:
114-
'''
135+
"""
115136
Post a data record (Insert or overwrite)
116137
If keyCache exists for the given collection, entries are added.
117138
@collection: collection name
@@ -123,20 +144,19 @@ def post(self, collection=None, key=None, record=None, flags=0) -> any:
123144
{'key': '6-test-1', 'count': 1}
124145
_ADD_GUID: add Globally Unique IDentifier to key
125146
{'key': '5e675199-7680-4000-856b--test-1', 'count': 1}
126-
'''
147+
"""
127148
collection = str(collection or "") if collection else ""
128149

129150
if len(collection) < 1 or not collection or collection == "":
130151
raise ValueError("No valid collection name given")
131152

132153
if identifier_name_test(collection) == False:
133-
raise ValueError(
134-
"Collection name contains illegal characters")
154+
raise ValueError("Collection name contains illegal characters")
135155

136156
# Remove wildcards (unix only)
137157
if isinstance(key, int):
138158
key = file_name_wash(
139-
str(key)+"").replace(r"[\*\?]", "") if key else ""
159+
str(key) + "").replace(r"[\*\?]", "") if key else ""
140160

141161
flags = flags if isinstance(flags, int) else 0
142162

@@ -168,35 +188,44 @@ def post(self, collection=None, key=None, record=None, flags=0) -> any:
168188
raise ValueError("Sorry, that data format is not supported")
169189

170190
# 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+
):
172195
self.key_cache[collection].append(key)
173196

174197
return {"key": key, "count": 1}
175198

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)
179204
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.
184209
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.
189214
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+
"""
192217
keys = []
193218
uncache = []
194219
records = []
195220
count = 0
196221

197222
collection = str(collection or "") if collection else ""
198223

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+
):
200229
raise ValueError("Collection name contains illegal characters")
201230

202231
# Check key validity
@@ -219,7 +248,11 @@ def get(self, collection=None, key=None, flags=0, min_time=None, max_time=None)
219248
try:
220249
_list = os.listdir(scan_dir)
221250

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
223256
if collection and len(_list) > 0:
224257
self.key_cache[collection] = _list
225258
except FileNotFoundError as f:
@@ -233,62 +266,101 @@ def get(self, collection=None, key=None, flags=0, min_time=None, max_time=None)
233266

234267
# Wildcard search
235268
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+
)
237274
keys = [k for k in haystack if glob.fnmatch.fnmatch(k, key)]
238275
else:
239276
keys = _list
240277

241278
# 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+
):
243285
keys.sort()
244286
if flags & self._ORDER_DESC:
245287
keys.reverse()
246288
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+
):
248294
keys = []
249295
elif key:
250296
keys = [key]
251297

252298
count = len(keys)
253299

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+
):
255305
records = [None] * len(keys)
256306

257307
for i in range(len(keys)):
258308
file_name = os.path.join(scan_dir, keys[i])
259309

310+
# Skip .DS_Store files
311+
if not file_name.lower().endswith(".ds_store"):
312+
continue
313+
260314
# Read JSON record file
261315
if self.data_format & self._FORMAT_JSON:
262316
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}")
264319
records[i] = json.load(file)
265320
except FileNotFoundError:
266321
uncache.append(keys[i])
267322
records[i] = "*deleted*"
268323
count -= 1
324+
logging.warning(f">[269] File not found{file_name}")
269325
except json.JSONDecodeError:
270-
records[i] = ""
326+
records[i] = "*format*"
327+
logging.warning(f">[272] Not JSON format {file_name}")
271328
else:
272329
raise ValueError(
273330
"Sorry, that data format is not supported")
274331

275332
elif flags & self._DELETE:
276333
# 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")
281346
try:
282347
if os.path.exists(self.data_storage_area):
283348
shutil.rmtree(self.data_storage_area)
284349
self.key_cache = {}
285350
count = 1
286351
except Exception as e:
287-
print(f"Error deleting directory: {e}")
352+
logging.info(f"Error deleting directory: {e}")
288353
raise e
289354

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")
292364
fileName = os.path.join(self.data_storage_area, collection)
293365
fileNameSeq = os.path.join(
294366
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)
314386

315387
# Delete records and ( collection and sequences found with wildcards )
316388
elif keys:
317-
print("delete wildcat")
389+
logging.info("delete wildcat")
318390
for key in keys:
319391
# Remove files with regexp
320392
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)
332404
else:
333405
uncache.extend([key])
334406

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")
337409
fileNamesWild = glob.glob(os.path.join(scan_dir, key))
338410
for file in fileNamesWild:
339411
os.remove(file)
@@ -343,32 +415,34 @@ def get(self, collection=None, key=None, flags=0, min_time=None, max_time=None)
343415
if uncache:
344416
if collection in self.key_cache:
345417
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+
]
347420

348421
if keys != self.key_cache.get(collection):
349422
keys = [e for e in keys if e not in uncache]
350423

351424
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*"]
353427

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
357431
if records:
358-
result['result'] = records
432+
result["result"] = records
359433

360434
return result
361435

362436
def delete(self, collection=None, key=None):
363-
'''
437+
"""
364438
Delete one or more records or collections
365-
'''
439+
"""
366440
return self.get(collection=collection, key=key, flags=self._DELETE)
367441

368442
def sequence(self, seq_name: str) -> int:
369-
'''
443+
"""
370444
Get and auto incremented sequence or create it
371-
'''
445+
"""
372446
if not seq_name:
373447
raise ValueError("Sequence name is invalid")
374448

@@ -400,10 +474,10 @@ def sequence(self, seq_name: str) -> int:
400474
file.write("1")
401475
sequence = 1
402476
except Exception as e:
403-
print(f"Error creating file: {e}")
477+
logging.warning(f"Error creating file: {e}")
404478
raise e
405479
except Exception as e:
406-
print(f"Error reading/writing file: {e}")
480+
logging.warning(f"Error reading/writing file: {e}")
407481
raise e
408482

409483
if self.lock_files:

0 commit comments

Comments
 (0)