async def async_get_integration_with_requirements( hass: HomeAssistant, domain: str, done: set[str] | None = None) -> Integration: """Get an integration with all requirements installed, including the dependencies. This can raise IntegrationNotFound if manifest or integration is invalid, RequirementNotFound if there was some type of failure to install requirements. """ if done is None: done = {domain} else: done.add(domain) integration = await async_get_integration(hass, domain) if hass.config.skip_pip: return integration cache = hass.data.get(DATA_INTEGRATIONS_WITH_REQS) if cache is None: cache = hass.data[DATA_INTEGRATIONS_WITH_REQS] = {} int_or_evt: Integration | asyncio.Event | None | UndefinedType = cache.get( domain, UNDEFINED) if isinstance(int_or_evt, asyncio.Event): await int_or_evt.wait() int_or_evt = cache.get(domain, UNDEFINED) # When we have waited and it's UNDEFINED, it doesn't exist # We don't cache that it doesn't exist, or else people can't fix it # and then restart, because their config will never be valid. if int_or_evt is UNDEFINED: raise IntegrationNotFound(domain) if int_or_evt is not UNDEFINED: return cast(Integration, int_or_evt) event = cache[domain] = asyncio.Event() try: await _async_process_integration(hass, integration, done) except Exception: del cache[domain] event.set() raise cache[domain] = integration event.set() return integration
async def test_send_statistics_one_integration_fails(hass, caplog, aioclient_mock): """Test send statistics prefrences are defined.""" aioclient_mock.post(ANALYTICS_ENDPOINT_URL, status=200) analytics = Analytics(hass) await analytics.save_preferences({ATTR_BASE: True, ATTR_STATISTICS: True}) assert analytics.preferences[ATTR_BASE] assert analytics.preferences[ATTR_STATISTICS] hass.config.components = ["default_config"] with patch( "homeassistant.components.analytics.analytics.async_get_integration", side_effect=IntegrationNotFound("any"), ), patch("homeassistant.components.analytics.analytics.HA_VERSION", MOCK_VERSION): await analytics.send_analytics() post_call = aioclient_mock.mock_calls[0] assert "uuid" in post_call[2] assert post_call[2]["integration_count"] == 0
if (cache := hass.data.get(DATA_INTEGRATIONS_WITH_REQS)) is None: cache = hass.data[DATA_INTEGRATIONS_WITH_REQS] = {} int_or_evt: Integration | asyncio.Event | None | UndefinedType = cache.get( domain, UNDEFINED ) if isinstance(int_or_evt, asyncio.Event): await int_or_evt.wait() # When we have waited and it's UNDEFINED, it doesn't exist # We don't cache that it doesn't exist, or else people can't fix it # and then restart, because their config will never be valid. if (int_or_evt := cache.get(domain, UNDEFINED)) is UNDEFINED: raise IntegrationNotFound(domain) if int_or_evt is not UNDEFINED: return cast(Integration, int_or_evt) event = cache[domain] = asyncio.Event() try: await _async_process_integration(hass, integration, done) except Exception: del cache[domain] event.set() raise cache[domain] = integration event.set()
async def test_get_entries_ws(hass, hass_ws_client, clear_handlers): """Test get entries with the websocket api.""" assert await async_setup_component(hass, "config", {}) mock_integration(hass, MockModule("comp1")) mock_integration( hass, MockModule("comp2", partial_manifest={"integration_type": "helper"})) mock_integration(hass, MockModule("comp3")) entry = MockConfigEntry( domain="comp1", title="Test 1", source="bla", ) entry.add_to_hass(hass) MockConfigEntry( domain="comp2", title="Test 2", source="bla2", state=core_ce.ConfigEntryState.SETUP_ERROR, reason="Unsupported API", ).add_to_hass(hass) MockConfigEntry( domain="comp3", title="Test 3", source="bla3", disabled_by=core_ce.ConfigEntryDisabler.USER, ).add_to_hass(hass) ws_client = await hass_ws_client(hass) await ws_client.send_json({ "id": 5, "type": "config_entries/get", }) response = await ws_client.receive_json() assert response["id"] == 5 assert response["result"] == [ { "disabled_by": None, "domain": "comp1", "entry_id": ANY, "pref_disable_new_entities": False, "pref_disable_polling": False, "reason": None, "source": "bla", "state": "not_loaded", "supports_options": False, "supports_remove_device": False, "supports_unload": False, "title": "Test 1", }, { "disabled_by": None, "domain": "comp2", "entry_id": ANY, "pref_disable_new_entities": False, "pref_disable_polling": False, "reason": "Unsupported API", "source": "bla2", "state": "setup_error", "supports_options": False, "supports_remove_device": False, "supports_unload": False, "title": "Test 2", }, { "disabled_by": "user", "domain": "comp3", "entry_id": ANY, "pref_disable_new_entities": False, "pref_disable_polling": False, "reason": None, "source": "bla3", "state": "not_loaded", "supports_options": False, "supports_remove_device": False, "supports_unload": False, "title": "Test 3", }, ] await ws_client.send_json({ "id": 6, "type": "config_entries/get", "domain": "comp1", "type_filter": "integration", }) response = await ws_client.receive_json() assert response["id"] == 6 assert response["result"] == [{ "disabled_by": None, "domain": "comp1", "entry_id": ANY, "pref_disable_new_entities": False, "pref_disable_polling": False, "reason": None, "source": "bla", "state": "not_loaded", "supports_options": False, "supports_remove_device": False, "supports_unload": False, "title": "Test 1", }] # Verify we skip broken integrations with patch( "homeassistant.components.config.config_entries.async_get_integration", side_effect=IntegrationNotFound("any"), ): await ws_client.send_json({ "id": 7, "type": "config_entries/get", "type_filter": "integration", }) response = await ws_client.receive_json() assert response["id"] == 7 assert response["result"] == [ { "disabled_by": None, "domain": "comp1", "entry_id": ANY, "pref_disable_new_entities": False, "pref_disable_polling": False, "reason": None, "source": "bla", "state": "not_loaded", "supports_options": False, "supports_remove_device": False, "supports_unload": False, "title": "Test 1", }, { "disabled_by": None, "domain": "comp2", "entry_id": ANY, "pref_disable_new_entities": False, "pref_disable_polling": False, "reason": "Unsupported API", "source": "bla2", "state": "setup_error", "supports_options": False, "supports_remove_device": False, "supports_unload": False, "title": "Test 2", }, { "disabled_by": "user", "domain": "comp3", "entry_id": ANY, "pref_disable_new_entities": False, "pref_disable_polling": False, "reason": None, "source": "bla3", "state": "not_loaded", "supports_options": False, "supports_remove_device": False, "supports_unload": False, "title": "Test 3", }, ] # Verify we raise if something really goes wrong with patch( "homeassistant.components.config.config_entries.async_get_integration", side_effect=Exception, ): await ws_client.send_json({ "id": 8, "type": "config_entries/get", "type_filter": "integration", }) response = await ws_client.receive_json() assert response["id"] == 8 assert response["success"] is False
async def async_get_integration_with_requirements( hass: HomeAssistant, domain: str, done: Optional[Set[str]] = None) -> Integration: """Get an integration with all requirements installed, including the dependencies. This can raise IntegrationNotFound if manifest or integration is invalid, RequirementNotFound if there was some type of failure to install requirements. """ if done is None: done = {domain} else: done.add(domain) integration = await async_get_integration(hass, domain) if hass.config.skip_pip: return integration cache = hass.data.get(DATA_INTEGRATIONS_WITH_REQS) if cache is None: cache = hass.data[DATA_INTEGRATIONS_WITH_REQS] = {} int_or_evt: Union[Integration, asyncio.Event, None] = cache.get(domain, _UNDEF) if isinstance(int_or_evt, asyncio.Event): await int_or_evt.wait() int_or_evt = cache.get(domain, _UNDEF) # When we have waited and it's _UNDEF, it doesn't exist # We don't cache that it doesn't exist, or else people can't fix it # and then restart, because their config will never be valid. if int_or_evt is _UNDEF: raise IntegrationNotFound(domain) if int_or_evt is not _UNDEF: return cast(Integration, int_or_evt) event = cache[domain] = asyncio.Event() if integration.requirements: await async_process_requirements(hass, integration.domain, integration.requirements) deps_to_check = [ dep for dep in integration.dependencies + integration.after_dependencies if dep not in done ] for check_domain, to_check in DISCOVERY_INTEGRATIONS.items(): if (check_domain not in done and check_domain not in deps_to_check and any(check in integration.manifest for check in to_check)): deps_to_check.append(check_domain) if deps_to_check: await asyncio.gather(*[ async_get_integration_with_requirements(hass, dep, done) for dep in deps_to_check ]) cache[domain] = integration event.set() return integration