From e770af4d47d72792068311e8244d7762b581822b Mon Sep 17 00:00:00 2001
From: Charles Haley <>
Date: Sat, 25 Dec 2010 18:08:11 +0000
Subject: [PATCH 1/2] New formatter function, fix problems with strcat and math
results, define basic formatter for usbms and formatter
---
src/calibre/devices/usbms/books.py | 21 +++-------------
src/calibre/utils/formatter.py | 40 +++++++++++++++++++++---------
2 files changed, 31 insertions(+), 30 deletions(-)
diff --git a/src/calibre/devices/usbms/books.py b/src/calibre/devices/usbms/books.py
index 73afd770c1..1e7d74480a 100644
--- a/src/calibre/devices/usbms/books.py
+++ b/src/calibre/devices/usbms/books.py
@@ -14,22 +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 TemplateFormatter
-
-class SafeFormat(TemplateFormatter):
- '''
- Provides a format function that substitutes '' for any missing value
- '''
-
- def get_value(self, key, args, kwargs):
- try:
- if key in kwargs:
- return kwargs[key]
- return key
- except:
- return key
-
-safe_formatter = SafeFormat()
+from calibre.utils.formatter import eval_formatter
class Book(Metadata):
def __init__(self, prefix, lpath, size=None, other=None):
@@ -131,10 +116,10 @@ class CollectionsBookList(BookList):
field_name = field_meta['name']
else:
field_name = ''
- cat_name = safe_formatter.safe_format(
+ cat_name = eval_formatter.safe_format(
fmt=tweaks['sony_collection_name_template'],
kwargs={'category':field_name, 'value':field_value},
- error_value='', book=None)
+ error_value='GET_CATEGORY', book=None)
return cat_name.strip()
def get_collections(self, collection_attributes):
diff --git a/src/calibre/utils/formatter.py b/src/calibre/utils/formatter.py
index 182aff5a7a..bb7e953d19 100644
--- a/src/calibre/utils/formatter.py
+++ b/src/calibre/utils/formatter.py
@@ -36,7 +36,7 @@ class _Parser(object):
return gt
def _assign(self, target, value):
- setattr(self, target, value)
+ self.variables[target] = value
return value
def _concat(self, *args):
@@ -55,18 +55,23 @@ class _Parser(object):
}
x = float(x if x else 0)
y = float(y if y else 0)
- return ops[op](x, y)
+ return str(ops[op](x, y))
def _template(self, template):
template = template.replace('[[', '{').replace(']]', '}')
return self.parent.safe_format(template, self.parent.kwargs, 'TEMPLATE',
self.parent.book)
+ def _eval(self, template):
+ template = template.replace('[[', '{').replace(']]', '}')
+ return eval_formatter.safe_format(template, self.variables, 'EVAL', None)
+
local_functions = {
'add' : (2, partial(_math, op='+')),
'assign' : (2, _assign),
'cmp' : (5, _cmp),
'divide' : (2, partial(_math, op='/')),
+ 'eval' : (1, _eval),
'field' : (1, lambda s, x: s.parent.get_value(x, [], s.parent.kwargs)),
'multiply' : (2, partial(_math, op='*')),
'strcat' : (-1, _concat),
@@ -82,7 +87,7 @@ class _Parser(object):
if prog[1] != '':
self.error(_('failed to scan program. Invalid input {0}').format(prog[1]))
self.parent = parent
- setattr(self, '$', val)
+ self.variables = {'$':val}
def error(self, message):
m = 'Formatter: ' + message + _(' near ')
@@ -144,7 +149,7 @@ class _Parser(object):
# We have an identifier. Determine if it is a function
id = self.token()
if not self.token_op_is_a('('):
- return getattr(self, id, _('unknown id ') + id)
+ return self.variables.get(id, _('unknown id ') + id)
# We have a function.
# Check if it is a known one. We do this here so error reporting is
# better, as it can identify the tokens near the problem.
@@ -417,15 +422,18 @@ class TemplateFormatter(string.Formatter):
self.kwargs = kwargs
self.book = book
self.composite_values = {}
- try:
- ans = self.vformat(fmt, [], kwargs).strip()
- except Exception, e:
- if DEBUG:
- traceback.print_exc()
- ans = error_value + ' ' + e.message
+ if fmt.startswith('program:'):
+ ans = self._eval_program(None, fmt[8:])
+ else:
+ try:
+ ans = self.vformat(fmt, [], kwargs).strip()
+ except Exception, e:
+ if DEBUG:
+ traceback.print_exc()
+ ans = error_value + ' ' + e.message
return ans
-class ValidateFormat(TemplateFormatter):
+class ValidateFormatter(TemplateFormatter):
'''
Provides a format function that substitutes '' for any missing value
'''
@@ -435,6 +443,14 @@ class ValidateFormat(TemplateFormatter):
def validate(self, x):
return self.vformat(x, [], {})
-validation_formatter = ValidateFormat()
+validation_formatter = ValidateFormatter()
+class EvalFormatter(TemplateFormatter):
+ '''
+ A template formatter that uses a simple dict instead of an mi instance
+ '''
+ def get_value(self, key, args, kwargs):
+ return kwargs.get(key, _('No such variable ') + key)
+
+eval_formatter = EvalFormatter()
From 532749201a15611d262e903409ad5ea3352a6083 Mon Sep 17 00:00:00 2001
From: Charles Haley <>
Date: Sat, 25 Dec 2010 18:30:50 +0000
Subject: [PATCH 2/2] Make custom_column_widgets.py, book_info.py, and
book_details.py use comments_to_html. Fix _parent to parent()
---
src/calibre/gui2/book_details.py | 1 +
src/calibre/gui2/custom_column_widgets.py | 3 ++-
src/calibre/gui2/dialogs/book_info.py | 1 +
src/calibre/gui2/library/delegates.py | 2 +-
4 files changed, 5 insertions(+), 2 deletions(-)
diff --git a/src/calibre/gui2/book_details.py b/src/calibre/gui2/book_details.py
index 50ce72686a..dd12080d7f 100644
--- a/src/calibre/gui2/book_details.py
+++ b/src/calibre/gui2/book_details.py
@@ -46,6 +46,7 @@ def render_rows(data):
txt = txt.decode(preferred_encoding, 'replace')
if key.endswith(u':html'):
key = key[:-5]
+ txt = comments_to_html(txt)
elif '' not in txt:
txt = prepare_string_for_xml(txt)
if 'id' in data:
diff --git a/src/calibre/gui2/custom_column_widgets.py b/src/calibre/gui2/custom_column_widgets.py
index 40abb05f89..ec18675359 100644
--- a/src/calibre/gui2/custom_column_widgets.py
+++ b/src/calibre/gui2/custom_column_widgets.py
@@ -19,6 +19,7 @@ from calibre.gui2.comments_editor import Editor as CommentsEditor
from calibre.gui2 import UNDEFINED_QDATE, error_dialog
from calibre.utils.config import tweaks
from calibre.utils.icu import sort_key
+from calibre.library.comments import comments_to_html
class Base(object):
@@ -197,7 +198,7 @@ class Comments(Base):
def setter(self, val):
if val is None:
val = ''
- self._tb.html = val
+ self._tb.html = comments_to_html(val)
def getter(self):
val = unicode(self._tb.html).strip()
diff --git a/src/calibre/gui2/dialogs/book_info.py b/src/calibre/gui2/dialogs/book_info.py
index 6cae27d926..1384c27b8c 100644
--- a/src/calibre/gui2/dialogs/book_info.py
+++ b/src/calibre/gui2/dialogs/book_info.py
@@ -136,6 +136,7 @@ class BookInfo(QDialog, Ui_BookInfo):
txt = info[key]
if key.endswith(':html'):
key = key[:-5]
+ txt = comments_to_html(txt)
if key != _('Path'):
txt = u'
\n'.join(textwrap.wrap(txt, 120))
rows += u'