Load user-provided descriptions for python_scripts (#26069)

* Load user-provided descriptions for python_scripts

* Import SERVICE_DESCRIPTION_CACHE

* Use async_set_service_schema to register service descriptions

* Add python_script tests for loading service descriptions

* Use async/await in test
This commit is contained in:
Jeff Irion 2019-08-22 11:01:56 -07:00 committed by Paulus Schoutsen
parent 2d432da14c
commit aff151c90a
2 changed files with 114 additions and 1 deletions

View file

@ -9,8 +9,10 @@ import voluptuous as vol
from homeassistant.const import SERVICE_RELOAD
from homeassistant.exceptions import HomeAssistantError
from homeassistant.helpers.service import async_set_service_schema
from homeassistant.loader import bind_hass
from homeassistant.util import sanitize_filename
from homeassistant.util.yaml.loader import load_yaml
import homeassistant.util.dt as dt_util
_LOGGER = logging.getLogger(__name__)
@ -90,10 +92,23 @@ def discover_scripts(hass):
continue
hass.services.remove(DOMAIN, existing_service)
# Load user-provided service descriptions from python_scripts/services.yaml
services_yaml = os.path.join(path, "services.yaml")
if os.path.exists(services_yaml):
services_dict = load_yaml(services_yaml)
else:
services_dict = {}
for fil in glob.iglob(os.path.join(path, "*.py")):
name = os.path.splitext(os.path.basename(fil))[0]
hass.services.register(DOMAIN, name, python_script_service_handler)
service_desc = {
"description": services_dict.get(name, {}).get("description", ""),
"fields": services_dict.get(name, {}).get("fields", {}),
}
async_set_service_schema(hass, DOMAIN, name, service_desc)
@bind_hass
def execute_script(hass, name, data=None):

View file

@ -3,8 +3,11 @@ import asyncio
import logging
from unittest.mock import patch, mock_open
from homeassistant.helpers.service import async_get_all_descriptions
from homeassistant.setup import async_setup_component
from homeassistant.components.python_script import execute
from homeassistant.components.python_script import DOMAIN, execute, FOLDER
from tests.common import patch_yaml_files
@asyncio.coroutine
@ -289,6 +292,101 @@ def test_reload(hass):
assert hass.services.has_service("python_script", "reload")
async def test_service_descriptions(hass):
"""Test that service descriptions are loaded and reloaded correctly."""
# Test 1: no user-provided services.yaml file
scripts1 = [
"/some/config/dir/python_scripts/hello.py",
"/some/config/dir/python_scripts/world_beer.py",
]
service_descriptions1 = (
"hello:\n"
" description: Description of hello.py.\n"
" fields:\n"
" fake_param:\n"
" description: Parameter used by hello.py.\n"
" example: 'This is a test of python_script.hello'"
)
services_yaml1 = {
"{}/{}/services.yaml".format(
hass.config.config_dir, FOLDER
): service_descriptions1
}
with patch(
"homeassistant.components.python_script.os.path.isdir", return_value=True
), patch(
"homeassistant.components.python_script.glob.iglob", return_value=scripts1
), patch(
"homeassistant.components.python_script.os.path.exists", return_value=True
), patch_yaml_files(
services_yaml1
):
await async_setup_component(hass, DOMAIN, {})
descriptions = await async_get_all_descriptions(hass)
assert len(descriptions) == 1
assert descriptions[DOMAIN]["hello"]["description"] == "Description of hello.py."
assert (
descriptions[DOMAIN]["hello"]["fields"]["fake_param"]["description"]
== "Parameter used by hello.py."
)
assert (
descriptions[DOMAIN]["hello"]["fields"]["fake_param"]["example"]
== "This is a test of python_script.hello"
)
assert descriptions[DOMAIN]["world_beer"]["description"] == ""
assert bool(descriptions[DOMAIN]["world_beer"]["fields"]) is False
# Test 2: user-provided services.yaml file
scripts2 = [
"/some/config/dir/python_scripts/hello2.py",
"/some/config/dir/python_scripts/world_beer.py",
]
service_descriptions2 = (
"hello2:\n"
" description: Description of hello2.py.\n"
" fields:\n"
" fake_param:\n"
" description: Parameter used by hello2.py.\n"
" example: 'This is a test of python_script.hello2'"
)
services_yaml2 = {
"{}/{}/services.yaml".format(
hass.config.config_dir, FOLDER
): service_descriptions2
}
with patch(
"homeassistant.components.python_script.os.path.isdir", return_value=True
), patch(
"homeassistant.components.python_script.glob.iglob", return_value=scripts2
), patch(
"homeassistant.components.python_script.os.path.exists", return_value=True
), patch_yaml_files(
services_yaml2
):
await hass.services.async_call(DOMAIN, "reload", {}, blocking=True)
descriptions = await async_get_all_descriptions(hass)
assert len(descriptions) == 1
assert descriptions[DOMAIN]["hello2"]["description"] == "Description of hello2.py."
assert (
descriptions[DOMAIN]["hello2"]["fields"]["fake_param"]["description"]
== "Parameter used by hello2.py."
)
assert (
descriptions[DOMAIN]["hello2"]["fields"]["fake_param"]["example"]
== "This is a test of python_script.hello2"
)
@asyncio.coroutine
def test_sleep_warns_one(hass, caplog):
"""Test time.sleep warns once."""