mirror of
https://github.com/kovidgoyal/calibre.git
synced 2025-11-24 07:23:02 -05:00
170 lines
5.3 KiB
Python
170 lines
5.3 KiB
Python
'''
|
|
Created on 23 Sep 2010
|
|
|
|
@author: charles
|
|
'''
|
|
|
|
import re, string, traceback
|
|
|
|
from calibre.constants import DEBUG
|
|
|
|
class TemplateFormatter(string.Formatter):
|
|
'''
|
|
Provides a format function that substitutes '' for any missing value
|
|
'''
|
|
|
|
# Dict to do recursion detection. It is up the the individual get_value
|
|
# method to use it. It is cleared when starting to format a template
|
|
composite_values = {}
|
|
|
|
def __init__(self):
|
|
string.Formatter.__init__(self)
|
|
self.book = None
|
|
self.kwargs = None
|
|
|
|
def _lookup(self, val, field_if_set, field_not_set):
|
|
if val:
|
|
return self.vformat('{'+field_if_set.strip()+'}', [], self.kwargs)
|
|
else:
|
|
return self.vformat('{'+field_not_set.strip()+'}', [], self.kwargs)
|
|
|
|
def _test(self, val, value_if_set, value_not_set):
|
|
if val:
|
|
return value_if_set
|
|
else:
|
|
return value_not_set
|
|
|
|
def _contains(self, val, test, value_if_present, value_if_not):
|
|
if re.search(test, val):
|
|
return value_if_present
|
|
else:
|
|
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):
|
|
return re.sub(pattern, replacement, val)
|
|
|
|
def _ifempty(self, val, value_if_empty):
|
|
if val:
|
|
return val
|
|
else:
|
|
return value_if_empty
|
|
|
|
def _shorten(self, val, leading, center_string, trailing):
|
|
l = max(0, int(leading))
|
|
t = max(0, int(trailing))
|
|
if len(val) > l + len(center_string) + t:
|
|
return val[0:l] + center_string + ('' if t == 0 else val[-t:])
|
|
else:
|
|
return val
|
|
|
|
functions = {
|
|
'uppercase' : (0, lambda s,x: x.upper()),
|
|
'lowercase' : (0, lambda s,x: x.lower()),
|
|
'titlecase' : (0, lambda s,x: x.title()),
|
|
'capitalize' : (0, lambda s,x: x.capitalize()),
|
|
'contains' : (3, _contains),
|
|
'ifempty' : (1, _ifempty),
|
|
'lookup' : (2, _lookup),
|
|
're' : (2, _re),
|
|
'shorten' : (3, _shorten),
|
|
'switch' : (-1, _switch),
|
|
'test' : (2, _test),
|
|
}
|
|
|
|
format_string_re = re.compile(r'^(.*)\|(.*)\|(.*)$')
|
|
compress_spaces = re.compile(r'\s+')
|
|
backslash_comma_to_comma = re.compile(r'\\,')
|
|
|
|
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):
|
|
raise Exception('get_value must be implemented in the subclass')
|
|
|
|
|
|
def _explode_format_string(self, fmt):
|
|
try:
|
|
matches = self.format_string_re.match(fmt)
|
|
if matches is None or matches.lastindex != 3:
|
|
return fmt, '', ''
|
|
return matches.groups()
|
|
except:
|
|
if DEBUG:
|
|
traceback.print_exc()
|
|
return fmt, '', ''
|
|
|
|
def format_field(self, val, fmt):
|
|
# Handle conditional text
|
|
fmt, prefix, suffix = self._explode_format_string(fmt)
|
|
|
|
# Handle functions
|
|
p = fmt.find('(')
|
|
dispfmt = fmt
|
|
if p >= 0 and fmt[-1] == ')':
|
|
colon = fmt[0:p].find(':')
|
|
if colon < 0:
|
|
dispfmt = ''
|
|
colon = 0
|
|
else:
|
|
dispfmt = fmt[0:colon]
|
|
colon += 1
|
|
if fmt[colon:p] in self.functions:
|
|
field = fmt[colon:p]
|
|
func = self.functions[field]
|
|
args = self.arg_parser.scan(fmt[p+1:])[0]
|
|
args = [self.backslash_comma_to_comma.sub(',', a) for a in args]
|
|
if (func[0] == 0 and (len(args) != 1 or args[0])) or \
|
|
(func[0] > 0 and func[0] != len(args)):
|
|
raise ValueError('Incorrect number of arguments for function '+ fmt[0:p])
|
|
if func[0] == 0:
|
|
val = func[1](self, val)
|
|
else:
|
|
val = func[1](self, val, *args)
|
|
if val:
|
|
val = string.Formatter.format_field(self, val, dispfmt)
|
|
if not val:
|
|
return ''
|
|
return prefix + val + suffix
|
|
|
|
def vformat(self, fmt, args, kwargs):
|
|
ans = string.Formatter.vformat(self, fmt, args, kwargs)
|
|
return self.compress_spaces.sub(' ', ans).strip()
|
|
|
|
def safe_format(self, fmt, kwargs, error_value, book):
|
|
self.kwargs = kwargs
|
|
self.book = book
|
|
self.composite_values = {}
|
|
try:
|
|
ans = self.vformat(fmt, [], kwargs).strip()
|
|
except:
|
|
if DEBUG:
|
|
traceback.print_exc()
|
|
ans = error_value
|
|
return ans
|
|
|
|
class ValidateFormat(TemplateFormatter):
|
|
'''
|
|
Provides a format function that substitutes '' for any missing value
|
|
'''
|
|
def get_value(self, key, args, kwargs):
|
|
return 'this is some text that should be long enough'
|
|
|
|
def validate(self, x):
|
|
return self.vformat(x, [], {})
|
|
|
|
validation_formatter = ValidateFormat()
|
|
|
|
|