Create issue if exception is not raised

This commit is contained in:
rikroe 2023-12-29 12:40:53 +01:00
parent f5f8d43d59
commit a61dd8619f
3 changed files with 53 additions and 6 deletions

View File

@ -27,6 +27,7 @@ from homeassistant.core import (
SupportsResponse,
)
from homeassistant.exceptions import HomeAssistantError, ServiceValidationError
from homeassistant.helpers import issue_registry as ir
from homeassistant.helpers.service import async_set_service_schema
from homeassistant.helpers.typing import ConfigType
from homeassistant.loader import bind_hass
@ -249,6 +250,16 @@ def execute(hass, filename, source, data=None, return_response=False):
if return_response:
raise ServiceValidationError(f"Error executing script: {err}") from err
logger.error("Error executing script: %s", err)
ir.create_issue(
hass,
DOMAIN,
filename,
breaks_in_ha_version="2024.4.0",
is_fixable=False,
severity=ir.IssueSeverity.ERROR,
translation_key="script_continue_on_error",
translation_placeholders={"script_name": filename.replace(".py", "")},
)
return None
except Exception as err: # pylint: disable=broad-except
if return_response:
@ -256,6 +267,16 @@ def execute(hass, filename, source, data=None, return_response=False):
f"Error executing script ({type(err).__name__}): {err}"
) from err
logger.exception("Error executing script: %s", err)
ir.create_issue(
hass,
DOMAIN,
filename,
breaks_in_ha_version="2024.4.0",
is_fixable=False,
severity=ir.IssueSeverity.ERROR,
translation_key="script_continue_on_error",
translation_placeholders={"script_name": filename.replace(".py", "")},
)
return None
return restricted_globals["output"]

View File

@ -4,5 +4,11 @@
"name": "[%key:common::action::reload%]",
"description": "Reloads all available Python scripts."
}
},
"issues": {
"script_continue_on_error": {
"title": "'python_script.{script_name}' logs errors and continues",
"description": "The `python_script` integration will change to raise script errors instead of logging them.\n\nThis will **stop** automations/scripts if `continue_on_error` is not set ([docs](https://www.home-assistant.io/docs/scripts/#continuing-on-error)).\n\nTo enable the new behavior, add a `response_variable` ([docs](https://www.home-assistant.io/docs/scripts/service-calls/#use-templates-to-handle-response-data)) to your service call."
}
}
}

View File

@ -7,6 +7,7 @@ import pytest
from homeassistant.components.python_script import DOMAIN, FOLDER, execute
from homeassistant.core import HomeAssistant
from homeassistant.exceptions import HomeAssistantError, ServiceValidationError
from homeassistant.helpers import issue_registry as ir
from homeassistant.helpers.service import async_get_all_descriptions
from homeassistant.setup import async_setup_component
@ -123,7 +124,9 @@ this is not valid Python
async def test_execute_runtime_error(
hass: HomeAssistant, caplog: pytest.LogCaptureFixture
hass: HomeAssistant,
caplog: pytest.LogCaptureFixture,
issue_registry: ir.IssueRegistry,
) -> None:
"""Test compile error logs error."""
caplog.set_level(logging.ERROR)
@ -135,9 +138,12 @@ raise Exception('boom')
await hass.async_block_till_done()
assert "Error executing script: boom" in caplog.text
assert len(issue_registry.issues) == 1
async def test_execute_runtime_error_with_response(hass: HomeAssistant) -> None:
async def test_execute_runtime_error_with_response(
hass: HomeAssistant, issue_registry: ir.IssueRegistry
) -> None:
"""Test compile error logs error."""
source = """
raise Exception('boom')
@ -148,10 +154,13 @@ raise Exception('boom')
assert type(task.exception()) == HomeAssistantError
assert "Error executing script (Exception): boom" in str(task.exception())
assert len(issue_registry.issues) == 0
async def test_accessing_async_methods(
hass: HomeAssistant, caplog: pytest.LogCaptureFixture
hass: HomeAssistant,
caplog: pytest.LogCaptureFixture,
issue_registry: ir.IssueRegistry,
) -> None:
"""Test compile error logs error."""
caplog.set_level(logging.ERROR)
@ -163,9 +172,12 @@ hass.async_stop()
await hass.async_block_till_done()
assert "Not allowed to access async methods" in caplog.text
assert len(issue_registry.issues) == 1
async def test_accessing_async_methods_with_response(hass: HomeAssistant) -> None:
async def test_accessing_async_methods_with_response(
hass: HomeAssistant, issue_registry: ir.IssueRegistry
) -> None:
"""Test compile error logs error."""
source = """
hass.async_stop()
@ -176,6 +188,7 @@ hass.async_stop()
assert type(task.exception()) == ServiceValidationError
assert "Not allowed to access async methods" in str(task.exception())
assert len(issue_registry.issues) == 0
async def test_using_complex_structures(
@ -196,7 +209,9 @@ logger.info('Logging from inside script: %s %s' % (mydict["a"], mylist[2]))
async def test_accessing_forbidden_methods(
hass: HomeAssistant, caplog: pytest.LogCaptureFixture
hass: HomeAssistant,
caplog: pytest.LogCaptureFixture,
issue_registry: ir.IssueRegistry,
) -> None:
"""Test compile error logs error."""
caplog.set_level(logging.ERROR)
@ -208,12 +223,16 @@ async def test_accessing_forbidden_methods(
"time.tzset()": "TimeWrapper.tzset",
}.items():
caplog.records.clear()
issue_registry.async_delete("python_script", "test.py")
hass.async_add_executor_job(execute, hass, "test.py", source, {})
await hass.async_block_till_done()
assert f"Not allowed to access {name}" in caplog.text
assert len(issue_registry.issues) == 1
async def test_accessing_forbidden_methods_with_response(hass: HomeAssistant) -> None:
async def test_accessing_forbidden_methods_with_response(
hass: HomeAssistant, issue_registry: ir.IssueRegistry
) -> None:
"""Test compile error logs error."""
for source, name in {
"hass.stop()": "HomeAssistant.stop",
@ -226,6 +245,7 @@ async def test_accessing_forbidden_methods_with_response(hass: HomeAssistant) ->
assert type(task.exception()) == ServiceValidationError
assert f"Not allowed to access {name}" in str(task.exception())
assert len(issue_registry.issues) == 0
async def test_iterating(hass: HomeAssistant) -> None: