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 @@ def genesis(self, gui):
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 @@ def __init__(self, val, prog, parent):
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 @@ def statement(self):
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 @@ def expr(self):
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 @@ def __init__(self):
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 @@ def format_field(self, val, fmt):
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 @@ def safe_format(self, fmt, kwargs, error_value, book):
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 @@ def eval(self, formatter, kwargs, mi, locals, *args):
if isinstance(ret, list):
return ','.join(list)
except:
traceback.print_exc()
return _('Function threw exception' + traceback.format_exc())
class BuiltinStrcmp(FormatterFunction):
@ -327,7 +328,7 @@ def evaluate(self, formatter, kwargs, mi, locals, val, value_if_empty):
return value_if_empty
class BuiltinShorten(FormatterFunction):
name = 'shorten '
name = 'shorten'
arg_count = 4
doc = _('shorten(val, left chars, middle text, right chars) -- Return a '
'shortened version of the field, consisting of `left chars` '
@ -448,8 +449,13 @@ def __init__(self, name, doc, arg_count, program_text):
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):