From b638823ac07750a392d4a90bfb3b79b076bd712a Mon Sep 17 00:00:00 2001 From: Charles Haley Date: Mon, 11 Nov 2024 16:17:10 +0000 Subject: [PATCH] Now using your suggestion of overriding "_()" --- src/calibre/gui2/dialogs/template_dialog.py | 34 +- src/calibre/utils/formatter_functions.py | 350 +++++++------------- 2 files changed, 145 insertions(+), 239 deletions(-) diff --git a/src/calibre/gui2/dialogs/template_dialog.py b/src/calibre/gui2/dialogs/template_dialog.py index 69151e42a4..2c2b053a45 100644 --- a/src/calibre/gui2/dialogs/template_dialog.py +++ b/src/calibre/gui2/dialogs/template_dialog.py @@ -62,8 +62,8 @@ from calibre.utils.resources import get_path as P class DocViewer(Dialog): - def __init__(self, docs_dsl, builtins, function_type_string_method, parent=None): - self.docs_dsl = docs_dsl + def __init__(self, ffml, builtins, function_type_string_method, parent=None): + self.ffml = ffml self.builtins = builtins self.function_type_string = function_type_string_method self.last_operation = None @@ -102,18 +102,18 @@ class DocViewer(Dialog): def header_line(self, name): return f'\n

{name} ({self.function_type_string(name, longform=False)})

