This commit is contained in:
Kovid Goyal 2011-01-13 12:16:35 -07:00
commit ea7947341d
4 changed files with 27 additions and 10 deletions

View File

@ -38,7 +38,8 @@ class ConfigWidget(ConfigWidgetBase, Ui_Form):
This parameter can be None in some cases, such as when evaluating
non-book templates.</li>
<li><b>locals:</b> the local variables assigned to by the current
template program. Your_arguments must be one or more parameter (number
template program.</li>
<li><b>Your_arguments</b> must be one or more parameter (number
matching the arg count box), or the value *args for a variable number
of arguments. These are values passed into the function. One argument
is required, and is usually the value of the field being operated upon.

View File

@ -308,6 +308,12 @@ The following program produces the same results as the original recipe, using on
It would be possible to do the above with no custom columns by putting the program into the template box of the plugboard. However, to do so, all comments must be removed because the plugboard text box does not support multi-line editing. It is debatable whether the gain of not having the custom column is worth the vast increase in difficulty caused by the program being one giant line.
User-defined Template Functions
-------------------------------
You can add your own functions to the template processor. Such functions are written in python, and can be used in any of the three template programming modes. The functions are added by going to Preferences -> Advanced -> Template Functions. Instructions are shown in that dialog.
Special notes for save/send templates
-------------------------------------

View File

@ -26,7 +26,7 @@ class _Parser(object):
if prog[1] != '':
self.error(_('failed to scan program. Invalid input {0}').format(prog[1]))
self.parent = parent
self.variables = {'$':val}
self.parent.locals = {'$':val}
def error(self, message):
m = 'Formatter: ' + message + _(' near ')
@ -88,18 +88,20 @@ class _Parser(object):
def expr(self):
if self.token_is_id():
funcs = formatter_functions.get_functions()
# We have an identifier. Determine if it is a function
id = self.token()
if not self.token_op_is_a('('):
if self.token_op_is_a('='):
# classic assignment statement
self.consume()
return self._assign(id, self.expr())
return self.variables.get(id, _('unknown id ') + id)
cls = funcs['assign']
return cls.eval(self.parent, self.parent.kwargs,
self.parent.book, self.parent.locals, id, self.expr())
return self.parent.locals.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.
funcs = formatter_functions.get_functions()
if id not in funcs:
self.error(_('unknown function {0}').format(id))
@ -129,7 +131,7 @@ class _Parser(object):
if cls.arg_count != -1 and len(args) != cls.arg_count:
self.error('incorrect number of arguments for function {}'.format(id))
return cls.eval(self.parent, self.parent.kwargs,
self.parent.book, locals, *args)
self.parent.book, self.parent.locals, *args)
else:
f = self.parent.functions[id]
if f[0] != -1 and len(args) != f[0]+1:
@ -159,6 +161,7 @@ class TemplateFormatter(string.Formatter):
self.book = None
self.kwargs = None
self.program_cache = {}
self.locals = {}
def _do_format(self, val, fmt):
if not fmt or not val:
@ -286,9 +289,9 @@ class TemplateFormatter(string.Formatter):
print args
raise ValueError('Incorrect number of arguments for function '+ fmt[0:p])
if func.arg_count == 1:
val = func.eval(self, self.kwargs, self.book, locals, val).strip()
val = func.eval(self, self.kwargs, self.book, self.locals, val).strip()
else:
val = func.eval(self, self.kwargs, self.book, locals,
val = func.eval(self, self.kwargs, self.book, self.locals,
val, *args).strip()
if val:
val = self._do_format(val, dispfmt)
@ -309,6 +312,7 @@ class TemplateFormatter(string.Formatter):
self.kwargs = kwargs
self.book = book
self.composite_values = {}
self.locals = {}
try:
ans = self.vformat(fmt, [], kwargs).strip()
except Exception, e:

View File

@ -74,6 +74,7 @@ class FormatterFunction(object):
if isinstance(ret, list):
return ','.join(list)
except:
traceback.print_exc()
return _('Function threw exception' + traceback.format_exc())
class BuiltinStrcmp(FormatterFunction):
@ -448,8 +449,13 @@ class FormatterUserFunction(FormatterFunction):
self.arg_count = arg_count
self.program_text = program_text
tabs = re.compile(r'^\t*')
def compile_user_function(name, doc, arg_count, eval_func):
func = '\t' + eval_func.replace('\n', '\n\t')
def replace_func(mo):
return mo.group().replace('\t', ' ')
func = ' ' + '\n '.join([tabs.sub(replace_func, line )
for line in eval_func.splitlines()])
prog = '''
from calibre.utils.formatter_functions import FormatterUserFunction
class UserFunction(FormatterUserFunction):