mirror of
https://github.com/kovidgoyal/calibre.git
synced 2025-07-09 03:04:10 -04:00
Merge branch 'master' of https://github.com/cbhaley/calibre
This commit is contained in:
commit
7c7aef46f6
@ -654,25 +654,31 @@ A PTM template begins with:
|
||||
.. code-block:: python
|
||||
|
||||
python:
|
||||
def evaluate(book, db, globals, arguments, **kwargs):
|
||||
def evaluate(book, context):
|
||||
# book is a calibre metadata object
|
||||
# db is a calibre legacy database object
|
||||
# globals is the template global variable dictionary
|
||||
# arguments is a list of arguments if the template is called by a GPM template, otherwise None
|
||||
# kwargs is a dictionary provided for future use
|
||||
# context is an instance of calibre.utils.formatter.PythonTemplateContext,
|
||||
# which (currently) contains the following attributes:
|
||||
# db: a calibre legacy database object
|
||||
# globals: the template global variable dictionary
|
||||
# arguments: is a list of arguments if the template is called by a GPM template, otherwise None
|
||||
|
||||
# Python code goes here
|
||||
# your Python code goes here
|
||||
return 'a string'
|
||||
|
||||
You can add the above text to your template using the context menu, usually accessed with a right click. The comments are not significant and can be removed. You must use python indenting.
|
||||
|
||||
Here is an example of a PTM template that produces a list of all the authors for a series. The list is stored in a `Column built from other columns, behaves like tags`. It shows in :guilabel:`Book details` and has the :guilabel:`on separate lines` checked (in :guilabel:`Preferences->Look & feel->Book details`). That option requires the list to be comma-separated. To satisfy that requirement the template converts commas in author names to semicolons then builds a comma-separated list of authors. The authors are then sorted, which is why the template uses author_sort.::
|
||||
The context object supports ``str(context)`` that returns a string of the context's contents, and ``context.attributes`` that returns a list of the attribute names in the context.
|
||||
|
||||
Here is an example of a PTM template that produces a list of all the authors for a series. The list is stored in a `Column built from other columns, behaves like tags`. It shows in :guilabel:`Book details` and has the :guilabel:`on separate lines` checked (in :guilabel:`Preferences->Look & feel->Book details`). That option requires the list to be comma-separated. To satisfy that requirement the template converts commas in author names to semicolons then builds a comma-separated list of authors. The authors are then sorted, which is why the template uses author_sort.
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
python:
|
||||
def evaluate(book, db, globals, arguments, **kwargs):
|
||||
def evaluate(book, context):
|
||||
if book.series is None:
|
||||
return ''
|
||||
ans = set()
|
||||
db = context.db
|
||||
for id_ in db.search_getting_ids(f'series:"={book.series}"', ''):
|
||||
ans.update(v.strip() for v in db.new_api.field_for('author_sort', id_).split('&'))
|
||||
return ', '.join(v.replace(',', ';') for v in sorted(ans))
|
||||
|
@ -822,8 +822,8 @@ class ReadingTest(BaseTest):
|
||||
|
||||
# test counting books matching a search
|
||||
template = '''python:
|
||||
def evaluate(book, db, **kwargs):
|
||||
ids = db.new_api.search("series:true")
|
||||
def evaluate(book, ctx):
|
||||
ids = ctx.db.new_api.search("series:true")
|
||||
return str(len(ids))
|
||||
'''
|
||||
v = formatter.safe_format(template, {}, 'TEMPLATE ERROR', mi)
|
||||
@ -831,8 +831,8 @@ def evaluate(book, db, **kwargs):
|
||||
|
||||
# test counting books when none match the search
|
||||
template = '''python:
|
||||
def evaluate(book, db, **kwargs):
|
||||
ids = db.new_api.search("series:afafaf")
|
||||
def evaluate(book, ctx):
|
||||
ids = ctx.db.new_api.search("series:afafaf")
|
||||
return str(len(ids))
|
||||
'''
|
||||
v = formatter.safe_format(template, {}, 'TEMPLATE ERROR', mi)
|
||||
@ -840,8 +840,8 @@ def evaluate(book, db, **kwargs):
|
||||
|
||||
# test is_multiple values
|
||||
template = '''python:
|
||||
def evaluate(book, db, **kwargs):
|
||||
tags = db.new_api.all_field_names('tags')
|
||||
def evaluate(book, ctx):
|
||||
tags = ctx.db.new_api.all_field_names('tags')
|
||||
return ','.join(list(tags))
|
||||
'''
|
||||
v = formatter.safe_format(template, {}, 'TEMPLATE ERROR', mi)
|
||||
@ -855,9 +855,9 @@ def evaluate(book, db, **kwargs):
|
||||
"",
|
||||
0,
|
||||
'''python:
|
||||
def evaluate(book, db, globals, arguments):
|
||||
tags = set(db.new_api.all_field_names('tags'))
|
||||
tags.add(arguments[0])
|
||||
def evaluate(book, ctx):
|
||||
tags = set(ctx.db.new_api.all_field_names('tags'))
|
||||
tags.add(ctx.arguments[0])
|
||||
return ','.join(list(tags))
|
||||
'''
|
||||
]], None)
|
||||
|
@ -595,17 +595,18 @@ class TemplateDialog(QDialog, Ui_TemplateDialog):
|
||||
m.exec(self.textbox.mapToGlobal(point))
|
||||
|
||||
def add_python_template_header_text(self):
|
||||
self.textbox.setPlainText('python:\n'
|
||||
'def evaluate(book, db, globals, arguments, **kwargs):\n'
|
||||
'\t# book is a calibre metadata object\n'
|
||||
'\t# db is a calibre legacy database object\n'
|
||||
'\t# globals is the template global variable dictionary\n'
|
||||
'\t# arguments is a list of arguments if the template is '
|
||||
'called by a GPM template, otherwise None\n'
|
||||
'\t# kwargs is a dictionary provided for future use'
|
||||
'\n\n\t# Python code goes here\n'
|
||||
"\treturn 'a string'" +
|
||||
self.textbox.toPlainText())
|
||||
self.textbox.setPlainText('''python:
|
||||
def evaluate(book, context):
|
||||
# book is a calibre metadata object
|
||||
# context is an instance of calibre.utils.formatter.PythonTemplateContext,
|
||||
# which currently contains the following attributes:
|
||||
# db: a calibre legacy database object
|
||||
# globals: the template global variable dictionary
|
||||
# arguments: is a list of arguments if the template is called by a GPM template, otherwise None
|
||||
|
||||
# your Python code goes here
|
||||
return 'a string'
|
||||
''')
|
||||
|
||||
def set_word_wrap(self, to_what):
|
||||
gprefs['gpm_template_editor_word_wrap_mode'] = to_what
|
||||
|
@ -3,12 +3,12 @@ Created on 23 Sep 2010
|
||||
|
||||
@author: charles
|
||||
'''
|
||||
|
||||
__license__ = 'GPL v3'
|
||||
__copyright__ = '2010, Kovid Goyal <kovid@kovidgoyal.net>'
|
||||
__docformat__ = 'restructuredtext en'
|
||||
|
||||
import re, string, traceback, numbers
|
||||
from collections import OrderedDict
|
||||
from functools import partial
|
||||
from math import modf
|
||||
from sys import exc_info
|
||||
@ -830,6 +830,40 @@ class StopException(Exception):
|
||||
super().__init__('Template evaluation stopped')
|
||||
|
||||
|
||||
class PythonTemplateContext(object):
|
||||
|
||||
def __init__(self, **kwargs):
|
||||
# Set attributes we already know must exist.
|
||||
self.db = None
|
||||
self.arguments = None
|
||||
self.globals = None
|
||||
|
||||
attrs_set = {'db', 'arguments', 'globals'}
|
||||
|
||||
# Create/set attributes from the named parameters. Doing it this way we
|
||||
# aren't required to change the signature of __init__ if/when we add
|
||||
# attributes in the future. However, if a user depends upon the
|
||||
# existence of some attribute and the context creator doesn't supply it
|
||||
# then the user will get an AttributeError exception.
|
||||
for k,v in kwargs.items():
|
||||
attrs_set.add(k)
|
||||
setattr(self, k, v)
|
||||
self.attrs_set = attrs_set
|
||||
|
||||
@property
|
||||
def attributes(self):
|
||||
# return a list of attributes in the context object
|
||||
return sorted(list(self.attrs_set))
|
||||
|
||||
def __str__(self):
|
||||
# return a string of the attribute with values separated by newlines
|
||||
attrs = sorted(list(self.attrs_set))
|
||||
ans = OrderedDict()
|
||||
for k in attrs:
|
||||
ans[k] = getattr(self, k, None)
|
||||
return '\n'.join(f'{k}:{v}' for k,v in ans.items())
|
||||
|
||||
|
||||
class _Interpreter:
|
||||
def error(self, message, line_number):
|
||||
m = _('Interpreter: {0} - line number {1}').format(message, line_number)
|
||||
@ -1564,10 +1598,11 @@ class TemplateFormatter(string.Formatter):
|
||||
|
||||
def _run_python_template(self, compiled_template, arguments):
|
||||
try:
|
||||
return compiled_template(book=self.book,
|
||||
return compiled_template(self.book,
|
||||
PythonTemplateContext(
|
||||
db=get_database(self.book, get_database(self.book, None)),
|
||||
globals=self.global_vars,
|
||||
arguments=arguments)
|
||||
arguments=arguments))
|
||||
except Exception as e:
|
||||
ss = traceback.extract_tb(exc_info()[2])[-1]
|
||||
raise ValueError(_('Error in function {0} on line {1} : {2} - {3}').format(
|
||||
|
Loading…
x
Reference in New Issue
Block a user