\n' - def doc_from_class(self, cls): - return cls.raw_doc if self.english_cb.isChecked() and hasattr(cls, 'raw_doc') else cls.doc + def get_doc(self, func): + doc = func.doc if hasattr(func, 'doc') else '' + return doc.raw_text if self.english_cb.isChecked() and hasattr(doc, 'raw_text') else doc - def show_function(self, function_name): - self.last_operation = partial(self.show_function, function_name) - if function_name not in self.builtins or not self.builtins[function_name].doc: - self.set_html(self.header_line(function_name) + - ('No documentation provided')) + def show_function(self, fname): + self.last_operation = partial(self.show_function, fname) + bif = self.builtins[fname] + if fname not in self.builtins or not bif.doc: + self.set_html(self.header_line(fname) + ('No documentation provided')) else: - self.set_html(self.header_line(function_name) + - self.docs_dsl.document_to_html( - self.doc_from_class(self.builtins[function_name]), function_name)) + self.set_html(self.header_line(fname) + + self.ffml.document_to_html(self.get_doc(bif), fname)) def show_all_functions(self): self.last_operation = self.show_all_functions @@ -122,11 +122,11 @@ class DocViewer(Dialog): for name in sorted(self.builtins): a(self.header_line(name)) try: - doc = self.doc_from_class(cls = self.builtins[name]) + doc = self.get_doc(self.builtins[name]) if not doc: a(_('No documentation provided')) else: - a(self.docs_dsl.document_to_html(doc.strip(), name)) + a(self.ffml.document_to_html(doc.strip(), name)) except Exception: print('Exception in', name) raise @@ -441,7 +441,7 @@ class TemplateDialog(QDialog, Ui_TemplateDialog): self.setupUi(self) self.setWindowIcon(self.windowIcon()) - self.docs_dsl = FFMLProcessor() + self.ffml = FFMLProcessor() self.dialog_number = dialog_number self.coloring = color_field is not None self.iconing = icon_field_key is not None @@ -603,7 +603,7 @@ class TemplateDialog(QDialog, Ui_TemplateDialog): def open_documentation_viewer(self): if self.doc_viewer is None: - dv = self.doc_viewer = DocViewer(self.docs_dsl, self.all_functions, + dv = self.doc_viewer = DocViewer(self.ffml, self.all_functions, self.function_type_string, parent=self) dv.finished.connect(self.doc_viewer_finished) dv.show() @@ -1048,7 +1048,7 @@ def evaluate(book, context): self.func_type.clear() if name in self.all_functions: doc = self.all_functions[name].doc.strip() - self.documentation.setHtml(self.docs_dsl.document_to_html(doc, name)) + self.documentation.setHtml(self.ffml.document_to_html(doc, name)) if self.doc_viewer is not None: self.doc_viewer.show_function(name) if name in self.builtins and name in self.builtin_source_dict: diff --git a/src/calibre/utils/formatter_functions.py b/src/calibre/utils/formatter_functions.py index 08ea2348c1..c1636968b4 100644 --- a/src/calibre/utils/formatter_functions.py +++ b/src/calibre/utils/formatter_functions.py @@ -34,11 +34,24 @@ from calibre.utils.config import tweaks from calibre.utils.date import UNDEFINED_DATE, format_date, now, parse_date from calibre.utils.icu import capitalize, sort_key, strcmp from calibre.utils.icu import lower as icu_lower -from calibre.utils.localization import _, calibre_langcode_to_name, canonicalize_lang +from calibre.utils.localization import _ as xlated, calibre_langcode_to_name, canonicalize_lang from calibre.utils.titlecase import titlecase from polyglot.builtins import iteritems, itervalues +# Class and method to save an untranslated copy of translated strings +class TranslatedStringWithRaw(str): + + def __new__(cls, txt): + instance = super().__new__(cls, xlated(txt)) + instance.raw_text = txt + return instance + + +def _(txt): + return TranslatedStringWithRaw(txt) + + class StoredObjectType(Enum): PythonFunction = auto() StoredGPMTemplate = auto() @@ -48,7 +61,7 @@ class StoredObjectType(Enum): class FormatterFunctions: error_function_body = ('def evaluate(self, formatter, kwargs, mi, locals):\n' - '\treturn "' + + '\treturn "' + _('Duplicate user function name {0}. ' 'Change the name or ensure that the functions are identical') + '"') @@ -218,14 +231,13 @@ class BuiltinStrcmp(BuiltinFormatterFunction): name = 'strcmp' arg_count = 5 category = 'Relational' - raw_doc = ( + __doc__ = doc = _( r''' ``strcmp(x, y, lt, eq, gt)`` -- does a case-insensitive lexical comparison of ``x`` and ``y``. Returns ``lt`` if ``x < y``, ``eq`` if ``x == y``, otherwise ``gt``. This function can often be replaced by one of the lexical comparison operators (``==``, ``>``, ``<``, etc.) ''') - __doc__ = doc = _(raw_doc) def evaluate(self, formatter, kwargs, mi, locals, x, y, lt, eq, gt): v = strcmp(x, y) @@ -240,7 +252,7 @@ class BuiltinStrcmpcase(BuiltinFormatterFunction): name = 'strcmpcase' arg_count = 5 category = 'Relational' - raw_doc = ( + __doc__ = doc = _( r''' ``strcmpcase(x, y, lt, eq, gt)`` -- does a case-sensitive lexical comparison of ``x`` and ``y``. Returns ``lt`` if ``x < y``, ``eq`` if ``x == y``, otherwise @@ -250,7 +262,6 @@ Note: This is NOT the default behavior used by calibre, for example, in the lexical comparison operators (``==``, ``>``, ``<``, etc.). This function could cause unexpected results, preferably use ``strcmp()`` whenever possible. ''') - __doc__ = doc = _(raw_doc) def evaluate(self, formatter, kwargs, mi, locals, x, y, lt, eq, gt): from calibre.utils.icu import case_sensitive_strcmp as case_strcmp @@ -266,14 +277,13 @@ class BuiltinCmp(BuiltinFormatterFunction): name = 'cmp' category = 'Relational' arg_count = 5 - raw_doc = ( + __doc__ = doc = _( r''' ``cmp(x, y, lt, eq, gt)`` -- compares ``x`` and ``y`` after converting both to numbers. Returns ``lt`` if ``x <# y``, ``eq`` if ``x ==# y``, otherwise ``gt``. This function can usually be replaced with one of the numeric compare operators (``==#``, ``<#``, ``>#``, etc). ''') - __doc__ = doc = _(raw_doc) def evaluate(self, formatter, kwargs, mi, locals, x, y, lt, eq, gt): x = float(x if x and x != 'None' else 0) @@ -289,7 +299,7 @@ class BuiltinFirstMatchingCmp(BuiltinFormatterFunction): name = 'first_matching_cmp' category = 'Relational' arg_count = -1 - raw_doc = ( + __doc__ = doc = _( r''' ``first_matching_cmp(val, [ cmp, result, ]* else_result)`` -- compares ``val < cmp`` in sequence, returning the associated result for the first comparison that @@ -302,7 +312,6 @@ first_matching_cmp(i,5,"small",10,"middle",15,"large","giant") [/CODE] returns ``"large"``. The same example with a first value of 16 returns ``"giant"``. ''') - __doc__ = doc = _(raw_doc) def evaluate(self, formatter, kwargs, mi, locals, *args): if (len(args) % 2) != 0: @@ -319,12 +328,11 @@ class BuiltinStrcat(BuiltinFormatterFunction): name = 'strcat' arg_count = -1 category = 'String manipulation' - raw_doc = ( + __doc__ = doc = _( r''' ``strcat(a [, b]*)`` -- can take any number of arguments. Returns a string formed by concatenating all the arguments. ''') - __doc__ = doc = _(raw_doc) def evaluate(self, formatter, kwargs, mi, locals, *args): i = 0 @@ -338,11 +346,10 @@ class BuiltinStrlen(BuiltinFormatterFunction): name = 'strlen' arg_count = 1 category = 'String manipulation' - raw_doc = ( + __doc__ = doc = _( r''' ``strlen(value)`` -- Returns the length of the string ``value``. ''') - __doc__ = doc = _(raw_doc) def evaluate(self, formatter, kwargs, mi, locals, a): try: @@ -355,13 +362,12 @@ class BuiltinAdd(BuiltinFormatterFunction): name = 'add' arg_count = -1 category = 'Arithmetic' - raw_doc = ( + __doc__ = doc = _( r''' ``add(x [, y]*)`` -- returns the sum of its arguments. Throws an exception if an argument is not a number. In most cases you can use the ``+`` operator instead of this function. ''') - __doc__ = doc = _(raw_doc) def evaluate(self, formatter, kwargs, mi, locals, *args): res = 0 @@ -375,13 +381,12 @@ class BuiltinSubtract(BuiltinFormatterFunction): name = 'subtract' arg_count = 2 category = 'Arithmetic' - raw_doc = ( + __doc__ = doc = _( r''' ``subtract(x, y)`` -- returns ``x - y``. Throws an exception if either ``x`` or ``y`` are not numbers. This function can usually be replaced by the ``-`` operator. ''') - __doc__ = doc = _(raw_doc) def evaluate(self, formatter, kwargs, mi, locals, x, y): x = float(x if x and x != 'None' else 0) @@ -393,13 +398,12 @@ class BuiltinMultiply(BuiltinFormatterFunction): name = 'multiply' arg_count = -1 category = 'Arithmetic' - raw_doc = ( + __doc__ = doc = _( r''' ``multiply(x [, y]*)`` -- returns the product of its arguments. Throws an exception if any argument is not a number. This function can usually be replaced by the ``*`` operator. ''') - __doc__ = doc = _(raw_doc) def evaluate(self, formatter, kwargs, mi, locals, *args): res = 1 @@ -413,13 +417,12 @@ class BuiltinDivide(BuiltinFormatterFunction): name = 'divide' arg_count = 2 category = 'Arithmetic' - raw_doc = ( + __doc__ = doc = _( r''' ``divide(x, y)`` -- returns ``x / y``. Throws an exception if either ``x`` or ``y`` are not numbers. This function can usually be replaced by the ``/`` operator. ''') - __doc__ = doc = _(raw_doc) def evaluate(self, formatter, kwargs, mi, locals, x, y): x = float(x if x and x != 'None' else 0) @@ -431,12 +434,11 @@ class BuiltinCeiling(BuiltinFormatterFunction): name = 'ceiling' arg_count = 1 category = 'Arithmetic' - raw_doc = ( + __doc__ = doc = _( r''' ``ceiling(x)`` -- returns the smallest integer greater than or equal to ``x``. Throws an exception if ``x`` is not a number. ''') - __doc__ = doc = _(raw_doc) def evaluate(self, formatter, kwargs, mi, locals, x): x = float(x if x and x != 'None' else 0) @@ -447,12 +449,11 @@ class BuiltinFloor(BuiltinFormatterFunction): name = 'floor' arg_count = 1 category = 'Arithmetic' - raw_doc = ( + __doc__ = doc = _( r''' ``floor(x)`` -- returns the largest integer less than or equal to ``x``. Throws an exception if ``x`` is not a number. ''') - __doc__ = doc = _(raw_doc) def evaluate(self, formatter, kwargs, mi, locals, x): x = float(x if x and x != 'None' else 0) @@ -463,12 +464,11 @@ class BuiltinRound(BuiltinFormatterFunction): name = 'round' arg_count = 1 category = 'Arithmetic' - raw_doc = ( + __doc__ = doc = _( r''' ``round(x)`` -- returns the nearest integer to ``x``. Throws an exception if ``x`` is not a number. ''') - __doc__ = doc = _(raw_doc) def evaluate(self, formatter, kwargs, mi, locals, x): x = float(x if x and x != 'None' else 0) @@ -479,12 +479,11 @@ class BuiltinMod(BuiltinFormatterFunction): name = 'mod' arg_count = 2 category = 'Arithmetic' - raw_doc = ( + __doc__ = doc = _( r''' ``mod(x, y)`` -- returns the ``floor`` of the remainder of ``x / y``. Throws an exception if either ``x`` or ``y`` is not a number. ''') - __doc__ = doc = _(raw_doc) def evaluate(self, formatter, kwargs, mi, locals, x, y): x = float(x if x and x != 'None' else 0) @@ -496,13 +495,12 @@ class BuiltinFractionalPart(BuiltinFormatterFunction): name = 'fractional_part' arg_count = 1 category = 'Arithmetic' - raw_doc = ( + __doc__ = doc = _( r''' ``fractional_part(value)`` -- returns the part of the value after the decimal point. For example, ``fractional_part(3.14)`` returns ``0.14``. Throws an exception if ``value`` is not a number. ''') - __doc__ = doc = _(raw_doc) def evaluate(self, formatter, kwargs, mi, locals, x): x = float(x if x and x != 'None' else 0) @@ -514,7 +512,7 @@ class BuiltinTemplate(BuiltinFormatterFunction): arg_count = 1 category = 'Recursion' - raw_doc = ( + __doc__ = doc = _( r''' ``template(x)`` -- evaluates ``x`` as a template. The evaluation is done in its own context, meaning that variables are not shared between the caller and the @@ -526,7 +524,6 @@ its value. Note also that prefixes and suffixes (the ``|prefix|suffix`` syntax) cannot be used in the argument to this function when using template program mode. ''') - __doc__ = doc = _(raw_doc) def evaluate(self, formatter, kwargs, mi, locals, template): template = template.replace('[[', '{').replace(']]', '}') @@ -537,7 +534,7 @@ class BuiltinEval(BuiltinFormatterFunction): name = 'eval' arg_count = 1 category = 'Recursion' - raw_doc = ( + __doc__ = doc = _( r''' ``eval(string)`` -- evaluates the string as a program, passing the local variables. This permits using the template processor to construct complex @@ -550,7 +547,6 @@ character. They are converted automatically. Note also that prefixes and suffixes (the ``|prefix|suffix`` syntax) cannot be used in the argument to this function when using Template Program Mode. ''') - __doc__ = doc = _(raw_doc) def evaluate(self, formatter, kwargs, mi, locals, template): from calibre.utils.formatter import EvalFormatter @@ -562,13 +558,12 @@ class BuiltinAssign(BuiltinFormatterFunction): name = 'assign' arg_count = 2 category = 'Other' - raw_doc = ( + __doc__ = doc = _( r''' ``assign(id, value)`` -- assigns ``value`` to ``id``, then returns ``value``. ``id`` must be an identifier, not an expression. In most cases you can use the ``=`` operator instead of this function. ''') - __doc__ = doc = _(raw_doc) def evaluate(self, formatter, kwargs, mi, locals, target, value): locals[target] = value @@ -579,7 +574,7 @@ class BuiltinListSplit(BuiltinFormatterFunction): name = 'list_split' arg_count = 3 category = 'List manipulation' - raw_doc = ( + __doc__ = doc = _( r''' ``list_split(list_val, sep, id_prefix)`` -- splits ``list_val`` into separate values using ``sep``, then assigns the values to local variables named @@ -597,7 +592,6 @@ is equivalent to: var_2 = 'foo [/CODE] ''') - __doc__ = doc = _(raw_doc) def evaluate(self, formatter, kwargs, mi, locals, list_val, sep, id_prefix): l = [v.strip() for v in list_val.split(sep)] @@ -611,13 +605,12 @@ class BuiltinPrint(BuiltinFormatterFunction): name = 'print' arg_count = -1 category = 'Other' - raw_doc = ( + __doc__ = doc = _( r''' ``print(a [, b]*)`` -- prints the arguments to standard output. Unless you start calibre from the command line (``calibre-debug -g``), the output will go into a black hole. The ``print`` function always returns its first argument. ''') - __doc__ = doc = _(raw_doc) def evaluate(self, formatter, kwargs, mi, locals, *args): print(args) @@ -628,11 +621,10 @@ class BuiltinField(BuiltinFormatterFunction): name = 'field' arg_count = 1 category = 'Get values from metadata' - raw_doc = ( + __doc__ = doc = _( r''' ``field(lookup_name)`` -- returns the value of the metadata field with lookup name ``lookup_name``. ''') - __doc__ = doc = _(raw_doc) def evaluate(self, formatter, kwargs, mi, locals, name): return formatter.get_value(name, [], kwargs) @@ -642,7 +634,7 @@ class BuiltinRawField(BuiltinFormatterFunction): name = 'raw_field' arg_count = -1 category = 'Get values from metadata' - raw_doc = ( + __doc__ = doc = _( r''' ``raw_field(lookup_name [, optional_default])`` -- returns the metadata field named by ``lookup_name`` without applying any formatting. It evaluates and @@ -650,7 +642,6 @@ returns the optional second argument ``optional_default`` if the field's value is undefined (``None``). The ``$$`` prefix can be used instead of the function, as in ``$$pubdate``. ''') - __doc__ = doc = _(raw_doc) def evaluate(self, formatter, kwargs, mi, locals, name, default=None): res = getattr(mi, name, None) @@ -668,13 +659,12 @@ class BuiltinRawList(BuiltinFormatterFunction): name = 'raw_list' arg_count = 2 category = 'Get values from metadata' - raw_doc = ( + __doc__ = doc = _( r''' ``raw_list(lookup_name, separator)`` -- returns the metadata list named by ``lookup_name`` without applying any formatting or sorting, with the items separated by separator. ''') - __doc__ = doc = _(raw_doc) def evaluate(self, formatter, kwargs, mi, locals, name, separator): res = getattr(mi, name, None) @@ -687,7 +677,7 @@ class BuiltinSubstr(BuiltinFormatterFunction): name = 'substr' arg_count = 3 category = 'String manipulation' - raw_doc = ( + __doc__ = doc = _( r''' ``substr(str, start, end)`` -- returns the ``start``'th through the ``end``'th characters of ``str``. The first character in ``str`` is the zero'th character. @@ -696,7 +686,6 @@ 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'``. ''') - __doc__ = doc = _(raw_doc) def evaluate(self, formatter, kwargs, mi, locals, str_, start_, end_): return str_[int(start_): len(str_) if int(end_) == 0 else int(end_)] @@ -706,14 +695,13 @@ class BuiltinLookup(BuiltinFormatterFunction): name = 'lookup' arg_count = -1 category = 'Iterating over values' - raw_doc = ( + __doc__ = doc = _( r''' ``lookup(value, [ pattern, key, ]* else_key)`` -- The patterns will be checked against the value in order. If a pattern matches then the value of the field named by ``key`` is returned. If no pattern matches then the value of the field named by ``else_key`` is returned. See also the ``switch()`` function. ''') - __doc__ = doc = _(raw_doc) def evaluate(self, formatter, kwargs, mi, locals, val, *args): if len(args) == 2: # here for backwards compatibility @@ -736,12 +724,11 @@ class BuiltinTest(BuiltinFormatterFunction): name = 'test' arg_count = 3 category = 'If-then-else' - raw_doc = ( + __doc__ = doc = _( r''' ``test(value, text if not empty, text if empty)`` -- return ``text if not empty`` if the value is not empty, otherwise return ``text if empty``. ''') - __doc__ = doc = _(raw_doc) def evaluate(self, formatter, kwargs, mi, locals, val, value_if_set, value_not_set): if val: @@ -754,13 +741,12 @@ class BuiltinContains(BuiltinFormatterFunction): name = 'contains' arg_count = 4 category = 'If-then-else' - raw_doc = ( + __doc__ = doc = _( r''' ``contains(value, pattern, text if match, text if not match)`` -- checks if the value is matched by the regular expression ``pattern``. Returns ``text if match`` if the pattern matches the value, otherwise returns ``text if no match``. ''') - __doc__ = doc = _(raw_doc) def evaluate(self, formatter, kwargs, mi, locals, val, test, value_if_present, value_if_not): @@ -774,7 +760,7 @@ class BuiltinSwitch(BuiltinFormatterFunction): name = 'switch' arg_count = -1 category = 'Iterating over values' - raw_doc = ( + __doc__ = doc = _( r''' ``switch(value, [pattern, value,]+ else_value)`` -- for each ``pattern, value`` pair, checks if the value matches the regular expression ``pattern`` and if so returns @@ -782,7 +768,6 @@ the associated ``value``. If no ``pattern`` matches, then ``else_value`` is returned. You can have as many ``pattern, value`` pairs as you wish. The first match is returned. ''') - __doc__ = doc = _(raw_doc) def evaluate(self, formatter, kwargs, mi, locals, val, *args): if (len(args) % 2) != 1: @@ -800,7 +785,7 @@ class BuiltinSwitchIf(BuiltinFormatterFunction): name = 'switch_if' arg_count = -1 category = 'Iterating over values' - raw_doc = ( + __doc__ = doc = _( r''' ``switch_if([test_expression, value_expression,]+ else_expression)`` -- for each ``test_expression, value_expression`` pair, checks if ``test_expression`` is @@ -808,7 +793,6 @@ True (non-empty) and if so returns the result of ``value_expression``. If no ``test_expression`` is True then the result of ``else_expression`` is returned. You can have as many ``test_expression, value_expression`` pairs as you want. ''') - __doc__ = doc = _(raw_doc) def evaluate(self, formatter, kwargs, mi, locals, *args): if (len(args) % 2) != 1: @@ -828,7 +812,7 @@ class BuiltinStrcatMax(BuiltinFormatterFunction): name = 'strcat_max' arg_count = -1 category = 'String manipulation' - raw_doc = ( + __doc__ = doc = _( r''' ``strcat_max(max, string1 [, prefix2, string2]*)`` -- Returns a string formed by concatenating the arguments. The returned value is initialized to ``string1``. @@ -837,7 +821,6 @@ long as the resulting string length is less than ``max``. Prefixes can be empty. Returns ``string1`` even if ``string1`` is longer than ``max``. You can pass as many ``prefix, string`` pairs as you wish. ''') - __doc__ = doc = _(raw_doc) def evaluate(self, formatter, kwargs, mi, locals, *args): if len(args) < 2: @@ -866,7 +849,7 @@ class BuiltinInList(BuiltinFormatterFunction): name = 'list_contains' arg_count = -1 category = 'List lookup' - raw_doc = ( + __doc__ = doc = _( r''' ``list_contains(value, separator, [ pattern, found_val, ]* not_found_val)`` -- interpret the value as a list of items separated by ``separator``, checking the ``pattern`` @@ -902,7 +885,7 @@ class BuiltinStrInList(BuiltinFormatterFunction): name = 'str_in_list' arg_count = -1 category = 'List lookup' - raw_doc = ( + __doc__ = doc = _( r''' ``str_in_list(value, separator, [ string, found_val, ]+ not_found_val)`` -- interpret the value as a list of items separated by ``separator`` then compare ``string`` @@ -915,7 +898,6 @@ depending on string's value. If none of the strings match then ``not_found_value`` is returned. The strings are checked in order. The first match is returned. ''') - __doc__ = doc = _(raw_doc) def evaluate(self, formatter, kwargs, mi, locals, val, sep, *args): if (len(args) % 2) != 1: @@ -940,7 +922,7 @@ class BuiltinIdentifierInList(BuiltinFormatterFunction): name = 'identifier_in_list' arg_count = -1 category = 'List lookup' - raw_doc = ( + __doc__ = doc = _( r''' ``identifier_in_list(val, id_name [, found_val, not_found_val])`` -- treat ``val`` as a list of identifiers separated by commas. An identifier has the @@ -953,7 +935,6 @@ return ``found_val``, otherwise return ``not_found_val``. If ``found_val`` and ``not_found_val`` are not provided then if there is a match then return the ``identifier:value`` pair, otherwise the empty string (``''``). ''') - __doc__ = doc = _(raw_doc) def evaluate(self, formatter, kwargs, mi, locals, val, ident, *args): if len(args) == 0: @@ -982,7 +963,7 @@ class BuiltinRe(BuiltinFormatterFunction): name = 're' arg_count = 3 category = 'String manipulation' - raw_doc = ( + __doc__ = doc = _( r''' ``re(value, pattern, replacement)`` -- return the value after applying the regular expression. All instances of ``pattern`` in the value are replaced with @@ -990,7 +971,6 @@ expression. All instances of ``pattern`` in the value are replaced with [URL href="https://docs.python.org/3/library/re.html"]Python regular expressions[/URL]. ''') - __doc__ = doc = _(raw_doc) def evaluate(self, formatter, kwargs, mi, locals, val, pattern, replacement): return re.sub(pattern, replacement, val, flags=re.I) @@ -1000,7 +980,7 @@ class BuiltinReGroup(BuiltinFormatterFunction): name = 're_group' arg_count = -1 category = 'String manipulation' - raw_doc = ( + __doc__ = doc = _( r''' ``re_group(value, pattern [, template_for_group]*)`` -- return a string made by applying the regular expression pattern to ``value`` and replacing each matched @@ -1013,7 +993,6 @@ The following example looks for a series with more than one word and uppercases program: re_group(field('series'), "(\S* )(.*)", "{$:uppercase()}", "{$}")'} [/CODE] ''') - __doc__ = doc = _(raw_doc) def evaluate(self, formatter, kwargs, mi, locals, val, pattern, *args): from calibre.utils.formatter import EvalFormatter @@ -1039,13 +1018,12 @@ class BuiltinSwapAroundComma(BuiltinFormatterFunction): name = 'swap_around_comma' arg_count = 1 category = 'String manipulation' - raw_doc = ( + __doc__ = doc = _( r''' ``swap_around_comma(value)`` -- given a value of the form ``B, A``, return ``A B``. This is most useful for converting names in LN, FN format to FN LN. If there is no comma in the value then the function returns the value unchanged. ''') - __doc__ = doc = _(raw_doc) def evaluate(self, formatter, kwargs, mi, locals, val): return re.sub(r'^(.*?),\s*(.*$)', r'\2 \1', val, flags=re.I).strip() @@ -1055,12 +1033,11 @@ class BuiltinIfempty(BuiltinFormatterFunction): name = 'ifempty' arg_count = 2 category = 'If-then-else' - raw_doc = ( + __doc__ = doc = _( r''' ``ifempty(value, text if empty)`` -- if the value is not empty then return that value, otherwise return ``text if empty``. ''') - __doc__ = doc = _(raw_doc) def evaluate(self, formatter, kwargs, mi, locals, val, value_if_empty): if val: @@ -1073,7 +1050,7 @@ class BuiltinShorten(BuiltinFormatterFunction): name = 'shorten' arg_count = 4 category = 'String manipulation' - raw_doc = ( + __doc__ = doc = _( r''' ``shorten(value, left chars, middle text, right chars)`` -- Return a shortened version of the value, consisting of ``left chars`` characters from the beginning of the @@ -1090,7 +1067,6 @@ length is less than ``left chars`` + ``right chars`` + the length of ``middle te then the value will be returned unchanged. For example, the title `The Dome` would not be changed. ''') - __doc__ = doc = _(raw_doc) def evaluate(self, formatter, kwargs, mi, locals, val, leading, center_string, trailing): @@ -1108,7 +1084,7 @@ class BuiltinCount(BuiltinFormatterFunction): category = 'List manipulation' aliases = ['count'] - raw_doc = ( + __doc__ = doc = _( r''' ``list_count(value, separator)`` -- interprets the value as a list of items separated by ``separator`` and returns the number of items in the list. Most lists use @@ -1118,7 +1094,6 @@ Examples: ``{tags:list_count(,)}``, ``{authors:list_count(&)}``. Aliases: ``count()``, ``list_count()`` ''') - __doc__ = doc = _(raw_doc) def evaluate(self, formatter, kwargs, mi, locals, val, sep): return str(len([v for v in val.split(sep) if v])) @@ -1130,7 +1105,7 @@ class BuiltinListCountMatching(BuiltinFormatterFunction): category = 'List manipulation' aliases = ['count_matching'] - raw_doc = ( + __doc__ = doc = _( r''' ``list_count_matching(list, pattern, separator)`` -- interprets ``list`` as a list of items separated by ``separator``, returning the number of items in the @@ -1138,7 +1113,6 @@ list that match the regular expression ``pattern``. Aliases: ``list_count_matching()``, ``count_matching()`` ''') - __doc__ = doc = _(raw_doc) def evaluate(self, formatter, kwargs, mi, locals, list_, pattern, sep): res = 0 @@ -1152,7 +1126,7 @@ class BuiltinListitem(BuiltinFormatterFunction): name = 'list_item' arg_count = 3 category = 'List lookup' - raw_doc = ( + __doc__ = doc = _( r''' ``list_item(value, index, separator)`` -- interpret the value as a list of items separated by ``separator``, returning the 'index'th item. The first item is @@ -1161,7 +1135,6 @@ number zero. The last item has the index ``-1`` as in string is returned. The separator has the same meaning as in the count function, usually comma but is ampersand for author-like lists. ''') - __doc__ = doc = _(raw_doc) def evaluate(self, formatter, kwargs, mi, locals, val, index, sep): if not val: @@ -1178,7 +1151,7 @@ class BuiltinSelect(BuiltinFormatterFunction): name = 'select' arg_count = 2 category = 'List lookup' - raw_doc = ( + __doc__ = doc = _( r''' ``select(value, key)`` -- interpret the value as a comma-separated list of items with each item having the form ``id:value`` (the calibre ``identifier`` format). The @@ -1186,7 +1159,6 @@ function finds the first pair with the id equal to key and returns the corresponding value. If no id matches then the function returns the empty string. ''') - __doc__ = doc = _(raw_doc) def evaluate(self, formatter, kwargs, mi, locals, val, key): if not val: @@ -1203,7 +1175,7 @@ class BuiltinApproximateFormats(BuiltinFormatterFunction): name = 'approximate_formats' arg_count = 0 category = 'Get values from metadata' - raw_doc = ( + __doc__ = doc = _( r''' ``approximate_formats()`` -- return a comma-separated list of formats associated with the book. Because the list comes from calibre's database instead of the @@ -1217,7 +1189,6 @@ or send-to-device templates then you must make a custom "Column built from other columns", use the function in that column's template, and use that column's value in your save/send templates. ''') - __doc__ = doc = _(raw_doc) def evaluate(self, formatter, kwargs, mi, locals): if hasattr(mi, '_proxy_metadata'): @@ -1233,7 +1204,7 @@ class BuiltinFormatsModtimes(BuiltinFormatterFunction): name = 'formats_modtimes' arg_count = 1 category = 'Get values from metadata' - raw_doc = ( + __doc__ = doc = _( r''' ``formats_modtimes(date_format_string)`` -- return a comma-separated list of colon-separated items ``FMT:DATE`` representing modification times for the @@ -1258,14 +1229,13 @@ class BuiltinFormatsSizes(BuiltinFormatterFunction): name = 'formats_sizes' arg_count = 0 category = 'Get values from metadata' - raw_doc = ( + __doc__ = doc = _( r''' ``formats_sizes()`` -- return a comma-separated list of colon-separated ``FMT:SIZE`` items giving the sizes of the formats of a book in bytes. You can use the ``select()`` function to get the size for a specific format. Note that format names are always uppercase, as in EPUB. ''') - __doc__ = doc = _(raw_doc) def evaluate(self, formatter, kwargs, mi, locals): fmt_data = mi.get('format_metadata', {}) @@ -1279,14 +1249,13 @@ class BuiltinFormatsPaths(BuiltinFormatterFunction): name = 'formats_paths' arg_count = 0 category = 'Get values from metadata' - raw_doc = ( + __doc__ = doc = _( r''' ``formats_paths()`` -- return a comma-separated list of colon-separated items ``FMT:PATH`` giving the full path to the formats of a book. You can use the ``select()`` function to get the path for a specific format. Note that format names are always uppercase, as in EPUB. ''') - __doc__ = doc = _(raw_doc) def evaluate(self, formatter, kwargs, mi, locals): fmt_data = mi.get('format_metadata', {}) @@ -1300,12 +1269,11 @@ class BuiltinHumanReadable(BuiltinFormatterFunction): name = 'human_readable' arg_count = 1 category = 'Formatting values' - raw_doc = ( + __doc__ = doc = _( r''' ``human_readable(value)`` -- expects the value to be a number and returns a string representing that number in KB, MB, GB, etc. ''') - __doc__ = doc = _(raw_doc) def evaluate(self, formatter, kwargs, mi, locals, val): try: @@ -1318,7 +1286,7 @@ class BuiltinFormatNumber(BuiltinFormatterFunction): name = 'format_number' arg_count = 2 category = 'Formatting values' - raw_doc = ( + __doc__ = doc = _( r''' ``format_number(value, template)`` -- interprets the value as a number and formats that number using a Python formatting template such as ``{0:5.2f}`` or ``{0:,d}`` or @@ -1329,7 +1297,6 @@ language and the [URL href="https://docs.python.org/3/library/string.html#format Python documentation[/URL] for more examples. Returns the empty string if formatting fails. ''') - __doc__ = doc = _(raw_doc) def evaluate(self, formatter, kwargs, mi, locals, val, template): if val == '' or val == 'None': @@ -1357,7 +1324,7 @@ class BuiltinSublist(BuiltinFormatterFunction): name = 'sublist' arg_count = 4 category = 'List manipulation' - raw_doc = ( + __doc__ = doc = _( r''' ``sublist(value, start_index, end_index, separator)`` -- interpret the value as a list of items separated by ``separator``, returning a new list made from the @@ -1372,7 +1339,6 @@ Examples assuming that the tags column (which is comma-separated) contains "A, B [*]``{tags:sublist(0,-1,\,)}`` returns "A, B" [/LIST] ''') - __doc__ = doc = _(raw_doc) def evaluate(self, formatter, kwargs, mi, locals, val, start_index, end_index, sep): if not val: @@ -1397,7 +1363,7 @@ class BuiltinSubitems(BuiltinFormatterFunction): name = 'subitems' arg_count = 3 category = 'List manipulation' - raw_doc = ( + __doc__ = doc = _( r''' ``subitems(value, start_index, end_index)`` -- This function breaks apart lists of tag-like hierarchical items such as genres. It interprets the value as a comma- @@ -1455,7 +1421,7 @@ class BuiltinFormatDate(BuiltinFormatterFunction): name = 'format_date' arg_count = 2 category = 'Formatting values' - raw_doc = ( + __doc__ = doc = _( r''' ``format_date(value, format_string)`` -- format the value, which must be a date string, using the format_string, returning a string. It is best if the date is @@ -1496,7 +1462,6 @@ You might get unexpected results if the date you are formatting contains localized month names, which can happen if you changed the date format to contain ``MMMM``. Using ``format_date_field()`` avoids this problem. ''') - __doc__ = doc = _(raw_doc) def evaluate(self, formatter, kwargs, mi, locals, val, format_string): if not val or val == 'None': @@ -1520,7 +1485,7 @@ class BuiltinFormatDateField(BuiltinFormatterFunction): name = 'format_date_field' arg_count = 2 category = 'Formatting values' - raw_doc = ( + __doc__ = doc = _( r''' ``format_date_field(field_name, format_string)`` -- format the value in the field ``field_name``, which must be the lookup name of a date field, either @@ -1534,7 +1499,6 @@ format_date_field('pubdate', 'yyyy.MM.dd') format_date_field('#date_read', 'MMM dd, yyyy') [/CODE] ''') - __doc__ = doc = _(raw_doc) def evaluate(self, formatter, kwargs, mi, locals, field, format_string): try: @@ -1567,11 +1531,10 @@ class BuiltinUppercase(BuiltinFormatterFunction): name = 'uppercase' arg_count = 1 category = 'String case changes' - raw_doc = ( + __doc__ = doc = _( r''' ``uppercase(value)`` -- returns the value in upper case. ''') - __doc__ = doc = _(raw_doc) def evaluate(self, formatter, kwargs, mi, locals, val): return val.upper() @@ -1581,11 +1544,10 @@ class BuiltinLowercase(BuiltinFormatterFunction): name = 'lowercase' arg_count = 1 category = 'String case changes' - raw_doc = ( + __doc__ = doc = _( r''' ``lowercase(value)`` -- returns the value in lower case. ''') - __doc__ = doc = _(raw_doc) def evaluate(self, formatter, kwargs, mi, locals, val): return val.lower() @@ -1595,11 +1557,10 @@ class BuiltinTitlecase(BuiltinFormatterFunction): name = 'titlecase' arg_count = 1 category = 'String case changes' - raw_doc = ( + __doc__ = doc = _( r''' ``titlecase(value)`` -- returns the value in title case. ''') - __doc__ = doc = _(raw_doc) def evaluate(self, formatter, kwargs, mi, locals, val): return titlecase(val) @@ -1609,11 +1570,10 @@ class BuiltinCapitalize(BuiltinFormatterFunction): name = 'capitalize' arg_count = 1 category = 'String case changes' - raw_doc = ( + __doc__ = doc = _( r''' ``capitalize(value)`` -- returns the value with the first letter in upper case and the rest lower case. ''') - __doc__ = doc = _(raw_doc) def evaluate(self, formatter, kwargs, mi, locals, val): return capitalize(val) @@ -1623,7 +1583,7 @@ class BuiltinBooksize(BuiltinFormatterFunction): name = 'booksize' arg_count = 0 category = 'Get values from metadata' - raw_doc = ( + __doc__ = doc = _( r''' ``booksize()`` -- returns the value of the calibre ``size`` field. Returns '' if the book has no formats. @@ -1632,7 +1592,6 @@ or send-to-device templates then you must make a custom "Column built from other columns", use the function in that column's template, and use that column's value in your save/send templates ''') - __doc__ = doc = _(raw_doc) def evaluate(self, formatter, kwargs, mi, locals): if hasattr(mi, '_proxy_metadata'): @@ -1651,7 +1610,7 @@ class BuiltinOndevice(BuiltinFormatterFunction): name = 'ondevice' arg_count = 0 category = 'Get values from metadata' - raw_doc = ( + __doc__ = doc = _( r''' ``ondevice()`` -- return the string ``'Yes'`` if ``ondevice`` is set, otherwise return the empty string. This function works only in the GUI. If you want to use @@ -1659,7 +1618,6 @@ this value in save-to-disk or send-to-device templates then you must make a custom "Column built from other columns", use the function in that column\'s template, and use that column\'s value in your save/send templates. ''') - __doc__ = doc = _(raw_doc) def evaluate(self, formatter, kwargs, mi, locals): if hasattr(mi, '_proxy_metadata'): @@ -1673,12 +1631,11 @@ class BuiltinAnnotationCount(BuiltinFormatterFunction): name = 'annotation_count' arg_count = 0 category = 'Get values from metadata' - raw_doc = ( + __doc__ = doc = _( r''' ``annotation_count()`` -- return the total number of annotations of all types attached to the current book. This function works only in the GUI. ''') - __doc__ = doc = _(raw_doc) def evaluate(self, formatter, kwargs, mi, locals): c = self.get_database(mi).new_api.annotation_count_for_book(mi.id) @@ -1689,14 +1646,13 @@ class BuiltinIsMarked(BuiltinFormatterFunction): name = 'is_marked' arg_count = 0 category = 'Get values from metadata' - raw_doc = ( + __doc__ = doc = _( r''' ``is_marked()`` -- check whether the book is `marked` in calibre. If it is then return the value of the mark, either ``'true'`` (lower case) or a comma-separated list of named marks. Returns ``''`` (the empty string) if the book is not marked. This function works only in the GUI. ''') - __doc__ = doc = _(raw_doc) def evaluate(self, formatter, kwargs, mi, locals): c = self.get_database(mi).data.get_marked(mi.id) @@ -1707,11 +1663,10 @@ class BuiltinSeriesSort(BuiltinFormatterFunction): name = 'series_sort' arg_count = 0 category = 'Get values from metadata' - raw_doc = ( + __doc__ = doc = _( r''' ``series_sort()`` -- returns the series sort value. ''') - __doc__ = doc = _(raw_doc) def evaluate(self, formatter, kwargs, mi, locals): if mi.series: @@ -1725,11 +1680,10 @@ class BuiltinHasCover(BuiltinFormatterFunction): name = 'has_cover' arg_count = 0 category = 'Get values from metadata' - raw_doc = ( + __doc__ = doc = _( r''' ``has_cover()`` -- return ``'Yes'`` if the book has a cover, otherwise the empty string. ''') - __doc__ = doc = _(raw_doc) def evaluate(self, formatter, kwargs, mi, locals): if mi.has_cover: @@ -1741,13 +1695,12 @@ class BuiltinFirstNonEmpty(BuiltinFormatterFunction): name = 'first_non_empty' arg_count = -1 category = 'Iterating over values' - raw_doc = ( + __doc__ = doc = _( r''' ``first_non_empty(value [, value]*)`` -- returns the first ``value`` that is not empty. If all values are empty, then the empty string is returned. You can have as many values as you want. ''') - __doc__ = doc = _(raw_doc) def evaluate(self, formatter, kwargs, mi, locals, *args): i = 0 @@ -1762,7 +1715,7 @@ class BuiltinAnd(BuiltinFormatterFunction): name = 'and' arg_count = -1 category = 'Boolean' - raw_doc = ( + __doc__ = doc = _( r''' ``and(value [, value]*)`` -- returns the string ``'1'`` if all values are not empty, otherwise returns the empty string. You can have as many values as you want. In @@ -1771,7 +1724,6 @@ not to replace ``and()`` with ``&&`` is when short-circuiting can change the res because of side effects. For example, ``and(a='',b=5)`` will always do both assignments, where the ``&&`` operator won't do the second. ''') - __doc__ = doc = _(raw_doc) def evaluate(self, formatter, kwargs, mi, locals, *args): i = 0 @@ -1786,7 +1738,7 @@ class BuiltinOr(BuiltinFormatterFunction): name = 'or' arg_count = -1 category = 'Boolean' - raw_doc = ( + __doc__ = doc = _( r''' ``or(value [, value]*)`` -- returns the string ``'1'`` if any value is not empty, otherwise returns the empty string. You can have as many values as you @@ -1794,7 +1746,6 @@ want. This function can usually be replaced by the ``||`` operator. A reason it cannot be replaced is if short-circuiting will change the results because of side effects. ''') - __doc__ = doc = _(raw_doc) def evaluate(self, formatter, kwargs, mi, locals, *args): i = 0 @@ -1809,13 +1760,12 @@ class BuiltinNot(BuiltinFormatterFunction): name = 'not' arg_count = 1 category = 'Boolean' - raw_doc = ( + __doc__ = doc = _( r''' ``not(value)`` -- returns the string ``'1'`` if the value is empty, otherwise returns the empty string. This function can usually be replaced with the unary not (``!``) operator. ''') - __doc__ = doc = _(raw_doc) def evaluate(self, formatter, kwargs, mi, locals, val): return '' if val else '1' @@ -1825,7 +1775,7 @@ class BuiltinListJoin(BuiltinFormatterFunction): name = 'list_join' arg_count = -1 category = 'List manipulation' - raw_doc = ( + __doc__ = doc = _( r''' ``list_join(with_separator, list1, separator1 [, list2, separator2]*)`` -- return a list made by joining the items in the source lists (``list1`` etc) @@ -1857,7 +1807,6 @@ program: list_join('#@#', $authors, '&', list_re($#genre, ',', '^(.).*$', 'Genre: \1'), ',') [/CODE] ''') - __doc__ = doc = _(raw_doc) def evaluate(self, formatter, kwargs, mi, locals, with_separator, *args): if len(args) % 2 != 0: @@ -1879,7 +1828,7 @@ class BuiltinListUnion(BuiltinFormatterFunction): name = 'list_union' arg_count = 3 category = 'List manipulation' - raw_doc = ( + __doc__ = doc = _( r''' ``list_union(list1, list2, separator)`` -- return a list made by merging the items in ``list1`` and ``list2``, removing duplicate items using a case-insensitive @@ -1901,7 +1850,7 @@ class BuiltinRange(BuiltinFormatterFunction): name = 'range' arg_count = -1 category = 'List manipulation' - raw_doc = ( + __doc__ = doc = _( r''' ``range(start, stop, step, limit)`` -- returns a list of numbers generated by looping over the range specified by the parameters start, stop, and step, with a @@ -1926,7 +1875,6 @@ range(1, 5, 2, 5) -> '1, 3' range(1, 5, 2, 1) -> error(limit exceeded) [/CODE] ''') - __doc__ = doc = _(raw_doc) def evaluate(self, formatter, kwargs, mi, locals, *args): limit_val = 1000 @@ -1955,14 +1903,13 @@ class BuiltinListRemoveDuplicates(BuiltinFormatterFunction): name = 'list_remove_duplicates' arg_count = 2 category = 'List manipulation' - raw_doc = ( + __doc__ = doc = _( r''' ``list_remove_duplicates(list, separator)`` -- return a list made by removing duplicate items in ``list``. If items differ only in case then the last is returned. The items in ``list`` are separated by ``separator``, as are the items in the returned list. ''') - __doc__ = doc = _(raw_doc) def evaluate(self, formatter, kwargs, mi, locals, list_, separator): res = {icu_lower(l.strip()): l.strip() for l in list_.split(separator) if l.strip()} @@ -1975,14 +1922,13 @@ class BuiltinListDifference(BuiltinFormatterFunction): name = 'list_difference' arg_count = 3 category = 'List manipulation' - raw_doc = ( + __doc__ = doc = _( r''' ``list_difference(list1, list2, separator)`` -- return a list made by removing from ``list1`` any item found in ``list2`` using a case-insensitive comparison. The items in ``list1`` and ``list2`` are separated by separator, as are the items in the returned list. ''') - __doc__ = doc = _(raw_doc) def evaluate(self, formatter, kwargs, mi, locals, list1, list2, separator): l1 = [l.strip() for l in list1.split(separator) if l.strip()] @@ -2001,14 +1947,13 @@ class BuiltinListIntersection(BuiltinFormatterFunction): name = 'list_intersection' arg_count = 3 category = 'List manipulation' - raw_doc = ( + __doc__ = doc = _( r''' ``list_intersection(list1, list2, separator)`` -- return a list made by removing from ``list1`` any item not found in ``list2``, using a case-insensitive comparison. The items in ``list1`` and ``list2`` are separated by separator, as are the items in the returned list. ''') - __doc__ = doc = _(raw_doc) def evaluate(self, formatter, kwargs, mi, locals, list1, list2, separator): l1 = [l.strip() for l in list1.split(separator) if l.strip()] @@ -2027,14 +1972,13 @@ class BuiltinListSort(BuiltinFormatterFunction): name = 'list_sort' arg_count = 3 category = 'List manipulation' - raw_doc = ( + __doc__ = doc = _( r''' ``list_sort(list, direction, separator)`` -- return ``list`` sorted using a case-insensitive lexical sort. If ``direction`` is zero (number or character), ``list`` is sorted ascending, otherwise descending. The list items are separated by ``separator``, as are the items in the returned list. ''') - __doc__ = doc = _(raw_doc) def evaluate(self, formatter, kwargs, mi, locals, list1, direction, separator): res = [l.strip() for l in list1.split(separator) if l.strip()] @@ -2047,7 +1991,7 @@ class BuiltinListEquals(BuiltinFormatterFunction): name = 'list_equals' arg_count = 6 category = 'List manipulation' - raw_doc = ( + __doc__ = doc = _( r''' ``list_equals(list1, sep1, list2, sep2, yes_val, no_val)`` -- return ``yes_val`` if ``list1`` and ``list2`` contain the same items, otherwise return ``no_val``. @@ -2055,7 +1999,6 @@ The items are determined by splitting each list using the appropriate separator character (``sep1`` or ``sep2``). The order of items in the lists is not relevant. The comparison is case-insensitive. ''') - __doc__ = doc = _(raw_doc) def evaluate(self, formatter, kwargs, mi, locals, list1, sep1, list2, sep2, yes_val, no_val): s1 = {icu_lower(l.strip()) for l in list1.split(sep1) if l.strip()} @@ -2069,7 +2012,7 @@ class BuiltinListRe(BuiltinFormatterFunction): name = 'list_re' arg_count = 4 category = 'List manipulation' - raw_doc = ( + __doc__ = doc = _( r''' ``list_re(src_list, separator, include_re, opt_replace)`` -- Construct a list by first separating ``src_list`` into items using the ``separator`` character. For @@ -2077,7 +2020,6 @@ each item in the list, check if it matches ``include_re``. If it does then add it to the list to be returned. If ``opt_replace`` is not the empty string then apply the replacement before adding the item to the returned list. ''') - __doc__ = doc = _(raw_doc) def evaluate(self, formatter, kwargs, mi, locals, src_list, separator, include_re, opt_replace): l = [l.strip() for l in src_list.split(separator) if l.strip()] @@ -2098,13 +2040,12 @@ class BuiltinListReGroup(BuiltinFormatterFunction): name = 'list_re_group' arg_count = -1 category = 'List manipulation' - raw_doc = ( + __doc__ = doc = _( r''' ``list_re_group(src_list, separator, include_re, search_re [,template_for_group]*)`` -- Like list_re except replacements are not optional. It uses ``re_group(item, search_re, template ...)`` when doing the replacements. ''') - __doc__ = doc = _(raw_doc) def evaluate(self, formatter, kwargs, mi, locals, src_list, separator, include_re, search_re, *args): @@ -2141,14 +2082,13 @@ class BuiltinToday(BuiltinFormatterFunction): name = 'today' arg_count = 0 category = 'Date functions' - raw_doc = ( + __doc__ = doc = _( r''' ``today()`` -- return a date+time string for today (now). This value is designed for use in ``format_date`` or ``days_between``, but can be manipulated like any other string. The date is in [URL href="https://en.wikipedia.org/wiki/ISO_8601"]ISO[/URL] date/time format. ''') - __doc__ = doc = _(raw_doc) def evaluate(self, formatter, kwargs, mi, locals): return format_date(now(), 'iso') @@ -2158,14 +2098,13 @@ class BuiltinDaysBetween(BuiltinFormatterFunction): name = 'days_between' arg_count = 2 category = 'Date functions' - raw_doc = ( + __doc__ = doc = _( r''' ``days_between(date1, date2)`` -- return the number of days between ``date1`` and ``date2``. The number is positive if ``date1`` is greater than ``date2``, otherwise negative. If either ``date1`` or ``date2`` are not dates, the function returns the empty string. ''') - __doc__ = doc = _(raw_doc) def evaluate(self, formatter, kwargs, mi, locals, date1, date2): try: @@ -2185,7 +2124,7 @@ class BuiltinDateArithmetic(BuiltinFormatterFunction): name = 'date_arithmetic' arg_count = -1 category = 'Date functions' - raw_doc = ( + __doc__ = doc = _( r''' ``date_arithmetic(date, calc_spec, fmt)`` -- Calculate a new date from ``date`` using ``calc_spec``. Return the new date formatted according to optional @@ -2237,7 +2176,7 @@ class BuiltinLanguageStrings(BuiltinFormatterFunction): name = 'language_strings' arg_count = 2 category = 'Get values from metadata' - raw_doc = ( + __doc__ = doc = _( r''' ``language_strings(localize)`` -- return the language names for the language codes @@ -2247,7 +2186,6 @@ passed in as the value. Example: ``{languages:language_strings()}``. If ``localize`` is zero, return the strings in English. If ``localize`` is not zero, return the strings in the language of the current locale. ``Lang_codes`` is a comma-separated list. ''') - __doc__ = doc = _(raw_doc) def evaluate(self, formatter, kwargs, mi, locals, lang_codes, localize): retval = [] @@ -2265,14 +2203,13 @@ class BuiltinLanguageCodes(BuiltinFormatterFunction): name = 'language_codes' arg_count = 1 category = 'Get values from metadata' - raw_doc = ( + __doc__ = doc = _( r''' ``language_codes(lang_strings)`` -- return the [URL href="https://www.loc.gov/standards/iso639-2/php/code_list.php"]language codes[/URL] for the language names passed in ``lang_strings``. The strings must be in the language of the current locale. ``Lang_strings`` is a comma-separated list. ''') - __doc__ = doc = _(raw_doc) def evaluate(self, formatter, kwargs, mi, locals, lang_strings): retval = [] @@ -2290,11 +2227,10 @@ class BuiltinCurrentLibraryName(BuiltinFormatterFunction): name = 'current_library_name' arg_count = 0 category = 'Get values from metadata' - raw_doc = ( + __doc__ = doc = _( r''' ``current_library_name()`` -- return the last name on the path to the current calibre library. ''') - __doc__ = doc = _(raw_doc) def evaluate(self, formatter, kwargs, mi, locals): from calibre.library import current_library_name @@ -2305,12 +2241,11 @@ class BuiltinCurrentLibraryPath(BuiltinFormatterFunction): name = 'current_library_path' arg_count = 0 category = 'Get values from metadata' - raw_doc = ( + __doc__ = doc = _( r''' ``current_library_path()`` -- return the full path to the current calibre library. ''') - __doc__ = doc = _(raw_doc) def evaluate(self, formatter, kwargs, mi, locals): from calibre.library import current_library_path @@ -2321,7 +2256,7 @@ class BuiltinFinishFormatting(BuiltinFormatterFunction): name = 'finish_formatting' arg_count = 4 category = 'Formatting values' - raw_doc = ( + __doc__ = doc = _( r''' ``finish_formatting(val, format, prefix, suffix)`` -- apply the ``format``, ``prefix``, and ``suffix`` to a value in the same way as done in a template like @@ -2346,7 +2281,6 @@ program: ) [/CODE] ''') - __doc__ = doc = _(raw_doc) def evaluate(self, formatter, kwargs, mi, locals_, val, fmt, prefix, suffix): if not val: @@ -2358,7 +2292,7 @@ class BuiltinVirtualLibraries(BuiltinFormatterFunction): name = 'virtual_libraries' arg_count = 0 category = 'Get values from metadata' - raw_doc = ( + __doc__ = doc = _( r''' ``virtual_libraries()`` -- return a comma-separated list of Virtual libraries that contain this book. This function works only in the GUI. If you want to use these @@ -2366,7 +2300,6 @@ values in save-to-disk or send-to-device templates then you must make a custom "Column built from other columns", use the function in that column's template, and use that column's value in your save/send templates. ''') - __doc__ = doc = _(raw_doc) def evaluate(self, formatter, kwargs, mi, locals_): db = self.get_database(mi) @@ -2381,7 +2314,7 @@ class BuiltinCurrentVirtualLibraryName(BuiltinFormatterFunction): name = 'current_virtual_library_name' arg_count = 0 category = 'Get values from metadata' - raw_doc = ( + __doc__ = doc = _( r''' ``current_virtual_library_name()`` -- return the name of the current virtual library if there is one, otherwise the empty string. Library name case @@ -2391,7 +2324,6 @@ program: current_virtual_library_name() [/CODE] This function works only in the GUI. ''') - __doc__ = doc = _(raw_doc) def evaluate(self, formatter, kwargs, mi, locals): return self.get_database(mi).data.get_base_restriction_name() @@ -2401,7 +2333,7 @@ class BuiltinUserCategories(BuiltinFormatterFunction): name = 'user_categories' arg_count = 0 category = 'Get values from metadata' - raw_doc = ( + __doc__ = doc = _( r''' ``user_categories()`` -- return a comma-separated list of the user categories that contain this book. This function works only in the GUI. If you want to use these @@ -2409,7 +2341,6 @@ values in save-to-disk or send-to-device templates then you must make a custom `Column built from other columns`, use the function in that column's template, and use that column's value in your save/send templates ''') - __doc__ = doc = _(raw_doc) def evaluate(self, formatter, kwargs, mi, locals_): if hasattr(mi, '_proxy_metadata'): @@ -2423,14 +2354,13 @@ class BuiltinTransliterate(BuiltinFormatterFunction): name = 'transliterate' arg_count = 1 category = 'String manipulation' - raw_doc = ( + __doc__ = doc = _( r''' ``transliterate(value)`` -- Return a string in a latin alphabet formed by approximating the sound of the words in the source field. For example, if the source field is ``Фёдор Миха́йлович Достоевский`` this function returns ``Fiodor Mikhailovich Dostoievskii``. ''') - __doc__ = doc = _(raw_doc) def evaluate(self, formatter, kwargs, mi, locals, source): from calibre.utils.filenames import ascii_text @@ -2441,7 +2371,7 @@ class BuiltinGetLink(BuiltinFormatterFunction): name = 'get_link' arg_count = 2 category = 'Template database functions' - raw_doc = ( + __doc__ = doc = _( r''' ``get_link(field_name, field_value)`` -- fetch the link for field ``field_name`` with value ``field_value``. If there is no attached link, return the empty @@ -2466,7 +2396,6 @@ ans [/CODE] [/LIST] ''') - __doc__ = doc = _(raw_doc) def evaluate(self, formatter, kwargs, mi, locals, field_name, field_value): db = self.get_database(mi).new_api @@ -2485,7 +2414,7 @@ class BuiltinAuthorLinks(BuiltinFormatterFunction): name = 'author_links' arg_count = 2 category = 'Get values from metadata' - raw_doc = ( + __doc__ = doc = _( r''' ``author_links(val_separator, pair_separator)`` -- returns a string containing a list of authors and those authors' link values in the form: @@ -2498,7 +2427,6 @@ with no added spaces. Assuming the ``val_separator`` is a colon, choose separators that do not occur in author names or links. An author is included even if the author link is empty. ''') - __doc__ = doc = _(raw_doc) def evaluate(self, formatter, kwargs, mi, locals, val_sep, pair_sep): if hasattr(mi, '_proxy_metadata'): @@ -2517,7 +2445,7 @@ class BuiltinAuthorSorts(BuiltinFormatterFunction): name = 'author_sorts' arg_count = 1 category = 'Get values from metadata' - raw_doc = ( + __doc__ = doc = _( r''' ``author_sorts(val_separator)`` -- returns a string containing a list of author's sort values for the authors of the book. The sort is the one in the @@ -2527,7 +2455,6 @@ etc. with no added spaces. The author sort values in this list are in the same order as the authors of the book. If you want spaces around ``val_separator`` then include them in the ``val_separator`` string. ''') - __doc__ = doc = _(raw_doc) def evaluate(self, formatter, kwargs, mi, locals, val_sep): sort_data = mi.author_sort_map @@ -2541,7 +2468,7 @@ class BuiltinConnectedDeviceName(BuiltinFormatterFunction): name = 'connected_device_name' arg_count = 1 category = 'Get values from metadata' - raw_doc = ( + __doc__ = doc = _( r''' ``connected_device_name(storage_location_key)`` -- if a device is connected then return the device name, otherwise return the empty string. Each storage location @@ -2579,7 +2506,7 @@ class BuiltinConnectedDeviceUUID(BuiltinFormatterFunction): name = 'connected_device_uuid' arg_count = 1 category = 'Get values from metadata' - raw_doc = ( + __doc__ = doc = _( r''' ``connected_device_uuid(storage_location_key)`` -- if a device is connected then return the device uuid (unique id), otherwise return the empty string. Each @@ -2587,7 +2514,6 @@ storage location on a device has a different uuid. The ``storage_location_key`` location names are ``'main'``, ``'carda'`` and ``'cardb'``. This function works only in the GUI. ''') - __doc__ = doc = _(raw_doc) def evaluate(self, formatter, kwargs, mi, locals, storage_location): # We can't use get_database() here because we need the device manager. @@ -2618,7 +2544,7 @@ class BuiltinCheckYesNo(BuiltinFormatterFunction): name = 'check_yes_no' arg_count = 4 category = 'If-then-else' - raw_doc = ( + __doc__ = doc = _( r''' ``check_yes_no(field_name, is_undefined, is_false, is_true)`` -- checks if the value of the yes/no field named by the lookup name ``field_name`` is one of the @@ -2632,7 +2558,6 @@ Example: ``check_yes_no("#bool", 1, 0, 1)`` returns ``'Yes'`` if the yes/no fiel More than one of ``is_undefined``, ``is_false``, or ``is_true`` can be set to 1. ''') - __doc__ = doc = _(raw_doc) def evaluate(self, formatter, kwargs, mi, locals, field, is_undefined, is_false, is_true): # 'field' is a lookup name, not a value @@ -2656,14 +2581,13 @@ class BuiltinRatingToStars(BuiltinFormatterFunction): name = 'rating_to_stars' arg_count = 2 category = 'Formatting values' - raw_doc = ( + __doc__ = doc = _( r''' ``rating_to_stars(value, use_half_stars)`` -- Returns the value as string of star (``★``) characters. The value must be a number between 0 and 5. Set use_half_stars to 1 if you want half star characters for fractional numbers available with custom ratings columns. ''') - __doc__ = doc = _(raw_doc) def evaluate(self, formatter, kwargs, mi, locals, value, use_half_stars): if not value: @@ -2683,7 +2607,7 @@ class BuiltinSwapAroundArticles(BuiltinFormatterFunction): name = 'swap_around_articles' arg_count = 2 category = 'String manipulation' - raw_doc = ( + __doc__ = doc = _( r''' ``swap_around_articles(value, separator)`` -- returns the value with articles moved to the end. The value can be a list, in which case each item in the list is @@ -2691,7 +2615,6 @@ processed. If the value is a list then you must provide the ``separator``. If no ``separator`` is provided then the value is treated as being a single value, not a list. The `articles` are those used by calibre to generate the ``title_sort``. ''') - __doc__ = doc = _(raw_doc) def evaluate(self, formatter, kwargs, mi, locals, val, separator): if not val: @@ -2711,7 +2634,7 @@ class BuiltinArguments(BuiltinFormatterFunction): name = 'arguments' arg_count = -1 category = 'Other' - raw_doc = ( + __doc__ = doc = _( r''' ``arguments(id[=expression] [, id[=expression]]*)`` -- Used in a stored template to retrieve the arguments passed in the call. It both declares and @@ -2722,7 +2645,6 @@ argument is not provided in the call then ``arguments()`` assigns that variable the provided default value. If there is no default value then the variable is set to the empty string. ''') - __doc__ = doc = _(raw_doc) def evaluate(self, formatter, kwargs, mi, locals, *args): # The arguments function is implemented in-line in the formatter @@ -2733,7 +2655,7 @@ class BuiltinGlobals(BuiltinFormatterFunction): name = 'globals' arg_count = -1 category = 'Other' - raw_doc = ( + __doc__ = doc = _( r''' ``globals(id[=expression] [, id[=expression]]*)`` -- Retrieves "global variables" that can be passed into the formatter. The name ``id`` is the name of the global @@ -2743,7 +2665,6 @@ provided in the globals then it assigns that variable the provided default value. If there is no default value then the variable is set to the empty string.) ''') - __doc__ = doc = _(raw_doc) def evaluate(self, formatter, kwargs, mi, locals, *args): # The globals function is implemented in-line in the formatter @@ -2754,14 +2675,13 @@ class BuiltinSetGlobals(BuiltinFormatterFunction): name = 'set_globals' arg_count = -1 category = 'other' - raw_doc = ( + __doc__ = doc = _( r''' ``set_globals(id[=expression] [, id[=expression]]*)`` -- Sets `global variables` that can be passed into the formatter. The globals are given the name of the ``id`` passed in. The value of the ``id`` is used unless an expression is provided. ''') - __doc__ = doc = _(raw_doc) def evaluate(self, formatter, kwargs, mi, locals, *args): # The globals function is implemented in-line in the formatter @@ -2772,12 +2692,11 @@ class BuiltinFieldExists(BuiltinFormatterFunction): name = 'field_exists' arg_count = 1 category = 'If-then-else' - raw_doc = ( + __doc__ = doc = _( r''' ``field_exists(lookup_name)`` -- checks if a field (column) with the lookup name ``lookup_name`` exists, returning ``'1'`` if so and the empty string if not. ''') - __doc__ = doc = _(raw_doc) def evaluate(self, formatter, kwargs, mi, locals, field_name): if field_name.lower() in mi.all_field_keys(): @@ -2789,7 +2708,7 @@ class BuiltinCharacter(BuiltinFormatterFunction): name = 'character' arg_count = 1 category = 'String manipulation' - raw_doc = ( + __doc__ = doc = _( r''' ``character(character_name)`` -- returns the character named by character_name. For example, ``character('newline')`` returns a newline character (``'\n'``). @@ -2797,7 +2716,6 @@ The supported character names are ``newline``, ``return``, ``tab``, and ``backslash``. This function is used to put these characters into the output of templates. ''') - __doc__ = doc = _(raw_doc) def evaluate(self, formatter, kwargs, mi, locals, character_name): # The globals function is implemented in-line in the formatter @@ -2808,12 +2726,11 @@ class BuiltinToHex(BuiltinFormatterFunction): name = 'to_hex' arg_count = 1 category = 'String manipulation' - raw_doc = ( + __doc__ = doc = _( r''' ``to_hex(val)`` -- returns the string ``val`` encoded into hex. This is useful when constructing calibre URLs. ''') - __doc__ = doc = _(raw_doc) def evaluate(self, formatter, kwargs, mi, locals, val): return val.encode().hex() @@ -2823,7 +2740,7 @@ class BuiltinUrlsFromIdentifiers(BuiltinFormatterFunction): name = 'urls_from_identifiers' arg_count = 2 category = 'Formatting values' - raw_doc = ( + __doc__ = doc = _( r''' ``urls_from_identifiers(identifiers, sort_results)`` -- given a comma-separated list of ``identifiers``, where an ``identifier`` is a colon-separated pair of @@ -2833,7 +2750,6 @@ generated from the identifiers. The list not sorted if sort_results is ``0`` name. The URLs are generated in the same way as the built-in identifiers column when shown in Book Details. ''') - __doc__ = doc = _(raw_doc) def evaluate(self, formatter, kwargs, mi, locals, identifiers, sort_results): from calibre.ebooks.metadata.sources.identify import urls_from_identifiers @@ -2861,7 +2777,7 @@ class BuiltinBookCount(BuiltinFormatterFunction): name = 'book_count' arg_count = 2 category = 'Template database functions' - raw_doc = ( + __doc__ = doc = _( r''' ``book_count(query, use_vl)`` -- returns the count of books found by searching for ``query``. If ``use_vl`` is ``0`` (zero) then virtual libraries are ignored. @@ -2902,7 +2818,6 @@ eliminates problems caused by the requirement to escape quotes in search expressions. [/LIST] ''') - __doc__ = doc = _(raw_doc) def evaluate(self, formatter, kwargs, mi, locals, query, use_vl): from calibre.db.fields import rendering_composite_name @@ -2921,7 +2836,7 @@ class BuiltinBookValues(BuiltinFormatterFunction): name = 'book_values' arg_count = 4 category = 'Template database functions' - raw_doc = ( + __doc__ = doc = _( r''' ``book_values(column, query, sep, use_vl)`` -- returns a list of the unique values contained in the column ``column`` (a lookup name), separated by ``sep``, @@ -2933,7 +2848,6 @@ with only one book. It cannot be used in composite columns unless the tweak ``allow_template_database_functions_in_composites`` is set to True. It can be used only in the GUI. ''') - __doc__ = doc = _(raw_doc) def evaluate(self, formatter, kwargs, mi, locals, column, query, sep, use_vl): from calibre.db.fields import rendering_composite_name @@ -2961,7 +2875,7 @@ class BuiltinHasExtraFiles(BuiltinFormatterFunction): name = 'has_extra_files' arg_count = -1 category = 'Template database functions' - raw_doc = ( + __doc__ = doc = _( r''' ``has_extra_files([pattern])`` -- returns the count of extra files, otherwise '' (the empty string). If the optional parameter ``pattern`` (a regular expression) @@ -2970,7 +2884,6 @@ files are counted. The pattern match is case insensitive. See also the functions ``extra_file_names()``, ``extra_file_size()`` and ``extra_file_modtime()``. This function can be used only in the GUI. ''') - __doc__ = doc = _(raw_doc) def evaluate(self, formatter, kwargs, mi, locals, *args): if len(args) > 1: @@ -2993,7 +2906,7 @@ class BuiltinExtraFileNames(BuiltinFormatterFunction): name = 'extra_file_names' arg_count = -1 category = 'Template database functions' - raw_doc = ( + __doc__ = doc = _( r''' ``extra_file_names(sep [, pattern])`` -- returns a ``sep``-separated list of extra files in the book's ``data/`` folder. If the optional parameter @@ -3002,7 +2915,6 @@ files that match ``pattern``. The pattern match is case insensitive. See also the functions ``has_extra_files()``, ``extra_file_modtime()`` and ``extra_file_size()``. This function can be used only in the GUI. ''') - __doc__ = doc = _(raw_doc) def evaluate(self, formatter, kwargs, mi, locals, sep, *args): if len(args) > 1: @@ -3025,14 +2937,13 @@ class BuiltinExtraFileSize(BuiltinFormatterFunction): name = 'extra_file_size' arg_count = 1 category = 'Template database functions' - raw_doc = ( + __doc__ = doc = _( r''' ``extra_file_size(file_name)`` -- returns the size in bytes of the extra file ``file_name`` in the book's ``data/`` folder if it exists, otherwise ``-1``. See also the functions ``has_extra_files()``, ``extra_file_names()`` and ``extra_file_modtime()``. This function can be used only in the GUI. ''') - __doc__ = doc = _(raw_doc) def evaluate(self, formatter, kwargs, mi, locals, file_name): db = self.get_database(mi).new_api @@ -3051,7 +2962,7 @@ class BuiltinExtraFileModtime(BuiltinFormatterFunction): name = 'extra_file_modtime' arg_count = 2 category = 'Template database functions' - raw_doc = ( + __doc__ = doc = _( r''' ``extra_file_modtime(file_name, format_string)`` -- returns the modification time of the extra file ``file_name`` in the book's ``data/`` folder if it @@ -3062,7 +2973,6 @@ since the epoch. See also the functions ``has_extra_files()``, ``extra_file_names()`` and ``extra_file_size()``. The epoch is OS dependent. This function can be used only in the GUI. ''') - __doc__ = doc = _(raw_doc) def evaluate(self, formatter, kwargs, mi, locals, file_name, format_string): db = self.get_database(mi).new_api @@ -3084,7 +2994,7 @@ class BuiltinGetNote(BuiltinFormatterFunction): name = 'get_note' arg_count = 3 category = 'Template database functions' - raw_doc = ( + __doc__ = doc = _( r''' ``get_note(field_name, field_value, plain_text)`` -- fetch the note for field 'field_name' with value 'field_value'. If ``plain_text`` is empty, return the @@ -3104,7 +3014,6 @@ program: [/CODE] [/LIST] ''') - __doc__ = doc = _(raw_doc) def evaluate(self, formatter, kwargs, mi, locals, field_name, field_value, plain_text): db = self.get_database(mi).new_api @@ -3147,7 +3056,7 @@ class BuiltinHasNote(BuiltinFormatterFunction): name = 'has_note' arg_count = 2 category = 'Template database functions' - raw_doc = ( + __doc__ = doc = _( r''' ``has_note(field_name, field_value)``. This function has two variants: [LIST] @@ -3172,7 +3081,6 @@ values in ``field_name``. Example: list_count(has_note('authors', ''), '&') ==# list_count_field('authors') [/CODE] ''') - __doc__ = doc = _(raw_doc) def evaluate(self, formatter, kwargs, mi, locals, field_name, field_value): db = self.get_database(mi).new_api @@ -3199,7 +3107,7 @@ class BuiltinIsDarkMode(BuiltinFormatterFunction): name = 'is_dark_mode' arg_count = 0 category = 'other' - raw_doc = ( + __doc__ = doc = _( r''' ``is_dark_mode()`` -- returns ``'1'`` if calibre is running in dark mode, ``''`` (the empty string) otherwise. This function can be used in advanced color and @@ -3208,7 +3116,6 @@ icon rules to choose different colors/icons according to the mode. Example: if is_dark_mode() then 'dark.png' else 'light.png' fi [/CODE] ''') - __doc__ = doc = _(raw_doc) def evaluate(self, formatter, kwargs, mi, locals): try: @@ -3223,7 +3130,7 @@ class BuiltinFieldListCount(BuiltinFormatterFunction): name = 'list_count_field' arg_count = 0 category = 'List manipulation' - raw_doc = ( + __doc__ = doc = _( r''' ``list_count_field(lookup_name)``-- returns the count of items in the field with the lookup name ``lookup_name``. The field must be multi-valued such as @@ -3232,7 +3139,6 @@ is much faster than ``list_count()`` because it operates directly on calibre data without converting it to a string first. Example: ``list_count_field('tags')`` ''') - __doc__ = doc = _(raw_doc) def evaluate(self, formatter, kwargs, mi, locals, *args): # The globals function is implemented in-line in the formatter