Add support for simultaneous runs of Script helper#31937
Add support for simultaneous runs of Script helper#31937balloob merged 5 commits intohome-assistant:devfrom
Conversation
- 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.
|
Hey there @home-assistant/core, mind taking a look at this pull request as its been labeled with a integration ( |
|
Hey there @home-assistant/core, mind taking a look at this pull request as its been labeled with a integration ( |
|
Tests were added to help make sure the "legacy" behavior is maintained. Test for the new features (i.e., if_running & run_mode) still need to be added. |
This comment has been minimized.
This comment has been minimized.
|
This is off to a really great start! 🎉 |
- 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.
- 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.
|
FYI I'm working on the tests now. |
- 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.
balloob
left a comment
There was a problem hiding this comment.
Looks good to me! 🎉
Anything else you want to change before we merge it?
|
Thanks! The reviews have been great and not only helped improve the code, but I learned a lot, too. 😃 No, I think that's it. Besides the formal tests I also have a few scripts and automations I wrote specifically for this to test if the legacy behavior has been maintained, and they seem to indicate it has. So I think we're good. Next PR will focus on using the new capabilities in the script integration. After that automations. |
Proposed change
The Script helper (i.e.,
homeassistant.helpers.script) is used by several integrations to implement Home Assistant's Script Syntax, the most notable beingautomation&script. However the current behavior, when run multiple times simultaneously (e.g., via an automation that is triggered quickly), is at best unexpected, and worse, requires clumsy workarounds. (More on that below.)This PR is the first step towards resolving those issues. It improves the Script helper to properly support multiple, simultaneous runs, with options to provide usage flexibility. However, by default, it implements the current behavior to avoid breakage. The intention is to ultimately remove the so-called "legacy" behavior.
Type of change
Additional information
Background
See PR #31740. It was a first attempt at resolving these issues and contains a description of the problems related to the current behavior of the Script helper, as well as YAML for an example automation that is affected by it. However it was decided to break up the changes into smaller chunks, this being the first.
Although not mentioned in the other PR, the typical workaround is to move the automation's
actionsequence into a script, and then in the automation'sactionsection invoke the new script like this:This is an attempt to implement the new
restartbehavior described below.Also, there currently isn't an easy way to call a script completly "synchronously" if it contains a
delayorwait_template. The typical workaround to effectively make a script synchronous is to use aninput_boolean. It would first be turned on by the "caller", then the caller would use await_templateto wait for it to be turned off, which would done by the last step of the called script. This is an attempt to implement the newblockingbehavior described below.New Script helper options
if_running
async_runcalled) while previous run has not yet completed.errorRaise an exceptionignoreReturn without doing anything (previous run continues as-is)parallelStart run in new taskrestartStop previous run before starting new runrun_mode
async_runwill return.backgroundReturns immediatelylegacyImplements previous behavior, which is to return when done, or when suspended bydelayorwait_template, whichever comes firstblockingReturns when run has completedDefaults
run_mode=legacy(andif_runningis not used.)if_running=parallelandrun_mode=blocking.Checklist
black --fast homeassistant tests)If user exposed functionality or configuration variables are added/changed:
If the code communicates with devices, web services, or third-party tools:
Updated and included derived files by running:
python3 -m script.hassfest.requirements_all.txt.Updated by running
python3 -m script.gen_requirements_all..coveragerc.The integration reached or maintains the following Integration Quality Scale: