Allow specifying a custom log function for template render (#99572)

* Allow specifying a custom log function for template render

* Bypass template cache when reporting errors + fix tests

* Send errors as events

* Fix logic for creating new TemplateEnvironment

* Add strict mode back

* Only send error events if report_errors is True

* Force test of websocket_api only

* Debug test

* Run pytest with higher verbosity

* Timeout after 1 minute, enable syslog output

* Adjust timeout

* Add debug logs

* Fix unsafe call to WebSocketHandler._send_message

* Remove debug code

* Improve test coverage

* Revert accidental change

* Include severity in error events

* Remove redundant information from error events
This commit is contained in:
Erik Montnemery 2023-09-06 10:03:35 +02:00 committed by GitHub
parent f41b045244
commit 48f7924e9e
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 374 additions and 84 deletions

View file

@ -5,6 +5,7 @@ from collections.abc import Callable
import datetime as dt
from functools import lru_cache, partial
import json
import logging
from typing import Any, cast
import voluptuous as vol
@ -505,6 +506,7 @@ def _cached_template(template_str: str, hass: HomeAssistant) -> template.Templat
vol.Optional("variables"): dict,
vol.Optional("timeout"): vol.Coerce(float),
vol.Optional("strict", default=False): bool,
vol.Optional("report_errors", default=False): bool,
}
)
@decorators.async_response
@ -513,14 +515,32 @@ async def handle_render_template(
) -> None:
"""Handle render_template command."""
template_str = msg["template"]
template_obj = _cached_template(template_str, hass)
report_errors: bool = msg["report_errors"]
if report_errors:
template_obj = template.Template(template_str, hass)
else:
template_obj = _cached_template(template_str, hass)
variables = msg.get("variables")
timeout = msg.get("timeout")
@callback
def _error_listener(level: int, template_error: str) -> None:
connection.send_message(
messages.event_message(
msg["id"],
{"error": template_error, "level": logging.getLevelName(level)},
)
)
@callback
def _thread_safe_error_listener(level: int, template_error: str) -> None:
hass.loop.call_soon_threadsafe(_error_listener, level, template_error)
if timeout:
try:
log_fn = _thread_safe_error_listener if report_errors else None
timed_out = await template_obj.async_render_will_timeout(
timeout, variables, strict=msg["strict"]
timeout, variables, strict=msg["strict"], log_fn=log_fn
)
except TemplateError as ex:
connection.send_error(msg["id"], const.ERR_TEMPLATE_ERROR, str(ex))
@ -542,7 +562,11 @@ async def handle_render_template(
track_template_result = updates.pop()
result = track_template_result.result
if isinstance(result, TemplateError):
connection.send_error(msg["id"], const.ERR_TEMPLATE_ERROR, str(result))
if not report_errors:
return
connection.send_message(
messages.event_message(msg["id"], {"error": str(result)})
)
return
connection.send_message(
@ -552,12 +576,14 @@ async def handle_render_template(
)
try:
log_fn = _error_listener if report_errors else None
info = async_track_template_result(
hass,
[TrackTemplate(template_obj, variables)],
_template_listener,
raise_on_template_error=True,
strict=msg["strict"],
log_fn=log_fn,
)
except TemplateError as ex:
connection.send_error(msg["id"], const.ERR_TEMPLATE_ERROR, str(ex))