From 1ce6a63a62c62da580894772235655032da35389 Mon Sep 17 00:00:00 2001 From: Charles Haley Date: Sun, 21 Jan 2024 14:38:34 +0000 Subject: [PATCH] Bug #2049992: `unknown function` when referencing stored template "function" in `epub:save_to_disk` plugboard template The comments in Saver.__init__ are there in case there is some reason to not remove the load_user_template_functions() call. They can be removed. --- src/calibre/gui2/save.py | 16 ++++++++++++---- src/calibre/library/save_to_disk.py | 5 ++++- 2 files changed, 16 insertions(+), 5 deletions(-) diff --git a/src/calibre/gui2/save.py b/src/calibre/gui2/save.py index 584c64d353..6be6703b0e 100644 --- a/src/calibre/gui2/save.py +++ b/src/calibre/gui2/save.py @@ -26,7 +26,6 @@ from calibre.library.save_to_disk import ( ) from calibre.ptempfile import PersistentTemporaryDirectory, SpooledTemporaryFile from calibre.utils.filenames import make_long_path_useable -from calibre.utils.formatter_functions import load_user_template_functions from calibre.utils.ipc.pool import Failure, Pool from polyglot.builtins import iteritems, itervalues from polyglot.queue import Empty @@ -80,7 +79,13 @@ class Saver(QObject): self.db = db.new_api self.plugboards = self.db.pref('plugboards', {}) self.template_functions = self.db.pref('user_template_functions', []) - load_user_template_functions('', self.template_functions) + self.library_id = self.db.library_id + # This call to load_user_template_functions isn't needed because + # __init__ is running on the GUI thread. It must be done in the separate + # process by the worker + # from calibre.gui2 import is_gui_thread + # print(f'Saver __init__ is_gui_thread: {is_gui_thread()}') + # load_user_template_functions('', self.template_functions) self.collected_data = {} self.errors = defaultdict(list) self._book_id_data = {} @@ -111,7 +116,8 @@ class Saver(QObject): if self.pool is not None: self.pool.shutdown() self.setParent(None) - self.jobs = self.pool = self.plugboards = self.template_functions = self.collected_data = self.all_book_ids = self.pd = self.db = None # noqa + self.jobs = self.pool = self.plugboards = self.template_functions = self.library_id =\ + self.collected_data = self.all_book_ids = self.pd = self.db = None # noqa self.deleteLater() def book_id_data(self, book_id): @@ -155,7 +161,9 @@ class Saver(QObject): plugboards_cache = {fmt:find_plugboard(plugboard_save_to_disk_value, fmt, self.plugboards) for fmt in all_fmts} self.pool = Pool(name='SaveToDisk') if self.pool is None else self.pool try: - self.pool.set_common_data(plugboards_cache) + self.pool.set_common_data({'plugboard_cache': plugboards_cache, + 'template_functions': self.template_functions, + 'library_id': self.library_id}) except Failure as err: error_dialog(self.pd, _('Critical failure'), _( 'Could not save books to disk, click "Show details" for more information'), diff --git a/src/calibre/library/save_to_disk.py b/src/calibre/library/save_to_disk.py index e844d23b74..8e652a2c6a 100644 --- a/src/calibre/library/save_to_disk.py +++ b/src/calibre/library/save_to_disk.py @@ -20,6 +20,7 @@ from calibre.utils.filenames import ( ascii_filename, make_long_path_useable, shorten_components_to, ) from calibre.utils.formatter import TemplateFormatter +from calibre.utils.formatter_functions import load_user_template_functions from calibre.utils.localization import _ plugboard_any_device_value = 'any device' @@ -439,8 +440,10 @@ def read_serialized_metadata(data): def update_serialized_metadata(book, common_data=None): + # This is called from a worker process. It must not open the database. result = [] - plugboard_cache = common_data + plugboard_cache = common_data['plugboard_cache'] + load_user_template_functions(common_data['library_id'], common_data['template_functions']) from calibre.customize.ui import apply_null_metadata with apply_null_metadata: fmts = [fp.rpartition(os.extsep)[-1] for fp in book['fmts']]