View Single Post
Old 12-26-2022, 06:34 AM   #3
chaley
Grand Sorcerer
chaley ought to be getting tired of karma fortunes by now.chaley ought to be getting tired of karma fortunes by now.chaley ought to be getting tired of karma fortunes by now.chaley ought to be getting tired of karma fortunes by now.chaley ought to be getting tired of karma fortunes by now.chaley ought to be getting tired of karma fortunes by now.chaley ought to be getting tired of karma fortunes by now.chaley ought to be getting tired of karma fortunes by now.chaley ought to be getting tired of karma fortunes by now.chaley ought to be getting tired of karma fortunes by now.chaley ought to be getting tired of karma fortunes by now.
 
Posts: 12,447
Karma: 8012886
Join Date: Jan 2010
Location: Notts, England
Device: Kobo Libra 2
I don't know if this is of any use to you, but here is how the Action Chains plugin invokes templates, with hopes that @capink doesn't mind me copying his code here. I picked this because it uses all of the template types, attributes in the python context, plugin-defined template functions, and user-provided templates and functions.

The main invocation function:
Code:
from calibre.ebooks.metadata.book.formatter import SafeFormat

[...]

class ACTemplateContext(PythonTemplateContext):
    pass

def get_template_output(template, kwargs, TEMPLATE_ERROR, book, global_vars={},
                        template_functions=None, context_attrs={}):
    if not template_functions:
        template_functions = get_template_functions[0]
    try:
        # calibre >= 6.7.0. support for python template context object
        context = ACTemplateContext()
        context.set_values(**context_attrs)
        output = SafeFormat().safe_format(template, kwargs, TEMPLATE_ERROR, book,
                                          global_vars=global_vars,
                                          template_functions=template_functions,
                                          python_context_object=context)
    except:
        output = SafeFormat().safe_format(template, kwargs, TEMPLATE_ERROR, book,
                                          global_vars=global_vars,
                                          template_functions=template_functions)
    return output
The template functions are built by this function. Much of the complexity comes from the fact that Action Chains adds many new Formatter Functions and that the plugin users can define their own functions.
Code:
def get_template_functions(plugin_action):
    all_functions = OrderedDict()

    builtin_functions = OrderedDict()
    for cls in BUILTIN_FUNCTIONS:
        builtin_functions[cls.name] = cls

    imported_functions = get_imported_functions(plugin_action)

    user_functions = get_user_functions(plugin_action)
    
    action_functions = get_action_functions(plugin_action)
    
    calibre_funcs = formatter_functions().get_functions()

    # Note: imported functions can either be class or instantiated object
    for function_name, function_obj in imported_functions.items():
        # dont override builtin functions
        if function_name in builtin_functions.keys():
            continue
        try:
            if isinstance(function_obj, (TemplateFunction, FormatterFunction)):
                function = function_obj
            elif issubclass(function_obj, TemplateFunction):
                function = function_obj(plugin_action)
            elif issubclass(function_obj, FormatterFunction):
                function = function_obj()
            all_functions[function_name] = function
        except TypeError as e:
            # TypeError: issubclass() arg 1 must be a class
            import traceback
            if DEBUG:
                prints('Action Chains: Error intializing imported function: Un-reconized object: {}\n{}'.format(function_obj, traceback.format_exc()))            
        except Exception as e:
            import traceback
            if DEBUG:
                prints('Action Chains: Error intializing imported function: {}\n{}'.format(function_name, traceback.format_exc()))

    for function_name, cls in user_functions.items():
        try:
            function = cls(plugin_action)
            all_functions[function_name] = function
        except Exception as e:
            import traceback
            if DEBUG:
                prints('Action Chains: Error intializing user function: {}\n{}'.format(function_name, traceback.format_exc()))

    for function_name, function in action_functions.items():
        try:
            all_functions[function_name] = function
        except Exception as e:
            import traceback
            if DEBUG:
                prints('Action Chains: Error intializing action function: {}\n{}'.format(function_name, traceback.format_exc()))

    for name, func in calibre_funcs.items():
        all_functions[name] = func

    # we keep this last in case we want to override some calibre formatter functions
    for function_name, cls in builtin_functions.items():
        function = cls(plugin_action)
        all_functions[function_name] = function

    return all_functions, builtin_functions, user_functions, action_functions, imported_functions
chaley is offline   Reply With Quote