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
Exemplo n.º 2
0
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
Exemplo n.º 3
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()
Exemplo n.º 4
0
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
Exemplo n.º 5
0
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