mirror of
https://github.com/thegeeklab/ansible-later.git
synced 2024-11-26 23:00:36 +00:00
201 lines
6.0 KiB
Python
201 lines
6.0 KiB
Python
|
#
|
||
|
# Jinja2 (http://jinja.pocoo.org) based template renderer.
|
||
|
#
|
||
|
# Author: Satoru SATOH <ssato redhat.com>
|
||
|
# License: MIT
|
||
|
#
|
||
|
# pylint: disable=unused-argument,wrong-import-position,wrong-import-order
|
||
|
"""anyconfig.template module
|
||
|
|
||
|
Template rendering module for jinja2-based template config files.
|
||
|
"""
|
||
|
from __future__ import absolute_import
|
||
|
|
||
|
import codecs
|
||
|
import locale
|
||
|
import logging
|
||
|
import os
|
||
|
|
||
|
import anyconfig.compat
|
||
|
|
||
|
LOGGER = logging.getLogger(__name__)
|
||
|
RENDER_S_OPTS = ['ctx', 'paths', 'filters']
|
||
|
RENDER_OPTS = RENDER_S_OPTS + ['ask']
|
||
|
SUPPORTED = False
|
||
|
try:
|
||
|
import jinja2
|
||
|
from jinja2.exceptions import TemplateNotFound
|
||
|
|
||
|
SUPPORTED = True
|
||
|
|
||
|
def tmpl_env(paths):
|
||
|
"""
|
||
|
:param paths: A list of template search paths
|
||
|
"""
|
||
|
return jinja2.Environment(loader=jinja2.FileSystemLoader(paths))
|
||
|
|
||
|
except ImportError:
|
||
|
LOGGER.warning("Jinja2 is not available on your system, so "
|
||
|
"template support will be disabled.")
|
||
|
|
||
|
class TemplateNotFound(RuntimeError):
|
||
|
"""Dummy exception"""
|
||
|
pass
|
||
|
|
||
|
def tmpl_env(*args):
|
||
|
"""Dummy function"""
|
||
|
return None
|
||
|
|
||
|
|
||
|
def copen(filepath, flag='r', encoding=None):
|
||
|
|
||
|
"""
|
||
|
FIXME: How to test this ?
|
||
|
|
||
|
>>> c = copen(__file__)
|
||
|
>>> c is not None
|
||
|
True
|
||
|
"""
|
||
|
if encoding is None:
|
||
|
encoding = locale.getdefaultlocale()[1]
|
||
|
|
||
|
return codecs.open(filepath, flag, encoding)
|
||
|
|
||
|
|
||
|
def make_template_paths(template_file, paths=None):
|
||
|
"""
|
||
|
Make up a list of template search paths from given `template_file`
|
||
|
(absolute or relative path to the template file) and/or `paths` (a list of
|
||
|
template search paths given by user).
|
||
|
|
||
|
NOTE: User-given `paths` will take higher priority over a dir of
|
||
|
template_file.
|
||
|
|
||
|
:param template_file: Absolute or relative path to the template file
|
||
|
:param paths: A list of template search paths
|
||
|
:return: List of template paths ([str])
|
||
|
|
||
|
>>> make_template_paths("/path/to/a/template")
|
||
|
['/path/to/a']
|
||
|
>>> make_template_paths("/path/to/a/template", ["/tmp"])
|
||
|
['/tmp', '/path/to/a']
|
||
|
>>> os.chdir("/tmp")
|
||
|
>>> make_template_paths("./path/to/a/template")
|
||
|
['/tmp/path/to/a']
|
||
|
>>> make_template_paths("./path/to/a/template", ["/tmp"])
|
||
|
['/tmp', '/tmp/path/to/a']
|
||
|
"""
|
||
|
tmpldir = os.path.abspath(os.path.dirname(template_file))
|
||
|
return [tmpldir] if paths is None else paths + [tmpldir]
|
||
|
|
||
|
|
||
|
def render_s(tmpl_s, ctx=None, paths=None, filters=None):
|
||
|
"""
|
||
|
Compile and render given template string `tmpl_s` with context `context`.
|
||
|
|
||
|
:param tmpl_s: Template string
|
||
|
:param ctx: Context dict needed to instantiate templates
|
||
|
:param paths: Template search paths
|
||
|
:param filters: Custom filters to add into template engine
|
||
|
:return: Compiled result (str)
|
||
|
|
||
|
>>> render_s("aaa") == "aaa"
|
||
|
True
|
||
|
>>> s = render_s('a = {{ a }}, b = "{{ b }}"', {'a': 1, 'b': 'bbb'})
|
||
|
>>> if SUPPORTED:
|
||
|
... assert s == 'a = 1, b = "bbb"'
|
||
|
"""
|
||
|
if paths is None:
|
||
|
paths = [os.curdir]
|
||
|
|
||
|
env = tmpl_env(paths)
|
||
|
|
||
|
if env is None:
|
||
|
return tmpl_s
|
||
|
|
||
|
if filters is not None:
|
||
|
env.filters.update(filters)
|
||
|
|
||
|
if ctx is None:
|
||
|
ctx = {}
|
||
|
|
||
|
return tmpl_env(paths).from_string(tmpl_s).render(**ctx)
|
||
|
|
||
|
|
||
|
def render_impl(template_file, ctx=None, paths=None, filters=None):
|
||
|
"""
|
||
|
:param template_file: Absolute or relative path to the template file
|
||
|
:param ctx: Context dict needed to instantiate templates
|
||
|
:param filters: Custom filters to add into template engine
|
||
|
:return: Compiled result (str)
|
||
|
"""
|
||
|
env = tmpl_env(make_template_paths(template_file, paths))
|
||
|
|
||
|
if env is None:
|
||
|
return copen(template_file).read()
|
||
|
|
||
|
if filters is not None:
|
||
|
env.filters.update(filters)
|
||
|
|
||
|
if ctx is None:
|
||
|
ctx = {}
|
||
|
|
||
|
return env.get_template(os.path.basename(template_file)).render(**ctx)
|
||
|
|
||
|
|
||
|
def render(filepath, ctx=None, paths=None, ask=False, filters=None):
|
||
|
"""
|
||
|
Compile and render template and return the result as a string.
|
||
|
|
||
|
:param template_file: Absolute or relative path to the template file
|
||
|
:param ctx: Context dict needed to instantiate templates
|
||
|
:param paths: Template search paths
|
||
|
:param ask: Ask user for missing template location if True
|
||
|
:param filters: Custom filters to add into template engine
|
||
|
:return: Compiled result (str)
|
||
|
"""
|
||
|
try:
|
||
|
return render_impl(filepath, ctx, paths, filters)
|
||
|
except TemplateNotFound as mtmpl:
|
||
|
if not ask:
|
||
|
raise
|
||
|
|
||
|
usr_tmpl = anyconfig.compat.raw_input(os.linesep + ""
|
||
|
"*** Missing template "
|
||
|
"'%s'. Please enter absolute "
|
||
|
"or relative path starting from "
|
||
|
"'.' to the template file: " %
|
||
|
mtmpl)
|
||
|
usr_tmpl = os.path.normpath(usr_tmpl.strip())
|
||
|
paths = make_template_paths(usr_tmpl, paths)
|
||
|
|
||
|
return render_impl(usr_tmpl, ctx, paths, filters)
|
||
|
|
||
|
|
||
|
def try_render(filepath=None, content=None, **options):
|
||
|
"""
|
||
|
Compile and render template and return the result as a string.
|
||
|
|
||
|
:param filepath: Absolute or relative path to the template file
|
||
|
:param content: Template content (str)
|
||
|
:param options: Keyword options passed to :func:`render` defined above.
|
||
|
:return: Compiled result (str) or None
|
||
|
"""
|
||
|
if filepath is None and content is None:
|
||
|
raise ValueError("Either 'path' or 'content' must be some value!")
|
||
|
|
||
|
tmpl_s = filepath or content[:10] + " ..."
|
||
|
LOGGER.debug("Compiling: %s", tmpl_s)
|
||
|
try:
|
||
|
if content is None:
|
||
|
render_opts = anyconfig.utils.filter_options(RENDER_OPTS, options)
|
||
|
return render(filepath, **render_opts)
|
||
|
render_s_opts = anyconfig.utils.filter_options(RENDER_S_OPTS, options)
|
||
|
return render_s(content, **render_s_opts)
|
||
|
except Exception as exc:
|
||
|
LOGGER.warning("Failed to compile '%s'. It may not be a template.%s"
|
||
|
"exc=%r", tmpl_s, os.linesep, exc)
|
||
|
return None
|
||
|
|
||
|
# vim:sw=4:ts=4:et:
|