1) allow commas inside arguments

2) add the 'switch' function
3) update the FAQ
This commit is contained in:
Charles Haley 2010-10-02 10:48:45 +01:00
parent 09987522cd
commit deddac1fdc
2 changed files with 19 additions and 2 deletions

View File

@ -108,7 +108,7 @@ Function references appear in the format part, going after the ``:`` and before
Functions are always applied before format specifications. See further down for an example of using both a format and a function, where this order is demonstrated. Functions are always applied before format specifications. See further down for an example of using both a format and a function, where this order is demonstrated.
The syntax for using functions is ``{field:function(arguments)}``, or ``{field:function(arguments)|prefix|suffix}``. Argument values cannot contain a comma, because it is used to separate arguments. The last (or only) argument cannot contain a closing parenthesis ( ')' ). Functions return the value of the field used in the template, suitably modified. The syntax for using functions is ``{field:function(arguments)}``, or ``{field:function(arguments)|prefix|suffix}``. Arguments are separated by commas. Commas inside arguments must be preceeded by a backslash ( '\\' ). The last (or only) argument cannot contain a closing parenthesis ( ')' ). Functions return the value of the field used in the template, suitably modified.
The functions available are: The functions available are:
@ -119,6 +119,7 @@ The functions available are:
* ``ifempty(text)`` -- if the field is not empty, return the value of the field. Otherwise return `text`. * ``ifempty(text)`` -- if the field is not empty, return the value of the field. Otherwise return `text`.
* ``test(text if not empty, text if empty)`` -- return `text if not empty` if the field is not empty, otherwise return `text if empty`. * ``test(text if not empty, text if empty)`` -- return `text if not empty` if the field is not empty, otherwise return `text if empty`.
* ``contains(pattern, text if match, text if not match`` -- checks if field contains matches for the regular expression `pattern`. Returns `text if match` if matches are found, otherwise it returns `text if no match`. * ``contains(pattern, text if match, text if not match`` -- checks if field contains matches for the regular expression `pattern`. Returns `text if match` if matches are found, otherwise it returns `text if no match`.
* ``switch(pattern, value, pattern, value, ..., else_value)`` -- for each ``pattern, value`` pair, checks if the field matches the ``pattern`` and if so, returns that ``value``. If no ``pattern`` matches, then ``else_value`` is returned. You can have as many ``pattern, value`` pairs as you want.
* ``shorten(left chars, middle text, right chars)`` -- Return a shortened version of the field, consisting of `left chars` characters from the beginning of the field, followed by `middle text`, followed by `right chars` characters from the end of the string. `Left chars` and `right chars` must be integers. For example, assume the title of the book is `Ancient English Laws in the Times of Ivanhoe`, and you want it to fit in a space of at most 15 characters. If you use ``{title:shorten(9,-,5)}``, the result will be `Ancient E-nhoe`. If the field's length is less than ``left chars`` + ``right chars`` + the length of ``middle text``, then the field will be used intact. For example, the title `The Dome` would not be changed. * ``shorten(left chars, middle text, right chars)`` -- Return a shortened version of the field, consisting of `left chars` characters from the beginning of the field, followed by `middle text`, followed by `right chars` characters from the end of the string. `Left chars` and `right chars` must be integers. For example, assume the title of the book is `Ancient English Laws in the Times of Ivanhoe`, and you want it to fit in a space of at most 15 characters. If you use ``{title:shorten(9,-,5)}``, the result will be `Ancient E-nhoe`. If the field's length is less than ``left chars`` + ``right chars`` + the length of ``middle text``, then the field will be used intact. For example, the title `The Dome` would not be changed.
* ``lookup(field if not empty, field if empty)`` -- like test, except the arguments are field (metadata) names, not text. The value of the appropriate field will be fetched and used. Note that because composite columns are fields, you can use this function in one composite field to use the value of some other composite field. This is extremely useful when constructing variable save paths (more later). * ``lookup(field if not empty, field if empty)`` -- like test, except the arguments are field (metadata) names, not text. The value of the appropriate field will be fetched and used. Note that because composite columns are fields, you can use this function in one composite field to use the value of some other composite field. This is extremely useful when constructing variable save paths (more later).
* ``re(pattern, replacement)`` -- return the field after applying the regular expression. All instances of `pattern` are replaced with `replacement`. As in all of |app|, these are python-compatible regular expressions. * ``re(pattern, replacement)`` -- return the field after applying the regular expression. All instances of `pattern` are replaced with `replacement`. As in all of |app|, these are python-compatible regular expressions.

View File

@ -39,6 +39,15 @@ class TemplateFormatter(string.Formatter):
else: else:
return value_if_not return value_if_not
def _switch(self, val, *args):
i = 0
while i < len(args):
if i + 1 >= len(args):
return args[i]
if re.search(args[i], val):
return args[i+1]
i += 2
def _re(self, val, pattern, replacement): def _re(self, val, pattern, replacement):
return re.sub(pattern, replacement, val) return re.sub(pattern, replacement, val)
@ -66,12 +75,19 @@ class TemplateFormatter(string.Formatter):
'lookup' : (2, _lookup), 'lookup' : (2, _lookup),
're' : (2, _re), 're' : (2, _re),
'shorten' : (3, _shorten), 'shorten' : (3, _shorten),
'switch' : (-1, _switch),
'test' : (2, _test), 'test' : (2, _test),
} }
format_string_re = re.compile(r'^(.*)\|(.*)\|(.*)$') format_string_re = re.compile(r'^(.*)\|(.*)\|(.*)$')
compress_spaces = re.compile(r'\s+') compress_spaces = re.compile(r'\s+')
arg_parser = re.Scanner([
(r',', lambda x,t: ''),
(r'.*?((?<!\\),)', lambda x,t: t[:-1]),
(r'.*?\)', lambda x,t: t[:-1]),
])
def get_value(self, key, args, kwargs): def get_value(self, key, args, kwargs):
raise Exception('get_value must be implemented in the subclass') raise Exception('get_value must be implemented in the subclass')
@ -105,7 +121,7 @@ class TemplateFormatter(string.Formatter):
if fmt[colon:p] in self.functions: if fmt[colon:p] in self.functions:
field = fmt[colon:p] field = fmt[colon:p]
func = self.functions[field] func = self.functions[field]
args = fmt[p+1:-1].split(',') args = self.arg_parser.scan(fmt[p+1:])[0]
if (func[0] == 0 and (len(args) != 1 or args[0])) or \ if (func[0] == 0 and (len(args) != 1 or args[0])) or \
(func[0] > 0 and func[0] != len(args)): (func[0] > 0 and func[0] != len(args)):
raise ValueError('Incorrect number of arguments for function '+ fmt[0:p]) raise ValueError('Incorrect number of arguments for function '+ fmt[0:p])