mirror of
https://github.com/kovidgoyal/calibre.git
synced 2025-07-09 03:04:10 -04:00
Make communication with toc editor process more robust
A crash on dialog close no longer affects writing the result. And avoid using the racy lock files for ipc.
This commit is contained in:
parent
2c802b7839
commit
e1a3458c51
@ -152,37 +152,31 @@ class ToCEditAction(InterfaceAction):
|
||||
self.check_for_completions()
|
||||
|
||||
def check_for_completions(self):
|
||||
from calibre.utils.lock import lock_file
|
||||
from calibre.utils.filenames import retry_on_fail
|
||||
for job in tuple(self.jobs):
|
||||
lock_path = job['path'] + '.lock'
|
||||
if job['started']:
|
||||
if not os.path.exists(lock_path):
|
||||
self.jobs.remove(job)
|
||||
continue
|
||||
try:
|
||||
lf = lock_file(lock_path, timeout=0.01, sleep_time=0.005)
|
||||
except EnvironmentError:
|
||||
continue
|
||||
else:
|
||||
self.jobs.remove(job)
|
||||
ret = int(lf.read().decode('ascii'))
|
||||
lf.close()
|
||||
os.remove(lock_path)
|
||||
if ret == 0:
|
||||
db = self.gui.current_db
|
||||
if db.new_api.library_id != job['library_id']:
|
||||
error_dialog(self.gui, _('Library changed'), _(
|
||||
'Cannot save changes made to {0} by the ToC editor as'
|
||||
' the calibre library has changed.').format(job['title']), show=True)
|
||||
else:
|
||||
db.new_api.add_format(job['book_id'], job['fmt'], job['path'], run_hooks=False)
|
||||
os.remove(job['path'])
|
||||
started_path = job['path'] + '.started'
|
||||
result_path = job['path'] + '.result'
|
||||
if job['started'] and os.path.exists(result_path):
|
||||
self.jobs.remove(job)
|
||||
with open(result_path) as f:
|
||||
ret = int(f.read().strip())
|
||||
retry_on_fail(os.remove, result_path)
|
||||
if ret == 0:
|
||||
db = self.gui.current_db
|
||||
if db.new_api.library_id != job['library_id']:
|
||||
error_dialog(self.gui, _('Library changed'), _(
|
||||
'Cannot save changes made to {0} by the ToC editor as'
|
||||
' the calibre library has changed.').format(job['title']), show=True)
|
||||
else:
|
||||
db.new_api.add_format(job['book_id'], job['fmt'], job['path'], run_hooks=False)
|
||||
os.remove(job['path'])
|
||||
else:
|
||||
if monotonic() - job['start_time'] > 10:
|
||||
self.jobs.remove(job)
|
||||
continue
|
||||
if os.path.exists(lock_path):
|
||||
if os.path.exists(started_path):
|
||||
job['started'] = True
|
||||
retry_on_fail(os.remove, started_path)
|
||||
if self.jobs:
|
||||
QTimer.singleShot(100, self.check_for_completions)
|
||||
|
||||
|
@ -5,6 +5,7 @@
|
||||
|
||||
import os
|
||||
import sys
|
||||
import tempfile
|
||||
import textwrap
|
||||
from functools import partial
|
||||
from qt.core import (
|
||||
@ -29,7 +30,7 @@ from calibre.gui2.progress_indicator import ProgressIndicator
|
||||
from calibre.gui2.toc.location import ItemEdit
|
||||
from calibre.ptempfile import reset_base_dir
|
||||
from calibre.utils.config import JSONConfig
|
||||
from calibre.utils.lock import ExclusiveFile
|
||||
from calibre.utils.filenames import atomic_rename
|
||||
from calibre.utils.logging import GUILog
|
||||
from polyglot.builtins import map, range, unicode_type
|
||||
|
||||
@ -985,8 +986,9 @@ class TOCEditor(QDialog): # {{{
|
||||
explode_done = pyqtSignal(object)
|
||||
writing_done = pyqtSignal(object)
|
||||
|
||||
def __init__(self, pathtobook, title=None, parent=None, prefs=None):
|
||||
def __init__(self, pathtobook, title=None, parent=None, prefs=None, write_result_to=None):
|
||||
QDialog.__init__(self, parent)
|
||||
self.write_result_to = write_result_to
|
||||
self.prefs = prefs or te_prefs
|
||||
self.pathtobook = pathtobook
|
||||
self.working = True
|
||||
@ -1071,7 +1073,7 @@ class TOCEditor(QDialog): # {{{
|
||||
' more information.')%self.book_title, det_msg=tb, show=True)
|
||||
super(TOCEditor, self).reject()
|
||||
return
|
||||
|
||||
self.write_result(0)
|
||||
super(TOCEditor, self).accept()
|
||||
|
||||
def reject(self):
|
||||
@ -1083,8 +1085,17 @@ class TOCEditor(QDialog): # {{{
|
||||
else:
|
||||
self.working = False
|
||||
self.prefs['toc_editor_window_geom'] = bytearray(self.saveGeometry())
|
||||
self.write_result(1)
|
||||
super(TOCEditor, self).reject()
|
||||
|
||||
def write_result(self, res):
|
||||
if self.write_result_to:
|
||||
with tempfile.NamedTemporaryFile(dir=os.path.dirname(self.write_result_to), delete=False) as f:
|
||||
src = f.name
|
||||
f.write(str(res).encode('utf-8'))
|
||||
f.flush()
|
||||
atomic_rename(src, self.write_result_to)
|
||||
|
||||
def start(self):
|
||||
t = Thread(target=self.explode)
|
||||
t.daemon = True
|
||||
@ -1140,15 +1151,15 @@ def main(path=None, title=None):
|
||||
# launched from within calibre, as both use calibre-parallel.exe
|
||||
set_app_uid(TOC_DIALOG_APP_UID)
|
||||
|
||||
with ExclusiveFile(path + '.lock') as wf:
|
||||
override = 'calibre-gui' if islinux else None
|
||||
app = Application([], override_program_name=override)
|
||||
d = TOCEditor(path, title=title)
|
||||
d.start()
|
||||
ret = 1
|
||||
if d.exec_() == QDialog.DialogCode.Accepted:
|
||||
ret = 0
|
||||
wf.write('{}'.format(ret).encode('ascii'))
|
||||
with open(path + '.started', 'w'):
|
||||
pass
|
||||
override = 'calibre-gui' if islinux else None
|
||||
app = Application([], override_program_name=override)
|
||||
d = TOCEditor(path, title=title, write_result_to=path + '.result')
|
||||
d.start()
|
||||
ret = 1
|
||||
if d.exec_() == QDialog.DialogCode.Accepted:
|
||||
ret = 0
|
||||
del d
|
||||
del app
|
||||
raise SystemExit(ret)
|
||||
|
Loading…
x
Reference in New Issue
Block a user