From 5fa86372e25f3d81f50756afc3865972490d791f Mon Sep 17 00:00:00 2001 From: Charles Haley <> Date: Sun, 26 Jun 2011 13:51:23 +0100 Subject: [PATCH 1/2] Fix threading problem with formatter in ebooks.metadata.book.base.py. --- src/calibre/ebooks/metadata/book/base.py | 7 +++++-- src/calibre/gui2/dialogs/metadata_bulk.py | 4 ++-- src/calibre/gui2/dialogs/template_dialog.py | 4 ++-- src/calibre/gui2/library/models.py | 5 +++-- 4 files changed, 12 insertions(+), 8 deletions(-) diff --git a/src/calibre/ebooks/metadata/book/base.py b/src/calibre/ebooks/metadata/book/base.py index 382cb6c5a2..c3af8bea07 100644 --- a/src/calibre/ebooks/metadata/book/base.py +++ b/src/calibre/ebooks/metadata/book/base.py @@ -70,6 +70,7 @@ class SafeFormat(TemplateFormatter): return '' return v +# DEPRECATED. This is not thread safe. Do not use. composite_formatter = SafeFormat() class Metadata(object): @@ -110,6 +111,7 @@ class Metadata(object): # List of strings or [] self.author = list(authors) if authors else []# Needed for backward compatibility self.authors = list(authors) if authors else [] + self.formatter = SafeFormat() def is_null(self, field): ''' @@ -146,7 +148,7 @@ class Metadata(object): return val if val is None: d['#value#'] = 'RECURSIVE_COMPOSITE FIELD (Metadata) ' + field - val = d['#value#'] = composite_formatter.safe_format( + val = d['#value#'] = self.formatter.safe_format( d['display']['composite_template'], self, _('TEMPLATE ERROR'), @@ -423,11 +425,12 @@ class Metadata(object): ''' if not ops: return + formatter = SafeFormat() for op in ops: try: src = op[0] dest = op[1] - val = composite_formatter.safe_format\ + val = formatter.safe_format\ (src, other, 'PLUGBOARD TEMPLATE ERROR', other) if dest == 'tags': self.set(dest, [f.strip() for f in val.split(',') if f.strip()]) diff --git a/src/calibre/gui2/dialogs/metadata_bulk.py b/src/calibre/gui2/dialogs/metadata_bulk.py index 7c7c78629c..22dfb98956 100644 --- a/src/calibre/gui2/dialogs/metadata_bulk.py +++ b/src/calibre/gui2/dialogs/metadata_bulk.py @@ -12,7 +12,7 @@ from PyQt4.Qt import Qt, QDialog, QGridLayout, QVBoxLayout, QFont, QLabel, \ from calibre.gui2.dialogs.metadata_bulk_ui import Ui_MetadataBulkDialog from calibre.gui2.dialogs.tag_editor import TagEditor from calibre.ebooks.metadata import string_to_authors, authors_to_string, title_sort -from calibre.ebooks.metadata.book.base import composite_formatter +from calibre.ebooks.metadata.book.base import SafeFormat from calibre.gui2.custom_column_widgets import populate_metadata_page from calibre.gui2 import error_dialog, ResizableDialog, UNDEFINED_QDATE, \ gprefs, question_dialog @@ -499,7 +499,7 @@ class MetadataBulkDialog(ResizableDialog, Ui_MetadataBulkDialog): def s_r_get_field(self, mi, field): if field: if field == '{template}': - v = composite_formatter.safe_format\ + v = SafeFormat().safe_format\ (unicode(self.s_r_template.text()), mi, _('S/R TEMPLATE ERROR'), mi) return [v] fm = self.db.metadata_for_field(field) diff --git a/src/calibre/gui2/dialogs/template_dialog.py b/src/calibre/gui2/dialogs/template_dialog.py index f78e7a7383..7d30f37bc1 100644 --- a/src/calibre/gui2/dialogs/template_dialog.py +++ b/src/calibre/gui2/dialogs/template_dialog.py @@ -11,7 +11,7 @@ from PyQt4.Qt import (Qt, QDialog, QDialogButtonBox, QSyntaxHighlighter, QFont, from calibre.gui2 import error_dialog from calibre.gui2.dialogs.template_dialog_ui import Ui_TemplateDialog from calibre.utils.formatter_functions import formatter_functions -from calibre.ebooks.metadata.book.base import composite_formatter, Metadata +from calibre.ebooks.metadata.book.base import SafeFormat, Metadata from calibre.library.coloring import (displayable_columns) @@ -270,7 +270,7 @@ class TemplateDialog(QDialog, Ui_TemplateDialog): self.highlighter.regenerate_paren_positions() self.text_cursor_changed() self.template_value.setText( - composite_formatter.safe_format(cur_text, self.mi, + SafeFormat().safe_format(cur_text, self.mi, _('EXCEPTION: '), self.mi)) def text_cursor_changed(self): diff --git a/src/calibre/gui2/library/models.py b/src/calibre/gui2/library/models.py index 40d6e2b6cf..8cbc2e1979 100644 --- a/src/calibre/gui2/library/models.py +++ b/src/calibre/gui2/library/models.py @@ -14,7 +14,7 @@ from PyQt4.Qt import (QAbstractTableModel, Qt, pyqtSignal, QIcon, QImage, from calibre.gui2 import NONE, UNDEFINED_QDATE from calibre.utils.pyparsing import ParseException from calibre.ebooks.metadata import fmt_sidx, authors_to_string, string_to_authors -from calibre.ebooks.metadata.book.base import composite_formatter +from calibre.ebooks.metadata.book.base import SafeFormat from calibre.ptempfile import PersistentTemporaryFile from calibre.utils.config import tweaks, prefs from calibre.utils.date import dt_factory, qt_to_dt @@ -91,6 +91,7 @@ class BooksModel(QAbstractTableModel): # {{{ self.current_highlighted_idx = None self.highlight_only = False self.colors = frozenset([unicode(c) for c in QColor.colorNames()]) + self.formatter = SafeFormat() self.read_config() def change_alignment(self, colname, alignment): @@ -711,7 +712,7 @@ class BooksModel(QAbstractTableModel): # {{{ try: if mi is None: mi = self.db.get_metadata(id_, index_is_id=True) - color = composite_formatter.safe_format(fmt, mi, '', mi) + color = self.formatter.safe_format(fmt, mi, '', mi) if color in self.colors: color = QColor(color) if color.isValid(): From 2ad45afdb2e255f1d00e326a3abf1f9971234508 Mon Sep 17 00:00:00 2001 From: Charles Haley <> Date: Sun, 26 Jun 2011 14:51:58 +0100 Subject: [PATCH 2/2] Remove references to eval_formatter. Note that the tag browser uses it, but has not been changed because it is in the middle of re-architecture. --- src/calibre/devices/usbms/books.py | 4 ++-- src/calibre/utils/formatter.py | 1 + src/calibre/utils/formatter_functions.py | 4 ++-- 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/src/calibre/devices/usbms/books.py b/src/calibre/devices/usbms/books.py index 731d3e2b49..4d726e5bde 100644 --- a/src/calibre/devices/usbms/books.py +++ b/src/calibre/devices/usbms/books.py @@ -14,7 +14,7 @@ from calibre.constants import preferred_encoding from calibre import isbytestring, force_unicode from calibre.utils.config import prefs, tweaks from calibre.utils.icu import strcmp -from calibre.utils.formatter import eval_formatter +from calibre.utils.formatter import EvalFormatter class Book(Metadata): def __init__(self, prefix, lpath, size=None, other=None): @@ -116,7 +116,7 @@ class CollectionsBookList(BookList): field_name = field_meta['name'] else: field_name = '' - cat_name = eval_formatter.safe_format( + cat_name = EvalFormatter().safe_format( fmt=tweaks['sony_collection_name_template'], kwargs={'category':field_name, 'value':field_value}, error_value='GET_CATEGORY', book=None) diff --git a/src/calibre/utils/formatter.py b/src/calibre/utils/formatter.py index ebf47db854..3a93c2b650 100644 --- a/src/calibre/utils/formatter.py +++ b/src/calibre/utils/formatter.py @@ -347,5 +347,6 @@ class EvalFormatter(TemplateFormatter): key = key.lower() return kwargs.get(key, _('No such variable ') + key) +# DEPRECATED. This is not thread safe. Do not use. eval_formatter = EvalFormatter() diff --git a/src/calibre/utils/formatter_functions.py b/src/calibre/utils/formatter_functions.py index 55bad6c7e8..c6f4bd1b0e 100644 --- a/src/calibre/utils/formatter_functions.py +++ b/src/calibre/utils/formatter_functions.py @@ -202,9 +202,9 @@ class BuiltinEval(BuiltinFormatterFunction): 'results from local variables.') def evaluate(self, formatter, kwargs, mi, locals, template): - from formatter import eval_formatter + from formatter import EvalFormatter template = template.replace('[[', '{').replace(']]', '}') - return eval_formatter.safe_format(template, locals, 'EVAL', None) + return EvalFormatter().safe_format(template, locals, 'EVAL', None) class BuiltinAssign(BuiltinFormatterFunction): name = 'assign'