Reduce time to reload yaml and check configuration (#38469)

* Reduce time to reload yaml and check configuration

We spend a significant amount of time compiling templates
that we have already compiled.

Use an LRU cache to avoid re-compiling templates that
we frequently use.

* pylint

* switch to WeakValueDictionary

* preen
This commit is contained in:
J. Nick Koston 2020-08-03 15:00:44 -10:00 committed by GitHub
parent 63403f894d
commit 62c664fbbd
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 49 additions and 0 deletions

View File

@ -10,6 +10,7 @@ import random
import re
from typing import Any, Dict, Iterable, List, Optional, Union
from urllib.parse import urlencode as urllib_urlencode
import weakref
import jinja2
from jinja2 import contextfilter, contextfunction
@ -958,6 +959,7 @@ class TemplateEnvironment(ImmutableSandboxedEnvironment):
"""Initialise template environment."""
super().__init__()
self.hass = hass
self.template_cache = weakref.WeakValueDictionary()
self.filters["round"] = forgiving_round
self.filters["multiply"] = multiply
self.filters["log"] = logarithm
@ -1042,5 +1044,25 @@ class TemplateEnvironment(ImmutableSandboxedEnvironment):
"""Test if attribute is safe."""
return isinstance(obj, Namespace) or super().is_safe_attribute(obj, attr, value)
def compile(self, source, name=None, filename=None, raw=False, defer_init=False):
"""Compile the template."""
if (
name is not None
or filename is not None
or raw is not False
or defer_init is not False
):
# If there are any non-default keywords args, we do
# not cache. In prodution we currently do not have
# any instance of this.
return super().compile(source, name, filename, raw, defer_init)
cached = self.template_cache.get(source)
if cached is None:
cached = self.template_cache[source] = super().compile(source)
return cached
_NO_HASS_ENV = TemplateEnvironment(None)

View File

@ -1885,3 +1885,30 @@ def test_urlencode(hass):
hass,
)
assert tpl.async_render() == "the%20quick%20brown%20fox%20%3D%20true"
async def test_cache_garbage_collection():
"""Test caching a template."""
template_string = (
"{% set dict = {'foo': 'x&y', 'bar': 42} %} {{ dict | urlencode }}"
)
tpl = template.Template((template_string),)
tpl.ensure_valid()
assert template._NO_HASS_ENV.template_cache.get(
template_string
) # pylint: disable=protected-access
tpl2 = template.Template((template_string),)
tpl2.ensure_valid()
assert template._NO_HASS_ENV.template_cache.get(
template_string
) # pylint: disable=protected-access
del tpl
assert template._NO_HASS_ENV.template_cache.get(
template_string
) # pylint: disable=protected-access
del tpl2
assert not template._NO_HASS_ENV.template_cache.get(
template_string
) # pylint: disable=protected-access