From bef7077158fd2fb7ec88da5eb79a4671871bcfa2 Mon Sep 17 00:00:00 2001 From: Charles Haley <> Date: Wed, 19 Oct 2011 07:50:52 +0200 Subject: [PATCH 1/3] change formatter_functions from an attribute to a function --- setup/resources.py | 2 +- src/calibre/gui2/dialogs/template_dialog.py | 6 +++--- src/calibre/gui2/preferences/template_functions.py | 8 ++++---- src/calibre/manual/template_ref_generate.py | 2 +- src/calibre/utils/formatter.py | 4 ++-- src/calibre/utils/formatter_functions.py | 11 +++++++---- 6 files changed, 18 insertions(+), 15 deletions(-) diff --git a/setup/resources.py b/setup/resources.py index ee72a98cb6..bfb0572229 100644 --- a/setup/resources.py +++ b/setup/resources.py @@ -206,7 +206,7 @@ class Resources(Command): function_dict = {} import inspect from calibre.utils.formatter_functions import formatter_functions - for obj in formatter_functions.get_builtins().values(): + for obj in formatter_functions().get_builtins().values(): eval_func = inspect.getmembers(obj, lambda x: inspect.ismethod(x) and x.__name__ == 'evaluate') try: diff --git a/src/calibre/gui2/dialogs/template_dialog.py b/src/calibre/gui2/dialogs/template_dialog.py index f02678363b..a395ecf6b9 100644 --- a/src/calibre/gui2/dialogs/template_dialog.py +++ b/src/calibre/gui2/dialogs/template_dialog.py @@ -45,7 +45,7 @@ class TemplateHighlighter(QSyntaxHighlighter): "keyword")) TemplateHighlighter.Rules.append((QRegExp( "|".join([r"\b%s\b" % builtin for builtin in - formatter_functions.get_builtins()])), + formatter_functions().get_builtins()])), "builtin")) TemplateHighlighter.Rules.append((QRegExp( @@ -248,8 +248,8 @@ class TemplateDialog(QDialog, Ui_TemplateDialog): except: self.builtin_source_dict = {} - self.funcs = formatter_functions.get_functions() - self.builtins = formatter_functions.get_builtins() + self.funcs = formatter_functions().get_functions() + self.builtins = formatter_functions().get_builtins() func_names = sorted(self.funcs) self.function.clear() diff --git a/src/calibre/gui2/preferences/template_functions.py b/src/calibre/gui2/preferences/template_functions.py index b23cc43bd6..f5203e9cdd 100644 --- a/src/calibre/gui2/preferences/template_functions.py +++ b/src/calibre/gui2/preferences/template_functions.py @@ -82,8 +82,8 @@ class ConfigWidget(ConfigWidgetBase, Ui_Form): traceback.print_exc() self.builtin_source_dict = {} - self.funcs = formatter_functions.get_functions() - self.builtins = formatter_functions.get_builtins_and_aliases() + self.funcs = formatter_functions().get_functions() + self.builtins = formatter_functions().get_builtins_and_aliases() self.build_function_names_box() self.function_name.currentIndexChanged[str].connect(self.function_index_changed) @@ -217,13 +217,13 @@ class ConfigWidget(ConfigWidgetBase, Ui_Form): pass def commit(self): - formatter_functions.reset_to_builtins() + formatter_functions().reset_to_builtins() pref_value = [] for f in self.funcs: if f in self.builtins: continue func = self.funcs[f] - formatter_functions.register_function(func) + formatter_functions().register_function(func) pref_value.append((func.name, func.doc, func.arg_count, func.program_text)) self.db.prefs.set('user_template_functions', pref_value) diff --git a/src/calibre/manual/template_ref_generate.py b/src/calibre/manual/template_ref_generate.py index 05cb57c7b4..24f9bba9dc 100644 --- a/src/calibre/manual/template_ref_generate.py +++ b/src/calibre/manual/template_ref_generate.py @@ -65,7 +65,7 @@ def generate_template_language_help(): funcs = defaultdict(dict) - for func in formatter_functions.get_builtins().values(): + for func in formatter_functions().get_builtins().values(): class_name = func.__class__.__name__ func_sig = getattr(func, 'doc') x = func_sig.find(' -- ') diff --git a/src/calibre/utils/formatter.py b/src/calibre/utils/formatter.py index bc25f25043..2d1a536d10 100644 --- a/src/calibre/utils/formatter.py +++ b/src/calibre/utils/formatter.py @@ -88,7 +88,7 @@ class _Parser(object): def expr(self): if self.token_is_id(): - funcs = formatter_functions.get_functions() + funcs = formatter_functions().get_functions() # We have an identifier. Determine if it is a function id = self.token() if not self.token_op_is_a('('): @@ -276,7 +276,7 @@ class TemplateFormatter(string.Formatter): dispfmt = fmt[0:colon] colon += 1 - funcs = formatter_functions.get_functions() + funcs = formatter_functions().get_functions() fname = fmt[colon:p] if fname in funcs: func = funcs[fname] diff --git a/src/calibre/utils/formatter_functions.py b/src/calibre/utils/formatter_functions.py index 38fcbe8a1e..090c7c0298 100644 --- a/src/calibre/utils/formatter_functions.py +++ b/src/calibre/utils/formatter_functions.py @@ -64,8 +64,11 @@ class FormatterFunctions(object): for a in c.aliases: self._functions[a] = c -formatter_functions = FormatterFunctions() +_ff = FormatterFunctions() +def formatter_functions(): + global _ff + return _ff class FormatterFunction(object): @@ -89,7 +92,7 @@ class FormatterFunction(object): class BuiltinFormatterFunction(FormatterFunction): def __init__(self): - formatter_functions.register_builtin(self) + formatter_functions().register_builtin(self) eval_func = inspect.getmembers(self.__class__, lambda x: inspect.ismethod(x) and x.__name__ == 'evaluate') try: @@ -1133,10 +1136,10 @@ class UserFunction(FormatterUserFunction): return cls def load_user_template_functions(funcs): - formatter_functions.reset_to_builtins() + formatter_functions().reset_to_builtins() for func in funcs: try: cls = compile_user_function(*func) - formatter_functions.register_function(cls) + formatter_functions().register_function(cls) except: traceback.print_exc() From 4c1d505b68639fca4bf70935b814bb0498eacf94 Mon Sep 17 00:00:00 2001 From: Charles Haley <> Date: Wed, 19 Oct 2011 07:58:47 +0200 Subject: [PATCH 2/3] Do not load the user template functions for second DBs --- src/calibre/library/database2.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/calibre/library/database2.py b/src/calibre/library/database2.py index 0c27cc6b0e..9c08e78011 100644 --- a/src/calibre/library/database2.py +++ b/src/calibre/library/database2.py @@ -302,7 +302,8 @@ class LibraryDatabase2(LibraryDatabase, SchemaUpgrade, CustomColumns): if cats_changed: self.prefs.set('user_categories', user_cats) - load_user_template_functions(self.prefs.get('user_template_functions', [])) + if not self.is_second_db: + load_user_template_functions(self.prefs.get('user_template_functions', [])) self.conn.executescript(''' DROP TRIGGER IF EXISTS author_insert_trg; From e6645d513a2a723c346fc8fcb4688550be1f9f1e Mon Sep 17 00:00:00 2001 From: Charles Haley <> Date: Wed, 19 Oct 2011 08:08:03 +0200 Subject: [PATCH 3/3] Back out the save-to-disk composite column optimization, as it breaks creating folders in complex templates. The optimization was made unnecessary by passing user-defined formatter functions to worker processes. --- src/calibre/library/save_to_disk.py | 24 ++++-------------------- 1 file changed, 4 insertions(+), 20 deletions(-) diff --git a/src/calibre/library/save_to_disk.py b/src/calibre/library/save_to_disk.py index 2dcdd252bc..69565e07d7 100644 --- a/src/calibre/library/save_to_disk.py +++ b/src/calibre/library/save_to_disk.py @@ -150,21 +150,12 @@ class Formatter(TemplateFormatter): traceback.print_exc() b = None if b is not None and b['datatype'] == 'composite': - val = b.get('#value#', None) - if val is not None: - return val.replace('/', '_').replace('\\', '_') if key in self.composite_values: - self.composite_values[key] = val return self.composite_values[key] - try: - # We really should not get here, but it is safer to try - self.composite_values[key] = 'RECURSIVE_COMPOSITE FIELD (S2D) ' + key - self.composite_values[key] = \ - self.vformat(b['display']['composite_template'], - [], kwargs).replace('/', '_').replace('\\', '_') - return self.composite_values[key] - except Exception, e: - return unicode(e) + self.composite_values[key] = 'RECURSIVE_COMPOSITE FIELD (S2D) ' + key + self.composite_values[key] = \ + self.vformat(b['display']['composite_template'], [], kwargs) + return self.composite_values[key] if key in kwargs: val = kwargs[key] if isinstance(val, list): @@ -179,13 +170,6 @@ def get_components(template, mi, id, timefmt='%b %Y', length=250, sanitize_func=ascii_filename, replace_whitespace=False, to_lowercase=False, safe_format=True): - # Note: the mi argument is assumed to be an instance of Metadata returned - # by db.get_metadata(). Reason: the composite columns should have already - # been evaluated, which get_metadata does. If the mi is something else and - # if the template uses composite columns, then a best-efforts attempt is - # made to evaluate them. This will fail if the template uses a user-defined - # template function. - tsorder = tweaks['save_template_title_series_sorting'] format_args = FORMAT_ARGS.copy() format_args.update(mi.all_non_none_fields())