Fix lack of thread saefty in template format system, that could lead to incorrect template evaluation in some cases. Fixes #801944 (Problem with metadata synch on Sony)

This commit is contained in:
Kovid Goyal 2011-06-26 09:22:48 -06:00
commit cde0352623
8 changed files with 20 additions and 13 deletions

View File

@ -14,7 +14,7 @@ from calibre.constants import preferred_encoding
from calibre import isbytestring, force_unicode from calibre import isbytestring, force_unicode
from calibre.utils.config import prefs, tweaks from calibre.utils.config import prefs, tweaks
from calibre.utils.icu import strcmp from calibre.utils.icu import strcmp
from calibre.utils.formatter import eval_formatter from calibre.utils.formatter import EvalFormatter
class Book(Metadata): class Book(Metadata):
def __init__(self, prefix, lpath, size=None, other=None): def __init__(self, prefix, lpath, size=None, other=None):
@ -116,7 +116,7 @@ class CollectionsBookList(BookList):
field_name = field_meta['name'] field_name = field_meta['name']
else: else:
field_name = '' field_name = ''
cat_name = eval_formatter.safe_format( cat_name = EvalFormatter().safe_format(
fmt=tweaks['sony_collection_name_template'], fmt=tweaks['sony_collection_name_template'],
kwargs={'category':field_name, 'value':field_value}, kwargs={'category':field_name, 'value':field_value},
error_value='GET_CATEGORY', book=None) error_value='GET_CATEGORY', book=None)

View File

@ -70,6 +70,7 @@ class SafeFormat(TemplateFormatter):
return '' return ''
return v return v
# DEPRECATED. This is not thread safe. Do not use.
composite_formatter = SafeFormat() composite_formatter = SafeFormat()
class Metadata(object): class Metadata(object):
@ -110,6 +111,7 @@ class Metadata(object):
# List of strings or [] # List of strings or []
self.author = list(authors) if authors else []# Needed for backward compatibility self.author = list(authors) if authors else []# Needed for backward compatibility
self.authors = list(authors) if authors else [] self.authors = list(authors) if authors else []
self.formatter = SafeFormat()
def is_null(self, field): def is_null(self, field):
''' '''
@ -146,7 +148,7 @@ class Metadata(object):
return val return val
if val is None: if val is None:
d['#value#'] = 'RECURSIVE_COMPOSITE FIELD (Metadata) ' + field 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'], d['display']['composite_template'],
self, self,
_('TEMPLATE ERROR'), _('TEMPLATE ERROR'),
@ -423,11 +425,12 @@ class Metadata(object):
''' '''
if not ops: if not ops:
return return
formatter = SafeFormat()
for op in ops: for op in ops:
try: try:
src = op[0] src = op[0]
dest = op[1] dest = op[1]
val = composite_formatter.safe_format\ val = formatter.safe_format\
(src, other, 'PLUGBOARD TEMPLATE ERROR', other) (src, other, 'PLUGBOARD TEMPLATE ERROR', other)
if dest == 'tags': if dest == 'tags':
self.set(dest, [f.strip() for f in val.split(',') if f.strip()]) self.set(dest, [f.strip() for f in val.split(',') if f.strip()])

View File

@ -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.metadata_bulk_ui import Ui_MetadataBulkDialog
from calibre.gui2.dialogs.tag_editor import TagEditor 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 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.custom_column_widgets import populate_metadata_page
from calibre.gui2 import error_dialog, ResizableDialog, UNDEFINED_QDATE, \ from calibre.gui2 import error_dialog, ResizableDialog, UNDEFINED_QDATE, \
gprefs, question_dialog gprefs, question_dialog
@ -499,7 +499,7 @@ class MetadataBulkDialog(ResizableDialog, Ui_MetadataBulkDialog):
def s_r_get_field(self, mi, field): def s_r_get_field(self, mi, field):
if field: if field:
if field == '{template}': if field == '{template}':
v = composite_formatter.safe_format\ v = SafeFormat().safe_format\
(unicode(self.s_r_template.text()), mi, _('S/R TEMPLATE ERROR'), mi) (unicode(self.s_r_template.text()), mi, _('S/R TEMPLATE ERROR'), mi)
return [v] return [v]
fm = self.db.metadata_for_field(field) fm = self.db.metadata_for_field(field)

View File

