1
0
mirror of https://github.com/home-assistant/core.git synced 2025-12-24 21:06:19 +00:00

Remove usage of "run_until_complete" (#16617)

* De-run_forever()-ization

* Use asyncio.run (or our own implementation on Python <3.7)
* hass.start is only used by tests
* setup_and_run_hass() is now async
* Add "main" async hass.run method
* move SIGINT handling to helpers/signal.py
  * add flag to .run to disable hass's signal handlers
* Teach async_start and async_stop to not step on each other
  (more than necessary)

* shorten over-long lines

* restore missing "import asyncio"

* move run_asyncio to homeassistant.util.async_

* LOGGER: warn => warning

* Add "force" flag to async_stop

only useful for testing

* Add 'attrs==18.2.0' to requirements_all.txt

Required for keeping requirements_test_all.txt in sync, where it is in
turn required to prevent auto-downgrading "attrs" during "pip install"

* Fixes for mypy

* Fix "mock_signal" fixture

* Revert mistaken edit

* Flake8 fixes

* mypy fixes

* pylint fix

* Revert adding attrs== to requirements_test*.txt

solved by using "pip -c"

* Rename "run" to "async_run", as per calling conventions
This commit is contained in:
Matthias Urlichs
2018-09-19 15:40:02 +02:00
committed by Paulus Schoutsen
parent da108f1999
commit 0121e3cb04
11 changed files with 126 additions and 37 deletions

View File

@@ -154,6 +154,8 @@ class HomeAssistant:
self.state = CoreState.not_running
self.exit_code = 0 # type: int
self.config_entries = None # type: Optional[ConfigEntries]
# If not None, use to signal end-of-loop
self._stopped = None # type: Optional[asyncio.Event]
@property
def is_running(self) -> bool:
@@ -161,23 +163,45 @@ class HomeAssistant:
return self.state in (CoreState.starting, CoreState.running)
def start(self) -> int:
"""Start home assistant."""
"""Start home assistant.
Note: This function is only used for testing.
For regular use, use "await hass.run()".
"""
# Register the async start
fire_coroutine_threadsafe(self.async_start(), self.loop)
# Run forever and catch keyboard interrupt
# Run forever
try:
# Block until stopped
_LOGGER.info("Starting Home Assistant core loop")
self.loop.run_forever()
except KeyboardInterrupt:
self.loop.call_soon_threadsafe(
self.loop.create_task, self.async_stop())
self.loop.run_forever()
finally:
self.loop.close()
return self.exit_code
async def async_run(self, *, attach_signals: bool = True) -> int:
"""Home Assistant main entry point.
Start Home Assistant and block until stopped.
This method is a coroutine.
"""
if self.state != CoreState.not_running:
raise RuntimeError("HASS is already running")
# _async_stop will set this instead of stopping the loop
self._stopped = asyncio.Event()
await self.async_start()
if attach_signals:
from homeassistant.helpers.signal \
import async_register_signal_handling
async_register_signal_handling(self)
await self._stopped.wait()
return self.exit_code
async def async_start(self) -> None:
"""Finalize startup from inside the event loop.
@@ -203,6 +227,13 @@ class HomeAssistant:
# Allow automations to set up the start triggers before changing state
await asyncio.sleep(0)
if self.state != CoreState.starting:
_LOGGER.warning(
'Home Assistant startup has been interrupted. '
'Its state may be inconsistent.')
return
self.state = CoreState.running
_async_create_timer(self)
@@ -321,13 +352,32 @@ class HomeAssistant:
def stop(self) -> None:
"""Stop Home Assistant and shuts down all threads."""
if self.state == CoreState.not_running: # just ignore
return
fire_coroutine_threadsafe(self.async_stop(), self.loop)
async def async_stop(self, exit_code: int = 0) -> None:
async def async_stop(self, exit_code: int = 0, *,
force: bool = False) -> None:
"""Stop Home Assistant and shuts down all threads.
The "force" flag commands async_stop to proceed regardless of
Home Assistan't current state. You should not set this flag
unless you're testing.
This method is a coroutine.
"""
if not force:
# Some tests require async_stop to run,
# regardless of the state of the loop.
if self.state == CoreState.not_running: # just ignore
return
if self.state == CoreState.stopping:
_LOGGER.info("async_stop called twice: ignored")
return
if self.state == CoreState.starting:
# This may not work
_LOGGER.warning("async_stop called before startup is complete")
# stage 1
self.state = CoreState.stopping
self.async_track_tasks()
@@ -341,7 +391,11 @@ class HomeAssistant:
self.executor.shutdown()
self.exit_code = exit_code
self.loop.stop()
if self._stopped is not None:
self._stopped.set()
else:
self.loop.stop()
@attr.s(slots=True, frozen=True)