mirror of
https://github.com/kovidgoyal/calibre.git
synced 2025-12-11 07:35:14 -05:00
Add the function "f_string()", similar to python's 'f strings', to the template language.
This commit is contained in:
parent
6e20528004
commit
4887d6990b
@ -61,6 +61,7 @@ class Node:
|
|||||||
NODE_SWITCH_IF = 32
|
NODE_SWITCH_IF = 32
|
||||||
NODE_LIST_COUNT_FIELD = 33
|
NODE_LIST_COUNT_FIELD = 33
|
||||||
NODE_WITH = 34
|
NODE_WITH = 34
|
||||||
|
NODE_FSTRING = 35
|
||||||
|
|
||||||
def __init__(self, line_number, name):
|
def __init__(self, line_number, name):
|
||||||
self.my_line_number = line_number
|
self.my_line_number = line_number
|
||||||
@ -77,7 +78,7 @@ class Node:
|
|||||||
|
|
||||||
class WithNode(Node):
|
class WithNode(Node):
|
||||||
def __init__(self, line_number, book_id, block):
|
def __init__(self, line_number, book_id, block):
|
||||||
Node.__init__(self, line_number, 'if ...')
|
Node.__init__(self, line_number, 'with ...')
|
||||||
self.node_type = self.NODE_WITH
|
self.node_type = self.NODE_WITH
|
||||||
self.book_id = book_id
|
self.book_id = book_id
|
||||||
self.block = block
|
self.block = block
|
||||||
@ -348,6 +349,13 @@ class ListCountFieldNode(Node):
|
|||||||
self.expression = expression
|
self.expression = expression
|
||||||
|
|
||||||
|
|
||||||
|
class FStringNode(Node):
|
||||||
|
def __init__(self, line_number, string):
|
||||||
|
Node.__init__(self, line_number, 'f_string')
|
||||||
|
self.node_type = self.NODE_FSTRING
|
||||||
|
self.string = string
|
||||||
|
|
||||||
|
|
||||||
class _Parser:
|
class _Parser:
|
||||||
LEX_OP = 1
|
LEX_OP = 1
|
||||||
LEX_ID = 2
|
LEX_ID = 2
|
||||||
@ -743,7 +751,9 @@ class _Parser:
|
|||||||
'strcat': (lambda _: True,
|
'strcat': (lambda _: True,
|
||||||
lambda ln, args: StrcatNode(ln, args)),
|
lambda ln, args: StrcatNode(ln, args)),
|
||||||
'list_count_field': (lambda args: len(args) == 1,
|
'list_count_field': (lambda args: len(args) == 1,
|
||||||
lambda ln, args: ListCountFieldNode(ln, args[0]))
|
lambda ln, args: ListCountFieldNode(ln, args[0])),
|
||||||
|
'f_string': (lambda args: len(args) == 1,
|
||||||
|
lambda ln, args: FStringNode(ln, args[0])),
|
||||||
}
|
}
|
||||||
|
|
||||||
def expr(self):
|
def expr(self):
|
||||||
@ -1392,6 +1402,14 @@ class _Interpreter:
|
|||||||
self.break_reporter(prog.node_name, res, prog.line_number)
|
self.break_reporter(prog.node_name, res, prog.line_number)
|
||||||
return res
|
return res
|
||||||
|
|
||||||
|
def do_node_f_string(self, prog):
|
||||||
|
def repl(mo):
|
||||||
|
print(mo.group()[1:-1])
|
||||||
|
p = self.parent.gpm_parser.program(self.parent, self.funcs,
|
||||||
|
self.parent.lex_scanner.scan(mo.group()[1:-1]))
|
||||||
|
return self.expr(p)
|
||||||
|
return str(re.sub(r'\{.*?\}', repl, self.expr(prog.string)))
|
||||||
|
|
||||||
def do_node_list_count_field(self, prog):
|
def do_node_list_count_field(self, prog):
|
||||||
name = field_metadata.search_term_to_field_key(self.expr(prog.expression))
|
name = field_metadata.search_term_to_field_key(self.expr(prog.expression))
|
||||||
res = getattr(self.parent_book, name, None)
|
res = getattr(self.parent_book, name, None)
|
||||||
@ -1654,6 +1672,7 @@ class _Interpreter:
|
|||||||
Node.NODE_LOCAL_FUNCTION_CALL: do_node_local_function_call,
|
Node.NODE_LOCAL_FUNCTION_CALL: do_node_local_function_call,
|
||||||
Node.NODE_LIST_COUNT_FIELD: do_node_list_count_field,
|
Node.NODE_LIST_COUNT_FIELD: do_node_list_count_field,
|
||||||
Node.NODE_WITH: do_node_with,
|
Node.NODE_WITH: do_node_with,
|
||||||
|
Node.NODE_FSTRING: do_node_f_string,
|
||||||
}
|
}
|
||||||
|
|
||||||
def expr(self, prog):
|
def expr(self, prog):
|
||||||
|
|||||||
@ -3761,6 +3761,49 @@ This function can be used only in the GUI.
|
|||||||
return '1' if d.exec() == QDialog.DialogCode.Accepted else ''
|
return '1' if d.exec() == QDialog.DialogCode.Accepted else ''
|
||||||
|
|
||||||
|
|
||||||
|
class BuiltinFString(BuiltinFormatterFunction):
|
||||||
|
name = 'f_string'
|
||||||
|
arg_count = 1
|
||||||
|
category = FORMATTING_VALUES
|
||||||
|
def __doc__getter__(self): return translate_ffml(
|
||||||
|
r'''
|
||||||
|
``f_string(string)`` -- interpret ``string`` similar to how python interprets ``f`` strings.
|
||||||
|
The indended use is to simplify long sequences of ``str & str`` or strcat(a,b,c) expressions.
|
||||||
|
|
||||||
|
Text between braces (``{`` and ``}``) must be General Program Mode template
|
||||||
|
expressions. The expressions, which can be expression lists, are evaluated in
|
||||||
|
the current context (current book and local variables). Text not between
|
||||||
|
braces is passed through unchanged.
|
||||||
|
|
||||||
|
Examples:
|
||||||
|
[LIST]
|
||||||
|
[*]``f_string('Here is the title: {$title}')`` - returns the string with ``{$title}``
|
||||||
|
replaced with the title of the current book. For example, if the book's title is
|
||||||
|
`20,000 Leagues Under the Sea` then the ``f_string()`` returns
|
||||||
|
`Here is the title: 20,000 Leagues Under the Sea`.
|
||||||
|
[*]Assuming the current date is 18 Sept 2025, this ``f_string()``
|
||||||
|
[CODE]
|
||||||
|
f_string("Today's date: the {d = today(); format_date(d, 'd')} of {format_date(d, 'MMMM')}, {format_date(d, 'yyyy')}")
|
||||||
|
[/CODE]
|
||||||
|
returns the string `Today's date: the 18 of September, 2025`.
|
||||||
|
Note the expression list (an assignment then an ``if`` statement) used in the first ``{ ... }`` group to assign today's date to a local variable.
|
||||||
|
[*]If the book is book #3 in a series named `Foo` that has 5 books then this template
|
||||||
|
[CODE]
|
||||||
|
program:
|
||||||
|
if $series then
|
||||||
|
series_count = book_count('series:"""=' & $series & '"""', 0);
|
||||||
|
return f_string("{$series}, book {$series_index} of {series_count}")
|
||||||
|
fi;
|
||||||
|
return 'This book is not in a series'
|
||||||
|
[/CODE]
|
||||||
|
returns `Foo, book 3 of 5`
|
||||||
|
[/LIST]
|
||||||
|
''')
|
||||||
|
|
||||||
|
def evaluate(self, formatter, kwargs, mi, locals, fstring):
|
||||||
|
raise ValueError(_('This function cannot be called directly. It is built into the formatter'))
|
||||||
|
|
||||||
|
|
||||||
_formatter_builtins = [
|
_formatter_builtins = [
|
||||||
BuiltinAdd(), BuiltinAnd(), BuiltinApproximateFormats(), BuiltinArguments(),
|
BuiltinAdd(), BuiltinAnd(), BuiltinApproximateFormats(), BuiltinArguments(),
|
||||||
BuiltinAssign(),
|
BuiltinAssign(),
|
||||||
@ -3776,7 +3819,7 @@ _formatter_builtins = [
|
|||||||
BuiltinFinishFormatting(), BuiltinFirstMatchingCmp(), BuiltinFloor(),
|
BuiltinFinishFormatting(), BuiltinFirstMatchingCmp(), BuiltinFloor(),
|
||||||
BuiltinFormatDate(), BuiltinFormatDateField(), BuiltinFormatDuration(), BuiltinFormatNumber(),
|
BuiltinFormatDate(), BuiltinFormatDateField(), BuiltinFormatDuration(), BuiltinFormatNumber(),
|
||||||
BuiltinFormatsModtimes(),BuiltinFormatsPaths(), BuiltinFormatsPathSegments(),
|
BuiltinFormatsModtimes(),BuiltinFormatsPaths(), BuiltinFormatsPathSegments(),
|
||||||
BuiltinFormatsSizes(), BuiltinFractionalPart(),BuiltinGetLink(),
|
BuiltinFormatsSizes(), BuiltinFractionalPart(),BuiltinFString(), BuiltinGetLink(),
|
||||||
BuiltinGetNote(), BuiltinGlobals(), BuiltinHasCover(), BuiltinHasExtraFiles(),
|
BuiltinGetNote(), BuiltinGlobals(), BuiltinHasCover(), BuiltinHasExtraFiles(),
|
||||||
BuiltinHasNote(), BuiltinHumanReadable(), BuiltinIdentifierInList(),
|
BuiltinHasNote(), BuiltinHumanReadable(), BuiltinIdentifierInList(),
|
||||||
BuiltinIfempty(), BuiltinIsDarkMode(), BuiltinLanguageCodes(), BuiltinLanguageStrings(),
|
BuiltinIfempty(), BuiltinIsDarkMode(), BuiltinLanguageCodes(), BuiltinLanguageStrings(),
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user