Fix error handling in Shell Command integration (#116409)

* raise proper HomeAssistantError on command timeout

* raise proper HomeAssistantError on non-utf8 command output

* add error translation and test it

* Update homeassistant/components/shell_command/strings.json

* Update tests/components/shell_command/test_init.py

---------

Co-authored-by: G Johansson <goran.johansson@shiftit.se>
This commit is contained in:
Michael 2024-04-29 20:19:14 +02:00 committed by GitHub
parent d1c58467c5
commit 50d83bbdbf
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 35 additions and 8 deletions

View File

@ -15,7 +15,7 @@ from homeassistant.core import (
ServiceResponse,
SupportsResponse,
)
from homeassistant.exceptions import TemplateError
from homeassistant.exceptions import HomeAssistantError, TemplateError
from homeassistant.helpers import config_validation as cv, template
from homeassistant.helpers.typing import ConfigType
from homeassistant.util.json import JsonObjectType
@ -91,7 +91,7 @@ async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool:
try:
async with asyncio.timeout(COMMAND_TIMEOUT):
stdout_data, stderr_data = await process.communicate()
except TimeoutError:
except TimeoutError as err:
_LOGGER.error(
"Timed out running command: `%s`, after: %ss", cmd, COMMAND_TIMEOUT
)
@ -103,7 +103,14 @@ async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool:
process._transport.close() # type: ignore[attr-defined]
del process
raise
raise HomeAssistantError(
translation_domain=DOMAIN,
translation_key="timeout",
translation_placeholders={
"command": cmd,
"timeout": str(COMMAND_TIMEOUT),
},
) from err
if stdout_data:
_LOGGER.debug(
@ -135,11 +142,15 @@ async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool:
service_response["stdout"] = stdout_data.decode("utf-8").strip()
if stderr_data:
service_response["stderr"] = stderr_data.decode("utf-8").strip()
except UnicodeDecodeError:
except UnicodeDecodeError as err:
_LOGGER.exception(
"Unable to handle non-utf8 output of command: `%s`", cmd
)
raise
raise HomeAssistantError(
translation_domain=DOMAIN,
translation_key="non_utf8_output",
translation_placeholders={"command": cmd},
) from err
return service_response
return None

View File

@ -0,0 +1,10 @@
{
"exceptions": {
"timeout": {
"message": "Timed out running command: `{command}`, after: {timeout} seconds"
},
"non_utf8_output": {
"message": "Unable to handle non-utf8 output of command: `{command}`"
}
}
}

View File

@ -11,7 +11,7 @@ import pytest
from homeassistant.components import shell_command
from homeassistant.core import HomeAssistant
from homeassistant.exceptions import TemplateError
from homeassistant.exceptions import HomeAssistantError, TemplateError
from homeassistant.setup import async_setup_component
@ -199,7 +199,10 @@ async def test_non_text_stdout_capture(
assert not response
# Non-text output throws with 'return_response'
with pytest.raises(UnicodeDecodeError):
with pytest.raises(
HomeAssistantError,
match="Unable to handle non-utf8 output of command: `curl -o - https://raw.githubusercontent.com/home-assistant/assets/master/misc/loading-screen.gif`",
):
response = await hass.services.async_call(
"shell_command", "output_image", blocking=True, return_response=True
)
@ -258,7 +261,10 @@ async def test_do_not_run_forever(
side_effect=mock_create_subprocess_shell,
),
):
with pytest.raises(TimeoutError):
with pytest.raises(
HomeAssistantError,
match="Timed out running command: `mock_sleep 10000`, after: 0.001 seconds",
):
await hass.services.async_call(
shell_command.DOMAIN,
"test_service",