Skip to content

Commit 34ece52

Browse files
authored
Merge branch 'main' into sysdiagnose_date
2 parents f8e7e6a + de2921b commit 34ece52

File tree

7 files changed

+99
-27
lines changed

7 files changed

+99
-27
lines changed

src/sysdiagnose/__init__.py

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,9 @@ def cases(self, force: bool = False) -> dict:
2222
# pseudo singleton, so it's not loaded unless necessary
2323
# load cases + migration of old cases format to new format
2424
if not self._cases or force:
25+
lock = FileLock(self.config.cases_file)
2526
try:
26-
FileLock.acquire_lock(self.config.cases_file)
27+
lock.acquire()
2728
with open(self.config.cases_file, 'r+') as f:
2829
self._cases = json.load(f)
2930
if 'cases' in self._cases: # conversion is needed
@@ -41,7 +42,7 @@ def cases(self, force: bool = False) -> dict:
4142
with open(self.config.cases_file, 'w') as f:
4243
json.dump(self._cases, f, indent=4)
4344
finally:
44-
FileLock.release_lock(self.config.cases_file)
45+
lock.release()
4546

4647
return self._cases
4748

@@ -65,16 +66,17 @@ def delete_case(self, case_id: str) -> None:
6566
raise Exception(f"Error while deleting case folder: {str(e)}")
6667

6768
# delete case from the cases
69+
lock = FileLock(self.config.cases_file)
6870
try:
69-
FileLock.acquire_lock(self.config.cases_file)
71+
lock.acquire()
7072
with open(self.config.cases_file, 'r+') as f:
7173
self._cases = json.load(f) # load latest version
7274
self._cases.pop(case_id, None) # delete case
7375
f.seek(0) # go back to the beginning of the file
7476
json.dump(self._cases, f, indent=4, sort_keys=True) # save the updated version
7577
f.truncate() # truncate the rest of the file ensuring no old data is left
7678
finally:
77-
FileLock.release_lock(self.config.cases_file)
79+
lock.release()
7880

7981
def create_case(self, sysdiagnose_file: str, force: bool = False, case_id: bool | str = False, tags: list[str] = None) -> int:
8082
'''
@@ -161,16 +163,17 @@ def create_case(self, sysdiagnose_file: str, force: bool = False, case_id: bool
161163
self.extract_sysdiagnose_files(sysdiagnose_file, case_data_folder)
162164

163165
# update case with new data
166+
lock = FileLock(self.config.cases_file)
164167
try:
165-
FileLock.acquire_lock(self.config.cases_file)
168+
lock.acquire()
166169
with open(self.config.cases_file, 'r+') as f:
167170
self._cases = json.load(f) # load latest version
168171
self._cases[case['case_id']] = case # update own case
169172
f.seek(0) # go back to the beginning of the file
170173
json.dump(self._cases, f, indent=4, sort_keys=True) # save the updated version
171174
f.truncate() # truncate the rest of the file ensuring no old data is left
172175
finally:
173-
FileLock.release_lock(self.config.cases_file)
176+
lock.release()
174177

175178
print(f"Sysdiagnose file has been processed: {sysdiagnose_file}")
176179
return case

src/sysdiagnose/parsers/accessibility_tcc.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ def execute(self) -> list | dict:
3131
# only one file to parse
3232
try:
3333
result = []
34-
apollo = Apollo(logger=logger, os_version='yolo') # FIXME get right OS version, but also update the Apollo modules to be aware of the latest OS versions
34+
apollo = Apollo(logger=logger, os_version='yolo', saf_module=self.module_name) # FIXME get right OS version, but also update the Apollo modules to be aware of the latest OS versions
3535
for logfile in self.get_log_files():
3636
result.extend(apollo.parse_db(db_fname=logfile, db_type='TCC.db'))
3737
return result

src/sysdiagnose/utils/apollo.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -166,7 +166,7 @@ def parse_db(self, db_fname: str, db_type: str = None) -> list:
166166
item['timestamp'] = timestamp.timestamp()
167167
item['datetime'] = timestamp.isoformat(timespec='microseconds')
168168
item['timestamp_desc'] = module_query['activity']
169-
item['module_name'] = module_query['name']
169+
item['apollo_module'] = module_query['name']
170170
item['saf_module'] = self.saf_module
171171
item['message'] = module_query['activity'] + ': ' + ', '.join([f"{k}={v}" for k, v in list(zip(headers, row)) if k != key_timestamp and 'time' not in k and 'id' not in k])
172172
results.append(item)

src/sysdiagnose/utils/lock.py

Lines changed: 41 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -12,24 +12,48 @@ class FileLock:
1212
to be locked. The lock is released when the file is deleted.
1313
This is useful for ensuring that only one process can access a resource at a time.
1414
15-
Make sure to use try, finally and always release the lock.
15+
Make sure to use try, finally and always release the lock if a lock was acquired.
16+
17+
Usage:
18+
lock = FileLock("myfile.txt")
19+
try:
20+
lock.acquire()
21+
# Perform operations on the file
22+
except TimeoutError as e:
23+
print(f"Could not acquire lock: {e}")
24+
finally:
25+
lock.release()
1626
'''
17-
@staticmethod
18-
def acquire_lock(filename: str, timeout: int = 5):
19-
"""Create a lock file for the given filename, waiting for the lock to be available."""
20-
lock_file = Path(f"{filename}.lock")
21-
start_time = time.time()
27+
def __init__(self, filename: str, timeout: int = 5):
28+
self.filename = filename
29+
self.timeout = timeout
30+
self.locked = False
31+
self.lock_file = Path(f"{filename}.lock")
32+
33+
def acquire(self):
34+
"""
35+
Acquire the lock for the given filename.
36+
Waits until the lock is available or raises a TimeoutError if it cannot be acquired within the specified timeout.
37+
"""
38+
if self.locked:
39+
return # Already locked, no need to acquire again
2240