@ -11,7 +11,7 @@ from PyQt4.Qt import (Qt, QDialog, QDialogButtonBox, QSyntaxHighlighter, QFont,
from calibre.gui2 import error_dialog from calibre.gui2 import error_dialog
from calibre.gui2.dialogs.template_dialog_ui import Ui_TemplateDialog from calibre.gui2.dialogs.template_dialog_ui import Ui_TemplateDialog
from calibre.utils.formatter_functions import formatter_functions 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) from calibre.library.coloring import (displayable_columns)
@ -270,7 +270,7 @@ class TemplateDialog(QDialog, Ui_TemplateDialog):
self.highlighter.regenerate_paren_positions() self.highlighter.regenerate_paren_positions()
self.text_cursor_changed() self.text_cursor_changed()
self.template_value.setText( self.template_value.setText(
composite_formatter.safe_format(cur_text, self.mi, SafeFormat().safe_format(cur_text, self.mi,
_('EXCEPTION: '), self.mi)) _('EXCEPTION: '), self.mi))
def text_cursor_changed(self): def text_cursor_changed(self):

View File

@ -14,7 +14,7 @@ from PyQt4.Qt import (QAbstractTableModel, Qt, pyqtSignal, QIcon, QImage,
from calibre.gui2 import NONE, UNDEFINED_QDATE from calibre.gui2 import NONE, UNDEFINED_QDATE
from calibre.utils.pyparsing import ParseException from calibre.utils.pyparsing import ParseException
from calibre.ebooks.metadata import fmt_sidx, authors_to_string, string_to_authors 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.ptempfile import PersistentTemporaryFile
from calibre.utils.config import tweaks, prefs from calibre.utils.config import tweaks, prefs
from calibre.utils.date import dt_factory, qt_to_dt from calibre.utils.date import dt_factory, qt_to_dt
@ -91,6 +91,7 @@ class BooksModel(QAbstractTableModel): # {{{
self.current_highlighted_idx = None self.current_highlighted_idx = None
self.highlight_only = False self.highlight_only = False
self.colors = frozenset([unicode(c) for c in QColor.colorNames()]) self.colors = frozenset([unicode(c) for c in QColor.colorNames()])
self.formatter = SafeFormat()
self.read_config() self.read_config()
def change_alignment(self, colname, alignment): def change_alignment(self, colname, alignment):
@ -711,7 +712,7 @@ class BooksModel(QAbstractTableModel): # {{{
try: try:
if mi is None: if mi is None:
mi = self.db.get_metadata(id_, index_is_id=True) 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: if color in self.colors:
color = QColor(color) color = QColor(color)
if color.isValid(): if color.isValid():

View File

@ -20,7 +20,7 @@ from calibre.utils.config import tweaks
from calibre.utils.icu import sort_key, lower, strcmp from calibre.utils.icu import sort_key, lower, strcmp
from calibre.library.field_metadata import TagsIcons, category_icon_map from calibre.library.field_metadata import TagsIcons, category_icon_map
from calibre.gui2.dialogs.confirm_delete import confirm from calibre.gui2.dialogs.confirm_delete import confirm
from calibre.utils.formatter import eval_formatter from calibre.utils.formatter import EvalFormatter
from calibre.utils.search_query_parser import saved_searches from calibre.utils.search_query_parser import saved_searches
TAG_SEARCH_STATES = {'clear': 0, 'mark_plus': 1, 'mark_plusplus': 2, TAG_SEARCH_STATES = {'clear': 0, 'mark_plus': 1, 'mark_plusplus': 2,
@ -341,6 +341,8 @@ class TagsModel(QAbstractItemModel): # {{{
def _create_node_tree(self, data, state_map): def _create_node_tree(self, data, state_map):
sort_by = config['sort_tags_by'] sort_by = config['sort_tags_by']
eval_formatter = EvalFormatter()
if data is None: if data is None:
print ('_create_node_tree: no data!') print ('_create_node_tree: no data!')
traceback.print_stack() traceback.print_stack()

View File

@ -347,5 +347,6 @@ class EvalFormatter(TemplateFormatter):
key = key.lower() key = key.lower()
return kwargs.get(key, _('No such variable ') + key) return kwargs.get(key, _('No such variable ') + key)
# DEPRECATED. This is not thread safe. Do not use.
eval_formatter = EvalFormatter() eval_formatter = EvalFormatter()

View File

@ -202,9 +202,9 @@ class BuiltinEval(BuiltinFormatterFunction):
'results from local variables.') 'results from local variables.')
def evaluate(self, formatter, kwargs, mi, locals, template): def evaluate(self, formatter, kwargs, mi, locals, template):
from formatter import eval_formatter from formatter import EvalFormatter
template = template.replace('[[', '{').replace(']]', '}') template = template.replace('[[', '{').replace(']]', '}')
return eval_formatter.safe_format(template, locals, 'EVAL', None) return EvalFormatter().safe_format(template, locals, 'EVAL', None)
class BuiltinAssign(BuiltinFormatterFunction): class BuiltinAssign(BuiltinFormatterFunction):
name = 'assign' name = 'assign'