Add support for simultaneous runs of Script helper (#31937)

* Add tests for legacy Script helper behavior

* Add Script helper if_running and run_mode options

- if_running controls what happens if Script run while previous run
  has not completed. Can be:
  - error: Raise an exception
  - ignore: Return without doing anything (previous run continues as-is)
  - parallel: Start run in new task
  - restart: Stop previous run before starting new run
- run_mode controls when call to async_run will return. Can be:
  - background: Returns immediately
  - legacy: Implements previous behavior, which is to return when done,
            or when suspended by delay or wait_template
  - blocking: Returns when run has completed
- If neither is specified, default is run_mode=legacy (and if_running
  is not used.) Otherwise, defaults are if_running=parallel and
  run_mode=background. If run_mode is set to legacy then if_running must
  be None.
- Caller may supply a logger which will be used throughout instead of
  default module logger.
- Move Script running state into new helper classes, comprised of an
  abstract base class and two concrete clases, one for legacy behavior
  and one for new behavior.
- Remove some non-async methods, as well as call_from_config which has
  only been used in tests.
- Adjust tests accordingly.

* Change per review

- Change run_mode default from background to blocking.
- Make sure change listener is called, even when there's an unexpected
  exception.
- Make _ScriptRun.async_stop more graceful by using an asyncio.Event for
  signaling instead of simply cancelling Task.
- Subclass _ScriptRun for background & blocking behavior.

Also:

- Fix timeouts in _ScriptRun by converting timedeltas to float seconds.
- General cleanup.

* Change per review 2

- Don't propagate exceptions if call from user has already returned
  (i.e., for background runs or legacy runs that have suspended.)
- Allow user to specify if exceptions should be logged. They will still
  be logged regardless if exception is not propagated.
- Rename _start_script_delay and _start_wait_template_delay for
  clarity.
- Remove return value from Script.async_run.
- Fix missing await.
- Change call to self.is_running in Script.async_run to direct test of
  self._runs.

* Change per review 3 and add tests

- Remove Script.set_logger().
- Enhance existing tests to check all run modes.
- Add tests for new features.
- Fix a few minor bugs found by tests.
This commit is contained in:
Phil Bruckner 2020-02-24 16:56:00 -06:00 committed by GitHub
parent 309989be89
commit b2d7bc40dc
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 1774 additions and 881 deletions

View file

@ -8,7 +8,7 @@ import voluptuous as vol
import homeassistant.components.demo.notify as demo
import homeassistant.components.notify as notify
from homeassistant.core import callback
from homeassistant.helpers import discovery, script
from homeassistant.helpers import discovery
from homeassistant.setup import setup_component
from tests.common import assert_setup_component, get_test_home_assistant
@ -121,7 +121,7 @@ class TestNotifyDemo(unittest.TestCase):
def test_calling_notify_from_script_loaded_from_yaml_without_title(self):
"""Test if we can call a notify from a script."""
self._setup_notify()
conf = {
step = {
"service": "notify.notify",
"data": {
"data": {
@ -130,8 +130,8 @@ class TestNotifyDemo(unittest.TestCase):
},
"data_template": {"message": "Test 123 {{ 2 + 2 }}\n"},
}
script.call_from_config(self.hass, conf)
setup_component(self.hass, "script", {"script": {"test": {"sequence": step}}})
self.hass.services.call("script", "test")
self.hass.block_till_done()
assert len(self.events) == 1
assert {
@ -144,7 +144,7 @@ class TestNotifyDemo(unittest.TestCase):
def test_calling_notify_from_script_loaded_from_yaml_with_title(self):
"""Test if we can call a notify from a script."""
self._setup_notify()
conf = {
step = {
"service": "notify.notify",
"data": {
"data": {
@ -153,8 +153,8 @@ class TestNotifyDemo(unittest.TestCase):
},
"data_template": {"message": "Test 123 {{ 2 + 2 }}\n", "title": "Test"},
}
script.call_from_config(self.hass, conf)
setup_component(self.hass, "script", {"script": {"test": {"sequence": step}}})
self.hass.services.call("script", "test")
self.hass.block_till_done()
assert len(self.events) == 1
assert {