Skip to content

Commit 3390fb3

Browse files
emontnemeryfrenck
authored andcommitted
Don't overwrite setup state in async_set_domains_to_be_loaded (#137547)
1 parent 3ebb58f commit 3390fb3

File tree

2 files changed

+78
-6
lines changed

2 files changed

+78
-6
lines changed

homeassistant/setup.py

+7-1
Original file line numberDiff line numberDiff line change
@@ -132,7 +132,13 @@ def async_set_domains_to_be_loaded(hass: core.HomeAssistant, domains: set[str])
132132
- Keep track of domains which will load but have not yet finished loading
133133
"""
134134
setup_done_futures = hass.data.setdefault(DATA_SETUP_DONE, {})
135-
setup_done_futures.update({domain: hass.loop.create_future() for domain in domains})
135+
setup_futures = hass.data.setdefault(DATA_SETUP, {})
136+
old_domains = set(setup_futures) | set(setup_done_futures) | hass.config.components
137+
if overlap := old_domains & domains:
138+
_LOGGER.debug("Domains to be loaded %s already loaded or pending", overlap)
139+
setup_done_futures.update(
140+
{domain: hass.loop.create_future() for domain in domains - old_domains}
141+
)
136142

137143

138144
def setup_component(hass: core.HomeAssistant, domain: str, config: ConfigType) -> bool:

tests/test_setup.py

+71-5
Original file line numberDiff line numberDiff line change
@@ -363,20 +363,24 @@ async def test_component_failing_setup(hass: HomeAssistant) -> None:
363363

364364
async def test_component_exception_setup(hass: HomeAssistant) -> None:
365365
"""Test component that raises exception during setup."""
366-
setup.async_set_domains_to_be_loaded(hass, {"comp"})
366+
domain = "comp"
367+
setup.async_set_domains_to_be_loaded(hass, {domain})
367368

368369
def exception_setup(hass: HomeAssistant, config: ConfigType) -> bool:
369370
"""Raise exception."""
370371
raise Exception("fail!") # noqa: TRY002
371372

372-
mock_integration(hass, MockModule("comp", setup=exception_setup))
373+
mock_integration(hass, MockModule(domain, setup=exception_setup))
373374

374-
assert not await setup.async_setup_component(hass, "comp", {})
375-
assert "comp" not in hass.config.components
375+
assert not await setup.async_setup_component(hass, domain, {})
376+
assert domain in hass.data[setup.DATA_SETUP]
377+
assert domain not in hass.data[setup.DATA_SETUP_DONE]
378+
assert domain not in hass.config.components
376379

377380

378381
async def test_component_base_exception_setup(hass: HomeAssistant) -> None:
379382
"""Test component that raises exception during setup."""
383+
domain = "comp"
380384
setup.async_set_domains_to_be_loaded(hass, {"comp"})
381385

382386
def exception_setup(hass: HomeAssistant, config: ConfigType) -> bool:
@@ -389,7 +393,69 @@ def exception_setup(hass: HomeAssistant, config: ConfigType) -> bool:
389393
await setup.async_setup_component(hass, "comp", {})
390394
assert str(exc_info.value) == "fail!"
391395

392-
assert "comp" not in hass.config.components
396+
assert domain in hass.data[setup.DATA_SETUP]
397+
assert domain not in hass.data[setup.DATA_SETUP_DONE]
398+
assert domain not in hass.config.components
399+
400+
401+
async def test_set_domains_to_be_loaded(hass: HomeAssistant) -> None:
402+
"""Test async_set_domains_to_be_loaded."""
403+
domain_good = "comp_good"
404+
domain_bad = "comp_bad"
405+
domain_base_exception = "comp_base_exception"
406+
domain_exception = "comp_exception"
407+
domains = {domain_good, domain_bad, domain_exception, domain_base_exception}
408+
setup.async_set_domains_to_be_loaded(hass, domains)
409+
410+
assert set(hass.data[setup.DATA_SETUP_DONE]) == domains
411+
setup_done = dict(hass.data[setup.DATA_SETUP_DONE])
412+
413+
# Calling async_set_domains_to_be_loaded again should not create new futures
414+
setup.async_set_domains_to_be_loaded(hass, domains)
415+
assert setup_done == hass.data[setup.DATA_SETUP_DONE]
416+
417+
def good_setup(hass: HomeAssistant, config: ConfigType) -> bool:
418+
"""Success."""
419+
return True
420+
421+
def bad_setup(hass: HomeAssistant, config: ConfigType) -> bool:
422+
"""Fail."""
423+
return False
424+
425+
def base_exception_setup(hass: HomeAssistant, config: ConfigType) -> bool:
426+
"""Raise exception."""
427+
raise BaseException("fail!") # noqa: TRY002
428+
429+
def exception_setup(hass: HomeAssistant, config: ConfigType) -> bool:
430+
"""Raise exception."""
431+
raise Exception("fail!") # noqa: TRY002
432+
433+
mock_integration(hass, MockModule(domain_good, setup=good_setup))
434+
mock_integration(hass, MockModule(domain_bad, setup=bad_setup))
435+
mock_integration(
436+
hass, MockModule(domain_base_exception, setup=base_exception_setup)
437+
)
438+
mock_integration(hass, MockModule(domain_exception, setup=exception_setup))
439+
440+
# Set up the four components
441+
assert await setup.async_setup_component(hass, domain_good, {})
442+
assert not await setup.async_setup_component(hass, domain_bad, {})
443+
assert not await setup.async_setup_component(hass, domain_exception, {})
444+
with pytest.raises(BaseException, match="fail!"):
445+
await setup.async_setup_component(hass, domain_base_exception, {})
446+
447+
# Check the result of the setup
448+
assert not hass.data[setup.DATA_SETUP_DONE]
449+
assert set(hass.data[setup.DATA_SETUP]) == {
450+
domain_bad,
451+
domain_exception,
452+
domain_base_exception,
453+
}
454+
assert set(hass.config.components) == {domain_good}
455+
456+
# Calling async_set_domains_to_be_loaded again should not create any new futures
457+
setup.async_set_domains_to_be_loaded(hass, domains)
458+
assert not hass.data[setup.DATA_SETUP_DONE]
393459

394460

395461
async def test_component_setup_with_validation_and_dependency(

0 commit comments

Comments
 (0)