Source code for toolkit.tpl

# coding=utf8

"""
The :mod:`tpl` module has some helper function implements base on :mod:`jinja2` for human.
"""

import os
import jinja2
import inspect
import functools

from toolkit import ToolkitException

_default_filters = {}
_default_tests = {}


class TplException(ToolkitException):
    pass


class RegisterError(TplException):
    pass


class RenderError(TplException):
    pass


[docs]def proxy_register(register_funtion): """ Proxy a function to a register function. :param register_funtion: a function need to proxy. :return: a proxy wrapper """ def wrapper(function): @functools.wraps(function) def register(excepted, filter_function=None): if inspect.isfunction(excepted): register_funtion(excepted.__name__, excepted) elif isinstance(excepted, str) and filter_function: if not inspect.isfunction(filter_function): raise RegisterError('Registered must be a function.') filter_function.__name__ = excepted register_funtion(excepted, filter_function) elif isinstance(excepted, str): def register_wrapper(func): if not inspect.isfunction(func): raise RegisterError('Registered must be a function.') func.__name__ = excepted register_funtion(excepted, func) return register_wrapper return register return wrapper
def _register_filter(name, filter_function): global _default_filters _default_filters[name] = filter_function def _register_test(name, test_function): global _default_tests _default_tests[name] = test_function @proxy_register(_register_filter)
[docs]def register_filter(): """ Add default filter function to template rendered environment. Register provide 3-way to add a function to environment and use on template. .. code-block:: python register_filter('lower', str.lower) @register_filter def lower(s): return s.lower() @register_filter('lower') def xxx_lower(): return s.lower() """ pass
@proxy_register(_register_test)
[docs]def register_test(): """ Add default test function to template rendered environment. Register provide 3-way to add a function to environment and use on template. .. code-block:: python register_test('digital', lambda v: type(v) in (int, float)) @register_test def digital(v): return type(v) in (int, float) @register_test('digital') def test_if_it_is_digital(): return type(v) in (int, float) """ pass
[docs]def create_env_by_folder(folder): """ Create :mod:`jinja2` environment with :meth:`jinja2.FileSystemLoader` :param folder: folder path. :return: jinja2 environment object. """ global _default_filters global _default_tests env = jinja2.Environment(loader=jinja2.FileSystemLoader(folder)) for k, f in _default_filters.iteritems(): env.filters[k] = f for k, f in _default_tests.iteritems(): env.tests[k] = f return env
[docs]def get_template(template_path): """ Get template object from absolute path. For example, the template has: .. code-block:: html {% macro add(a, b) -%} {{a + b}} {%- endmacro %} And call the macro as template object method. .. code-block:: python tpl = get_template('template.tpl') print(tpl.add(1, 2)) >>> 3 :param template_path: template absolute path. :return: template object """ folder, fname = os.path.split(template_path) return create_env_by_folder(folder).get_template(fname)
[docs]def render(template_path, output_file=None, **kwargs): """ Render jinja2 template file use absolute path. **Usage** A simple template as follow: .. code-block:: jinja This is a test template {{ title }} Then write render code by single line. .. code-block:: python render('template.tpl', name='toolkit') :param template_path: absolute file path of template file. :param output_file: the path of output file. :param kwargs: keyword arguments for template. """ template = get_template(template_path) rendered = template.render(**kwargs) if not output_file: return rendered with open(output_file) as f: f.write(rendered)
def render_recursive(folder, **kwargs): if not os.path.isdir(folder): err_msg = 'render_recursive() excepted a folder, but {} is not a folder.' raise RenderError(err_msg.format(folder))