This commit is contained in:
Kovid Goyal 2013-07-21 12:45:29 +05:30
commit 3cec1c5f30
2 changed files with 39 additions and 35 deletions

View File

@ -220,11 +220,10 @@ class ConfigWidget(ConfigWidgetBase, Ui_Form):
def commit(self): def commit(self):
# formatter_functions().reset_to_builtins() # formatter_functions().reset_to_builtins()
pref_value = [] pref_value = []
for f in self.funcs: for name, cls in self.funcs.iteritems():
func = self.funcs[f] if name not in self.builtins:
pref_value.append((func.name, func.doc, func.arg_count, func.program_text)) pref_value.append((cls.name, cls.doc, cls.arg_count, cls.program_text))
self.db.prefs.set('user_template_functions', pref_value) self.db.prefs.set('user_template_functions', pref_value)
formatter_functions().unregister_functions(self.db.library_id)
load_user_template_functions(self.db.library_id, pref_value) load_user_template_functions(self.db.library_id, pref_value)
return False return False

View File

@ -10,7 +10,6 @@ __docformat__ = 'restructuredtext en'
import inspect, re, traceback import inspect, re, traceback
from math import trunc from math import trunc
from collections import defaultdict
from calibre import human_readable from calibre import human_readable
from calibre.constants import DEBUG from calibre.constants import DEBUG
@ -23,10 +22,16 @@ from calibre.utils.localization import calibre_langcode_to_name, canonicalize_la
class FormatterFunctions(object): class FormatterFunctions(object):
error_function_body = ('def evaluate(self, formatter, kwargs, mi, locals):\n'
'\treturn "' +
_('Duplicate user function name {0}. '
'Change the name or ensure that the functions are identical')
+ '"')
def __init__(self): def __init__(self):
self._builtins = {} self._builtins = {}
self._functions = {} self._functions = {}
self._functions_from_library = defaultdict(list) self._functions_from_library = {}
def register_builtin(self, func_class): def register_builtin(self, func_class):
if not isinstance(func_class, FormatterFunction): if not isinstance(func_class, FormatterFunction):
@ -40,7 +45,7 @@ class FormatterFunctions(object):
for a in func_class.aliases: for a in func_class.aliases:
self._functions[a] = func_class self._functions[a] = func_class
def register_function(self, library_uuid, func_class, replace=False): def _register_function(self, func_class, replace=False):
if not isinstance(func_class, FormatterFunction): if not isinstance(func_class, FormatterFunction):
raise ValueError('Class %s is not an instance of FormatterFunction'%( raise ValueError('Class %s is not an instance of FormatterFunction'%(
func_class.__class__.__name__)) func_class.__class__.__name__))
@ -48,16 +53,36 @@ class FormatterFunctions(object):
if not replace and name in self._functions: if not replace and name in self._functions:
raise ValueError('Name %s already used'%name) raise ValueError('Name %s already used'%name)
self._functions[name] = func_class self._functions[name] = func_class
self._functions_from_library[library_uuid].append(name)
def function_exists(self, name): def register_functions(self, library_uuid, funcs):
return self._functions.get(name, None) self._functions_from_library[library_uuid] = funcs
self._register_functions()
def _register_functions(self):
for compiled_funcs in self._functions_from_library.itervalues():
for cls in compiled_funcs:
f = self._functions.get(cls.name, None)
replace = False
if f is not None:
existing_body = f.program_text
new_body = cls.program_text
if new_body != existing_body:
# Change the body of the template function to one that will
# return an error message. Also change the arg count to
# -1 (variable) to avoid template compilation errors
replace = True
func = [cls.name, '', -1, self.error_function_body.format(cls.name)]
cls = compile_user_function(*func)
else:
continue
formatter_functions()._register_function(cls, replace=replace)
def unregister_functions(self, library_uuid): def unregister_functions(self, library_uuid):
if library_uuid in self._functions_from_library: if library_uuid in self._functions_from_library:
for name in self._functions_from_library[library_uuid]: for cls in self._functions_from_library[library_uuid]:
self._functions.pop(name) self._functions.pop(cls.name, None)
self._functions_from_library.pop(library_uuid) self._functions_from_library.pop(library_uuid)
self._register_functions()
def get_builtins(self): def get_builtins(self):
return self._builtins return self._builtins
@ -1271,15 +1296,11 @@ class UserFunction(FormatterUserFunction):
cls = locals_['UserFunction'](name, doc, arg_count, eval_func) cls = locals_['UserFunction'](name, doc, arg_count, eval_func)
return cls return cls
error_function_body = ('def evaluate(self, formatter, kwargs, mi, locals):\n'
'\treturn "' +
_('Duplicate user function name {0}. '
'Change the name or ensure that the functions are identical')
+ '"')
def load_user_template_functions(library_uuid, funcs): def load_user_template_functions(library_uuid, funcs):
unload_user_template_functions(library_uuid) unload_user_template_functions(library_uuid)
compiled_funcs = []
for func in funcs: for func in funcs:
try: try:
# Force a name conflict to test the logic # Force a name conflict to test the logic
@ -1290,26 +1311,10 @@ def load_user_template_functions(library_uuid, funcs):
# source. This helps ensure that if the function already is defined # source. This helps ensure that if the function already is defined
# then white space differences don't cause them to compare differently # then white space differences don't cause them to compare differently
cls = compile_user_function(*func) compiled_funcs.append(compile_user_function(*func))
f = formatter_functions().function_exists(cls.name)
replace = False
if f is not None:
existing_body = f.program_text
new_body = cls.program_text
if new_body != existing_body:
# Change the body of the template function to one that will
# return an error message. Also change the arg count to
# -1 (variable) to avoid template compilation errors
replace = True
func[3] = error_function_body.format(func[0])
func[2] = -1
cls = compile_user_function(*func)
else:
continue
formatter_functions().register_function(library_uuid, cls, replace=replace)
except: except:
traceback.print_exc() traceback.print_exc()
formatter_functions().register_functions(library_uuid, compiled_funcs)
def unload_user_template_functions(library_uuid): def unload_user_template_functions(library_uuid):
formatter_functions().unregister_functions(library_uuid) formatter_functions().unregister_functions(library_uuid)