23-
while lock_file.exists():
24-
if time.time() - start_time > timeout:
25-
raise TimeoutError(f"Could not acquire lock for {filename} within {timeout} seconds.")
41+
start_time = time.time()
42+
while self.lock_file.exists():
43+
if time.time() - start_time > self.timeout:
44+
self.locked = False
45+
raise TimeoutError(f"Could not acquire lock for {self.filename} within {self.timeout} seconds.")
2646
time.sleep(0.1)
2747

28-
lock_file.touch()
29-
30-
@staticmethod
31-
def release_lock(filename: str):
32-
"""Release the lock by deleting the lock file."""
33-
lock_file = Path(f"{filename}.lock")
34-
if lock_file.exists():
35-
lock_file.unlink()
48+
self.lock_file.touch()
49+
self.locked = True
50+
51+
def release(self):
52+
"""
53+
Release the lock by deleting the lock file.
54+
If the lock file does not exist, it does nothing.
55+
"""
56+
if self.locked:
57+
self.locked = False
58+
if self.lock_file.exists():
59+
self.lock_file.unlink()

tests/test_lock.py

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
2+
from tests import SysdiagnoseTestCase
3+
from sysdiagnose.utils.lock import FileLock
4+
import unittest
5+
6+
7+
class TestLock(SysdiagnoseTestCase):
8+
9+
def test_lock(self):
10+
lock = FileLock('foo.txt', timeout=1)
11+
# first remove any existing lock file
12+
lock.lock_file.unlink(missing_ok=True)
13+
# do the real test
14+
lock.acquire()
15+
self.assertTrue(lock.locked)
16+
lock.release()
17+
self.assertFalse(lock.locked)
18+
self.assertFalse(lock.lock_file.exists())
19+
20+
def test_lock_timeout(self):
21+
lock = FileLock('foo.txt', timeout=1)
22+
# first remove any existing lock file
23+
lock.lock_file.unlink(missing_ok=True)
24+
lock.acquire()
25+
self.assertTrue(lock.locked)
26+
27+
# Try to acquire the lock again, should raise TimeoutError
28+
with self.assertRaises(TimeoutError):
29+
lock2 = FileLock('foo.txt', timeout=1)
30+
lock2.acquire()
31+
32+
# Release the first lock
33+
lock.release()
34+
self.assertFalse(lock.locked)
35+
self.assertFalse(lock.lock_file.exists())
36+
37+
# Now we can acquire the lock again
38+
lock2.acquire()
39+
self.assertTrue(lock2.locked)
40+
lock2.release()
41+
self.assertFalse(lock2.lock_file.exists())
42+
43+
44+
if __name__ == '__main__':
45+
unittest.main()

tests/test_parsers_accessibility_tcc.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ def test_get_accessibility_tcc(self):
1717

1818
result = p.get_result()
1919
for item in result:
20-
self.assertTrue('module_name' in item)
20+
self.assertTrue('apollo_module' in item)
2121
self.assert_has_required_fields_jsonl(item)
2222

2323

tests/test_parsers_powerlogs.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ def test_get_powerlogs(self):
1818
result = p.get_result()
1919
if result: # some files are empty
2020
for item in result:
21-
self.assertTrue('module_name' in item)
21+
self.assertTrue('apollo_module' in item)
2222
self.assert_has_required_fields_jsonl(item)
2323

2424

0 commit comments

Comments
 (0)