Use per run on the fly jinja2 byte code caching

pull/82224/head
Matt Martz 2 years ago
parent d6a8df3218
commit 451f4f9185
No known key found for this signature in database
GPG Key ID: 40832D88E9FC91D8

@ -30,6 +30,7 @@ from contextlib import contextmanager
from numbers import Number from numbers import Number
from traceback import format_exc from traceback import format_exc
from jinja2.bccache import FileSystemBytecodeCache
from jinja2.exceptions import TemplateSyntaxError, UndefinedError from jinja2.exceptions import TemplateSyntaxError, UndefinedError
from jinja2.loaders import FileSystemLoader from jinja2.loaders import FileSystemLoader
from jinja2.nativetypes import NativeEnvironment from jinja2.nativetypes import NativeEnvironment
@ -592,6 +593,8 @@ class Templar:
self.jinja2_native = C.DEFAULT_JINJA2_NATIVE self.jinja2_native = C.DEFAULT_JINJA2_NATIVE
self._cache_pattern = '__ansible_j2_%s.cache'
def _compile_single_var(self, env): def _compile_single_var(self, env):
self.SINGLE_VAR = re.compile(r"^%s\s*(\w*)\s*%s$" % (env.variable_start_string, env.variable_end_string)) self.SINGLE_VAR = re.compile(r"^%s\s*(\w*)\s*%s$" % (env.variable_start_string, env.variable_end_string))
@ -920,11 +923,32 @@ class Templar:
hint = "Mandatory variable has not been overridden" hint = "Mandatory variable has not been overridden"
return AnsibleUndefined(hint) return AnsibleUndefined(hint)
@staticmethod
def _make_bucket_name(data, overrides, environment):
name = data
if overrides:
combined = overrides | {}
if environment.newline_sequence != '\n':
combined |= {'newline_sequence': environment.newline_sequence}
override_str = ';'.join(
f'{k}={v}' for k, v in sorted(combined.items())
)
name += f'[{override_str}]'
return name
def do_template(self, data, preserve_trailing_newlines=True, escape_backslashes=True, fail_on_undefined=None, overrides=None, disable_lookups=False, def do_template(self, data, preserve_trailing_newlines=True, escape_backslashes=True, fail_on_undefined=None, overrides=None, disable_lookups=False,
convert_data=False): convert_data=False):
if self.jinja2_native and not isinstance(data, string_types): if self.jinja2_native and not isinstance(data, string_types):
return data return data
mtime = None
if (data_source := getattr(data, '_data_source', None)):
mtime = os.stat(data_source).st_mtime
cache_dir = os.path.join(C.DEFAULT_LOCAL_TMP, 'j2cache')
if not os.path.isdir(cache_dir):
os.makedirs(cache_dir, mode=0o700, exist_ok=True)
bcc = FileSystemBytecodeCache(cache_dir, self._cache_pattern)
# For preserving the number of input newlines in the output (used # For preserving the number of input newlines in the output (used
# later in this method) # later in this method)
data_newlines = _count_newlines_from_end(data) data_newlines = _count_newlines_from_end(data)
@ -943,14 +967,33 @@ class Templar:
# Allow users to specify backslashes in playbooks as "\\" instead of as "\\\\". # Allow users to specify backslashes in playbooks as "\\" instead of as "\\\\".
data = _escape_backslashes(data, myenv) data = _escape_backslashes(data, myenv)
bucket = bcc.get_bucket(
myenv,
self._make_bucket_name(data, overrides, myenv),
None,
data
)
cache_file = os.path.join(bcc.directory, self._cache_pattern % bucket.key)
if bucket.code and mtime and mtime > os.stat(cache_file).st_mtime:
# Why would this ever be the case, I really hope users aren't modifying things
# in the middle of a run taht could cause this
os.unlink(cache_file)
bucket.code = None
try: try:
t = myenv.from_string(data) if bucket.code:
bcc.load_bytecode(bucket)
else:
bucket.code = myenv.compile(data)
bcc.set_bucket(bucket)
t = myenv.template_class.from_code(myenv, bucket.code, myenv.globals, None)
except TemplateSyntaxError as e: except TemplateSyntaxError as e:
raise AnsibleError("template error while templating string: %s. String: %s" % (to_native(e), to_native(data)), orig_exc=e) raise AnsibleError("template error while templating string: %s. String: %s" % (to_native(e), to_native(data)), orig_exc=e)
except Exception as e: except Exception as e:
if 'recursion' in to_native(e): if 'recursion' in to_native(e):
raise AnsibleError("recursive loop detected in template string: %s" % to_native(data), orig_exc=e) raise AnsibleError("recursive loop detected in template string: %s" % to_native(data), orig_exc=e)
else: else:
display.warning(f'Unexpected exception when templating {data!r}: {e}')
return data return data
if disable_lookups: if disable_lookups:

Loading…
Cancel
Save