50 lines
2.1 KiB
Python
50 lines
2.1 KiB
Python
"""Consistent persistence lifecycle for individual CLI modules."""
|
|
from .reports import ReportStore,UnavailableStore,utc_now
|
|
from .schema import normalize_report
|
|
from .reporting import sharing_copy
|
|
from . import __version__
|
|
|
|
|
|
class ScanSession:
|
|
def __init__(self,key,path,lang):
|
|
self.key=key
|
|
self.lang=lang
|
|
self.report={'schema_version':3,'tool_version':__version__,'generated_at':utc_now(),
|
|
'mode':'read-only','result':'running',key:{}}
|
|
try:self.store=ReportStore(path,lambda value:sharing_copy(value,lang))
|
|
except FileExistsError:raise
|
|
except OSError:self.store=UnavailableStore(path)
|
|
self.failed=False
|
|
|
|
def checkpoint(self):
|
|
normalize_report(self.report)
|
|
if not self.store.checkpoint(self.report):self.failed=True
|
|
|
|
def __enter__(self):
|
|
try:
|
|
self.checkpoint()
|
|
except BaseException:
|
|
# Context managers do not receive __exit__ when __enter__ raises.
|
|
# Preserve a cancellation snapshot and close the journal even if a
|
|
# signal arrives during the first durable checkpoint.
|
|
import sys
|
|
self.__exit__(*sys.exc_info())
|
|
raise
|
|
return self
|
|
|
|
def __exit__(self,kind,value,traceback):
|
|
if kind is not None and issubclass(kind,KeyboardInterrupt):
|
|
from .worker import cancel_all
|
|
cancel_all()
|
|
self.report['result']='cancelled'
|
|
else:self.report['result']='incomplete' if kind else self.report['result'] if self.report['result']!='running' else 'local_execution_complete'
|
|
self.report['finished_at']=utc_now()
|
|
normalize_report(self.report)
|
|
try:
|
|
self.failed=not self.store.finish(self.report) or self.failed
|
|
finally:self.store.close()
|
|
if self.store.journal_path:
|
|
print(('恢复日志: ' if self.lang=='zh' else 'Recovery journal: ')+str(self.store.journal_path),flush=True)
|
|
if self.failed:print('REPORT_WRITE_FAILED'+(' / 报告写入未完成' if self.lang=='zh' else ''),flush=True)
|
|
return False
|