diff --git a/resources/template-functions.json b/resources/template-functions.json new file mode 100644 index 0000000000..cb329d771c --- /dev/null +++ b/resources/template-functions.json @@ -0,0 +1,28 @@ +{ + "contains": "def evaluate(self, formatter, kwargs, mi, locals,\n val, test, value_if_present, value_if_not):\n if re.search(test, val):\n return value_if_present\n else:\n return value_if_not\n", + "divide": "def evaluate(self, formatter, kwargs, mi, locals, x, y):\n x = float(x if x else 0)\n y = float(y if y else 0)\n return unicode(x / y)\n", + "uppercase": "def evaluate(self, formatter, kwargs, mi, locals, val):\n return val.upper()\n", + "strcat": "def evaluate(self, formatter, kwargs, mi, locals, *args):\n i = 0\n res = ''\n for i in range(0, len(args)):\n res += args[i]\n return res\n", + "substr": "def evaluate(self, formatter, kwargs, mi, locals, str_, start_, end_):\n return str_[int(start_): len(str_) if int(end_) == 0 else int(end_)]\n", + "ifempty": "def evaluate(self, formatter, kwargs, mi, locals, val, value_if_empty):\n if val:\n return val\n else:\n return value_if_empty\n", + "field": "def evaluate(self, formatter, kwargs, mi, locals, name):\n return formatter.get_value(name, [], kwargs)\n", + "capitalize": "def evaluate(self, formatter, kwargs, mi, locals, val):\n return capitalize(val)\n", + "list_item": "def evaluate(self, formatter, kwargs, mi, locals, val, index, sep):\n if not val:\n return ''\n index = int(index)\n val = val.split(sep)\n try:\n return val[index]\n except:\n return ''\n", + "shorten": "def evaluate(self, formatter, kwargs, mi, locals,\n val, leading, center_string, trailing):\n l = max(0, int(leading))\n t = max(0, int(trailing))\n if len(val) > l + len(center_string) + t:\n return val[0:l] + center_string + ('' if t == 0 else val[-t:])\n else:\n return val\n", + "re": "def evaluate(self, formatter, kwargs, mi, locals, val, pattern, replacement):\n return re.sub(pattern, replacement, val)\n", + "add": "def evaluate(self, formatter, kwargs, mi, locals, x, y):\n x = float(x if x else 0)\n y = float(y if y else 0)\n return unicode(x + y)\n", + "lookup": "def evaluate(self, formatter, kwargs, mi, locals, val, *args):\n if len(args) == 2: # here for backwards compatibility\n if val:\n return formatter.vformat('{'+args[0].strip()+'}', [], kwargs)\n else:\n return formatter.vformat('{'+args[1].strip()+'}', [], kwargs)\n if (len(args) % 2) != 1:\n raise ValueError(_('lookup requires either 2 or an odd number of arguments'))\n i = 0\n while i < len(args):\n if i + 1 >= len(args):\n return formatter.vformat('{' + args[i].strip() + '}', [], kwargs)\n if re.search(args[i], val):\n return formatter.vformat('{'+args[i+1].strip() + '}', [], kwargs)\n i += 2\n", + "template": "def evaluate(self, formatter, kwargs, mi, locals, template):\n template = template.replace('[[', '{').replace(']]', '}')\n return formatter.safe_format(template, kwargs, 'TEMPLATE', mi)\n", + "print": "def evaluate(self, formatter, kwargs, mi, locals, *args):\n print args\n return None\n", + "titlecase": "def evaluate(self, formatter, kwargs, mi, locals, val):\n return titlecase(val)\n", + "test": "def evaluate(self, formatter, kwargs, mi, locals, val, value_if_set, value_not_set):\n if val:\n return value_if_set\n else:\n return value_not_set\n", + "eval": "def evaluate(self, formatter, kwargs, mi, locals, template):\n from formatter import eval_formatter\n template = template.replace('[[', '{').replace(']]', '}')\n return eval_formatter.safe_format(template, locals, 'EVAL', None)\n", + "multiply": "def evaluate(self, formatter, kwargs, mi, locals, x, y):\n x = float(x if x else 0)\n y = float(y if y else 0)\n return unicode(x * y)\n", + "subtract": "def evaluate(self, formatter, kwargs, mi, locals, x, y):\n x = float(x if x else 0)\n y = float(y if y else 0)\n return unicode(x - y)\n", + "count": "def evaluate(self, formatter, kwargs, mi, locals, val, sep):\n return unicode(len(val.split(sep)))\n", + "lowercase": "def evaluate(self, formatter, kwargs, mi, locals, val):\n return val.lower()\n", + "assign": "def evaluate(self, formatter, kwargs, mi, locals, target, value):\n locals[target] = value\n return value\n", + "switch": "def evaluate(self, formatter, kwargs, mi, locals, val, *args):\n if (len(args) % 2) != 1:\n raise ValueError(_('switch requires an odd number of arguments'))\n i = 0\n while i < len(args):\n if i + 1 >= len(args):\n return args[i]\n if re.search(args[i], val):\n return args[i+1]\n i += 2\n", + "strcmp": "def evaluate(self, formatter, kwargs, mi, locals, x, y, lt, eq, gt):\n v = strcmp(x, y)\n if v < 0:\n return lt\n if v == 0:\n return eq\n return gt\n", + "cmp": "def evaluate(self, formatter, kwargs, mi, locals, x, y, lt, eq, gt):\n x = float(x if x else 0)\n y = float(y if y else 0)\n if x < y:\n return lt\n if x == y:\n return eq\n return gt\n" +} \ No newline at end of file diff --git a/setup/resources.py b/setup/resources.py index 972bf93345..eedaf0bf6d 100644 --- a/setup/resources.py +++ b/setup/resources.py @@ -86,19 +86,20 @@ class Resources(Command): self.info('\tCreating template-functions.json') dest = self.j(self.RESOURCES, 'template-functions.json') + function_dict = {} import inspect from calibre.utils.formatter_functions import all_builtin_functions - for cls in all_builtin_functions: - eval_func = inspect.getmembers(self.__class__, + for obj in all_builtin_functions: + eval_func = inspect.getmembers(obj, lambda x: inspect.ismethod(x) and x.__name__ == 'evaluate') try: lines = [l[4:] for l in inspect.getsourcelines(eval_func[0][1])[0]] except: continue lines = ''.join(lines) - function_dict[cls.name] = lines + function_dict[obj.name] = lines import json - json.dump(function_dict, open(dest, 'wb')) + json.dump(function_dict, open(dest, 'wb'), indent=4) def clean(self): for x in ('scripts', 'recipes', 'ebook-convert-complete'): diff --git a/src/calibre/gui2/actions/edit_metadata.py b/src/calibre/gui2/actions/edit_metadata.py index b01ab1ba21..f50251e700 100644 --- a/src/calibre/gui2/actions/edit_metadata.py +++ b/src/calibre/gui2/actions/edit_metadata.py @@ -94,7 +94,7 @@ class EditMetadataAction(InterfaceAction): get_social_metadata = config['get_social_metadata'] else: get_social_metadata = set_social_metadata - from calibre.gui2.metadata import DoDownload + from calibre.gui2.metadata.bulk_download import DoDownload if set_social_metadata is not None and set_social_metadata: x = _('social metadata') else: diff --git a/src/calibre/gui2/metadata/__init__.py b/src/calibre/gui2/metadata/__init__.py new file mode 100644 index 0000000000..68dfb8d2b5 --- /dev/null +++ b/src/calibre/gui2/metadata/__init__.py @@ -0,0 +1,9 @@ +#!/usr/bin/env python +# vim:fileencoding=UTF-8:ts=4:sw=4:sta:et:sts=4:ai + +__license__ = 'GPL v3' +__copyright__ = '2011, Kovid Goyal ' +__docformat__ = 'restructuredtext en' + + + diff --git a/src/calibre/gui2/metadata.py b/src/calibre/gui2/metadata/bulk_download.py similarity index 100% rename from src/calibre/gui2/metadata.py rename to src/calibre/gui2/metadata/bulk_download.py diff --git a/src/calibre/utils/formatter_functions.py b/src/calibre/utils/formatter_functions.py index aef0792c05..8f928cfe87 100644 --- a/src/calibre/utils/formatter_functions.py +++ b/src/calibre/utils/formatter_functions.py @@ -77,28 +77,18 @@ class FormatterFunction(object): exc_traceback)[-2:]).replace('\n', '') return _('Exception ' + info) - +all_builtin_functions = [] class BuiltinFormatterFunction(FormatterFunction): def __init__(self): formatter_functions.register_builtin(self) - try: - # strip off the first character, which is a newline - lines = self.program_text[1:] - except: - lines = '' - self.program_text = lines - - # If we can get the source, check if it is the same as in the string. - # This is to give an indication during testing that the text is wrong. eval_func = inspect.getmembers(self.__class__, lambda x: inspect.ismethod(x) and x.__name__ == 'evaluate') try: lines = [l[4:] for l in inspect.getsourcelines(eval_func[0][1])[0]] except: - return - lines = ''.join(lines) - if lines != self.program_text: - print 'mismatch in program text for function ', self.name + lines = [] + self.program_text = ''.join(lines) + all_builtin_functions.append(self) class BuiltinStrcmp(BuiltinFormatterFunction): name = 'strcmp' @@ -106,15 +96,6 @@ class BuiltinStrcmp(BuiltinFormatterFunction): doc = _('strcmp(x, y, lt, eq, gt) -- does a case-insensitive comparison of x ' 'and y as strings. Returns lt if x < y. Returns eq if x == y. ' 'Otherwise returns gt.') - program_text = r''' -def evaluate(self, formatter, kwargs, mi, locals, x, y, lt, eq, gt): - v = strcmp(x, y) - if v < 0: - return lt - if v == 0: - return eq - return gt -''' def evaluate(self, formatter, kwargs, mi, locals, x, y, lt, eq, gt): v = strcmp(x, y) @@ -129,16 +110,6 @@ class BuiltinCmp(BuiltinFormatterFunction): arg_count = 5 doc = _('cmp(x, y, lt, eq, gt) -- compares x and y after converting both to ' 'numbers. Returns lt if x < y. Returns eq if x == y. Otherwise returns gt.') - program_text = r''' -def evaluate(self, formatter, kwargs, mi, locals, x, y, lt, eq, gt): - x = float(x if x else 0) - y = float(y if y else 0) - if x < y: - return lt - if x == y: - return eq - return gt -''' def evaluate(self, formatter, kwargs, mi, locals, x, y, lt, eq, gt): x = float(x if x else 0) @@ -154,14 +125,6 @@ class BuiltinStrcat(BuiltinFormatterFunction): arg_count = -1 doc = _('strcat(a, b, ...) -- can take any number of arguments. Returns a ' 'string formed by concatenating all the arguments') - program_text = r''' -def evaluate(self, formatter, kwargs, mi, locals, *args): - i = 0 - res = '' - for i in range(0, len(args)): - res += args[i] - return res -''' def evaluate(self, formatter, kwargs, mi, locals, *args): i = 0 @@ -174,12 +137,6 @@ class BuiltinAdd(BuiltinFormatterFunction): name = 'add' arg_count = 2 doc = _('add(x, y) -- returns x + y. Throws an exception if either x or y are not numbers.') - program_text = r''' -def evaluate(self, formatter, kwargs, mi, locals, x, y): - x = float(x if x else 0) - y = float(y if y else 0) - return unicode(x + y) -''' def evaluate(self, formatter, kwargs, mi, locals, x, y): x = float(x if x else 0) @@ -190,12 +147,6 @@ class BuiltinSubtract(BuiltinFormatterFunction): name = 'subtract' arg_count = 2 doc = _('subtract(x, y) -- returns x - y. Throws an exception if either x or y are not numbers.') - program_text = r''' -def evaluate(self, formatter, kwargs, mi, locals, x, y): - x = float(x if x else 0) - y = float(y if y else 0) - return unicode(x - y) -''' def evaluate(self, formatter, kwargs, mi, locals, x, y): x = float(x if x else 0) @@ -206,12 +157,6 @@ class BuiltinMultiply(BuiltinFormatterFunction): name = 'multiply' arg_count = 2 doc = _('multiply(x, y) -- returns x * y. Throws an exception if either x or y are not numbers.') - program_text = r''' -def evaluate(self, formatter, kwargs, mi, locals, x, y): - x = float(x if x else 0) - y = float(y if y else 0) - return unicode(x * y) -''' def evaluate(self, formatter, kwargs, mi, locals, x, y): x = float(x if x else 0) @@ -222,12 +167,6 @@ class BuiltinDivide(BuiltinFormatterFunction): name = 'divide' arg_count = 2 doc = _('divide(x, y) -- returns x / y. Throws an exception if either x or y are not numbers.') - program_text = r''' -def evaluate(self, formatter, kwargs, mi, locals, x, y): - x = float(x if x else 0) - y = float(y if y else 0) - return unicode(x / y) -''' def evaluate(self, formatter, kwargs, mi, locals, x, y): x = float(x if x else 0) @@ -244,11 +183,6 @@ class BuiltinTemplate(BuiltinFormatterFunction): ']] for the } character; they are converted automatically. ' 'For example, template(\'[[title_sort]]\') will evaluate the ' 'template {title_sort} and return its value.') - program_text = r''' -def evaluate(self, formatter, kwargs, mi, locals, template): - template = template.replace('[[', '{').replace(']]', '}') - return formatter.safe_format(template, kwargs, 'TEMPLATE', mi) -''' def evaluate(self, formatter, kwargs, mi, locals, template): template = template.replace('[[', '{').replace(']]', '}') @@ -261,12 +195,6 @@ class BuiltinEval(BuiltinFormatterFunction): 'variables (those \'assign\'ed to) instead of the book metadata. ' ' This permits using the template processor to construct complex ' 'results from local variables.') - program_text = r''' -def evaluate(self, formatter, kwargs, mi, locals, template): - from formatter import eval_formatter - template = template.replace('[[', '{').replace(']]', '}') - return eval_formatter.safe_format(template, locals, 'EVAL', None) -''' def evaluate(self, formatter, kwargs, mi, locals, template): from formatter import eval_formatter @@ -278,11 +206,6 @@ class BuiltinAssign(BuiltinFormatterFunction): arg_count = 2 doc = _('assign(id, val) -- assigns val to id, then returns val. ' 'id must be an identifier, not an expression') - program_text = r''' -def evaluate(self, formatter, kwargs, mi, locals, target, value): - locals[target] = value - return value -''' def evaluate(self, formatter, kwargs, mi, locals, target, value): locals[target] = value @@ -294,11 +217,6 @@ class BuiltinPrint(BuiltinFormatterFunction): doc = _('print(a, b, ...) -- prints the arguments to standard output. ' 'Unless you start calibre from the command line (calibre-debug -g), ' 'the output will go to a black hole.') - program_text = r''' -def evaluate(self, formatter, kwargs, mi, locals, *args): - print args - return None -''' def evaluate(self, formatter, kwargs, mi, locals, *args): print args @@ -308,10 +226,6 @@ class BuiltinField(BuiltinFormatterFunction): name = 'field' arg_count = 1 doc = _('field(name) -- returns the metadata field named by name') - program_text = r''' -def evaluate(self, formatter, kwargs, mi, locals, name): - return formatter.get_value(name, [], kwargs) -''' def evaluate(self, formatter, kwargs, mi, locals, name): return formatter.get_value(name, [], kwargs) @@ -325,10 +239,6 @@ class BuiltinSubstr(BuiltinFormatterFunction): 'characters counting from the right. If end is zero, then it ' 'indicates the last character. For example, substr(\'12345\', 1, 0) ' 'returns \'2345\', and substr(\'12345\', 1, -1) returns \'234\'.') - program_text = r''' -def evaluate(self, formatter, kwargs, mi, locals, str_, start_, end_): - return str_[int(start_): len(str_) if int(end_) == 0 else int(end_)] -''' def evaluate(self, formatter, kwargs, mi, locals, str_, start_, end_): return str_[int(start_): len(str_) if int(end_) == 0 else int(end_)] @@ -343,23 +253,6 @@ class BuiltinLookup(BuiltinFormatterFunction): 'function in one composite field to use the value of some other ' 'composite field. This is extremely useful when constructing ' 'variable save paths') - program_text = r''' -def evaluate(self, formatter, kwargs, mi, locals, val, *args): - if len(args) == 2: # here for backwards compatibility - if val: - return formatter.vformat('{'+args[0].strip()+'}', [], kwargs) - else: - return formatter.vformat('{'+args[1].strip()+'}', [], kwargs) - if (len(args) % 2) != 1: - raise ValueError(_('lookup requires either 2 or an odd number of arguments')) - i = 0 - while i < len(args): - if i + 1 >= len(args): - return formatter.vformat('{' + args[i].strip() + '}', [], kwargs) - if re.search(args[i], val): - return formatter.vformat('{'+args[i+1].strip() + '}', [], kwargs) - i += 2 -''' def evaluate(self, formatter, kwargs, mi, locals, val, *args): if len(args) == 2: # here for backwards compatibility @@ -382,13 +275,6 @@ class BuiltinTest(BuiltinFormatterFunction): arg_count = 3 doc = _('test(val, text if not empty, text if empty) -- return `text if not ' 'empty` if the field is not empty, otherwise return `text if empty`') - program_text = r''' -def evaluate(self, formatter, kwargs, mi, locals, val, value_if_set, value_not_set): - if val: - return value_if_set - else: - return value_not_set -''' def evaluate(self, formatter, kwargs, mi, locals, val, value_if_set, value_not_set): if val: @@ -403,14 +289,6 @@ class BuiltinContains(BuiltinFormatterFunction): 'if field contains matches for the regular expression `pattern`. ' 'Returns `text if match` if matches are found, otherwise it returns ' '`text if no match`') - program_text = r''' -def evaluate(self, formatter, kwargs, mi, locals, - val, test, value_if_present, value_if_not): - if re.search(test, val): - return value_if_present - else: - return value_if_not -''' def evaluate(self, formatter, kwargs, mi, locals, val, test, value_if_present, value_if_not): @@ -427,18 +305,6 @@ class BuiltinSwitch(BuiltinFormatterFunction): 'the regular expression `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') - program_text = r''' -def evaluate(self, formatter, kwargs, mi, locals, val, *args): - if (len(args) % 2) != 1: - raise ValueError(_('switch requires an odd number of arguments')) - 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 evaluate(self, formatter, kwargs, mi, locals, val, *args): if (len(args) % 2) != 1: @@ -458,10 +324,6 @@ class BuiltinRe(BuiltinFormatterFunction): 'the regular expression. All instances of `pattern` are replaced ' 'with `replacement`. As in all of calibre, these are ' 'python-compatible regular expressions') - program_text = r''' -def evaluate(self, formatter, kwargs, mi, locals, val, pattern, replacement): - return re.sub(pattern, replacement, val) -''' def evaluate(self, formatter, kwargs, mi, locals, val, pattern, replacement): return re.sub(pattern, replacement, val) @@ -471,13 +333,6 @@ class BuiltinIfempty(BuiltinFormatterFunction): arg_count = 2 doc = _('ifempty(val, text if empty) -- return val if val is not empty, ' 'otherwise return `text if empty`') - program_text = r''' -def evaluate(self, formatter, kwargs, mi, locals, val, value_if_empty): - if val: - return val - else: - return value_if_empty -''' def evaluate(self, formatter, kwargs, mi, locals, val, value_if_empty): if val: @@ -500,16 +355,6 @@ class BuiltinShorten(BuiltinFormatterFunction): '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.') - program_text = r''' -def evaluate(self, formatter, kwargs, mi, locals, - 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 -''' def evaluate(self, formatter, kwargs, mi, locals, val, leading, center_string, trailing): @@ -527,10 +372,6 @@ class BuiltinCount(BuiltinFormatterFunction): 'separated by `separator`, returning the number of items in the ' 'list. Most lists use a comma as the separator, but authors ' 'uses an ampersand. Examples: {tags:count(,)}, {authors:count(&)}') - program_text = r''' -def evaluate(self, formatter, kwargs, mi, locals, val, sep): - return unicode(len(val.split(sep))) -''' def evaluate(self, formatter, kwargs, mi, locals, val, sep): return unicode(len(val.split(sep))) @@ -544,17 +385,6 @@ class BuiltinListitem(BuiltinFormatterFunction): 'using `list_item(-1,separator)`. If the item is not in the list, ' 'then the empty value is returned. The separator has the same ' 'meaning as in the count function.') - program_text = r''' -def evaluate(self, formatter, kwargs, mi, locals, val, index, sep): - if not val: - return '' - index = int(index) - val = val.split(sep) - try: - return val[index] - except: - return '' -''' def evaluate(self, formatter, kwargs, mi, locals, val, index, sep): if not val: @@ -570,10 +400,6 @@ class BuiltinUppercase(BuiltinFormatterFunction): name = 'uppercase' arg_count = 1 doc = _('uppercase(val) -- return value of the field in upper case') - program_text = r''' -def evaluate(self, formatter, kwargs, mi, locals, val): - return val.upper() -''' def evaluate(self, formatter, kwargs, mi, locals, val): return val.upper() @@ -582,10 +408,6 @@ class BuiltinLowercase(BuiltinFormatterFunction): name = 'lowercase' arg_count = 1 doc = _('lowercase(val) -- return value of the field in lower case') - program_text = r''' -def evaluate(self, formatter, kwargs, mi, locals, val): - return val.lower() -''' def evaluate(self, formatter, kwargs, mi, locals, val): return val.lower() @@ -594,10 +416,6 @@ class BuiltinTitlecase(BuiltinFormatterFunction): name = 'titlecase' arg_count = 1 doc = _('titlecase(val) -- return value of the field in title case') - program_text = r''' -def evaluate(self, formatter, kwargs, mi, locals, val): - return titlecase(val) -''' def evaluate(self, formatter, kwargs, mi, locals, val): return titlecase(val) @@ -606,23 +424,36 @@ class BuiltinCapitalize(BuiltinFormatterFunction): name = 'capitalize' arg_count = 1 doc = _('capitalize(val) -- return value of the field capitalized') - program_text = r''' -def evaluate(self, formatter, kwargs, mi, locals, val): - return capitalize(val) -''' def evaluate(self, formatter, kwargs, mi, locals, val): return capitalize(val) -all_builtin_functions = [ - BuiltinAdd(), BuiltinAssign(), BuiltinCapitalize(), BuiltinCmp(), - BuiltinContains(), BuiltinCount(), BuiltinDivide(), BuiltinEval(), - BuiltinIfempty(), BuiltinField(), BuiltinListitem(), BuiltinLookup(), - BuiltinLowercase(), BuiltinMultiply(), BuiltinPrint(), BuiltinRe(), - BuiltinShorten(), BuiltinStrcat(), BuiltinStrcmp(), BuiltinSubstr(), - BuiltinSubtract(), BuiltinSwitch(), BuiltinTemplate(), BuiltinTest(), - BuiltinTitlecase(), BuiltinUppercase(), - ] +builtin_add = BuiltinAdd() +builtin_assign = BuiltinAssign() +builtin_capitalize = BuiltinCapitalize() +builtin_cmp = BuiltinCmp() +builtin_contains = BuiltinContains() +builtin_count = BuiltinCount() +builtin_divide = BuiltinDivide() +builtin_eval = BuiltinEval() +builtin_ifempty = BuiltinIfempty() +builtin_field = BuiltinField() +builtin_list_item = BuiltinListitem() +builtin_lookup = BuiltinLookup() +builtin_lowercase = BuiltinLowercase() +builtin_multiply = BuiltinMultiply() +builtin_print = BuiltinPrint() +builtin_re = BuiltinRe() +builtin_shorten = BuiltinShorten() +builtin_strcat = BuiltinStrcat() +builtin_strcmp = BuiltinStrcmp() +builtin_substr = BuiltinSubstr() +builtin_subtract = BuiltinSubtract() +builtin_switch = BuiltinSwitch() +builtin_template = BuiltinTemplate() +builtin_test = BuiltinTest() +builtin_titlecase = BuiltinTitlecase() +builtin_uppercase = BuiltinUppercase() class FormatterUserFunction(FormatterFunction): def __init__(self, name, doc, arg_count, program_text):