diff --git a/homeassistant/components/airly/__init__.py b/homeassistant/components/airly/__init__.py index dc2323ddd4e..80f3518c652 100644 --- a/homeassistant/components/airly/__init__.py +++ b/homeassistant/components/airly/__init__.py @@ -1,15 +1,16 @@ """The Airly component.""" import asyncio -import logging from datetime import timedelta +import logging -import async_timeout from aiohttp.client_exceptions import ClientConnectorError from airly import Airly from airly.exceptions import AirlyError +import async_timeout from homeassistant.const import CONF_API_KEY, CONF_LATITUDE, CONF_LONGITUDE from homeassistant.core import Config, HomeAssistant +from homeassistant.exceptions import ConfigEntryNotReady from homeassistant.helpers.aiohttp_client import async_get_clientsession from homeassistant.util import Throttle @@ -45,6 +46,9 @@ async def async_setup_entry(hass, config_entry): await airly.async_update() + if not airly.data: + raise ConfigEntryNotReady() + hass.data[DOMAIN] = {} hass.data[DOMAIN][DATA_CLIENT] = {} hass.data[DOMAIN][DATA_CLIENT][config_entry.entry_id] = airly @@ -81,7 +85,7 @@ class AirlyData: """Update Airly data.""" try: - with async_timeout.timeout(10): + with async_timeout.timeout(20): measurements = self.airly.create_measurements_session_point( self.latitude, self.longitude ) @@ -104,11 +108,8 @@ class AirlyData: self.data[ATTR_API_CAQI_DESCRIPTION] = index["description"] self.data[ATTR_API_ADVICE] = index["advice"] _LOGGER.debug("Data retrieved from Airly") - except ( - ValueError, - AirlyError, - asyncio.TimeoutError, - ClientConnectorError, - ) as error: + except asyncio.TimeoutError: + _LOGGER.error("Asyncio Timeout Error") + except (ValueError, AirlyError, ClientConnectorError) as error: _LOGGER.error(error) self.data = {} diff --git a/homeassistant/components/environment_canada/manifest.json b/homeassistant/components/environment_canada/manifest.json index 4d1a8094663..7a8313059fa 100644 --- a/homeassistant/components/environment_canada/manifest.json +++ b/homeassistant/components/environment_canada/manifest.json @@ -2,11 +2,7 @@ "domain": "environment_canada", "name": "Environment Canada", "documentation": "https://www.home-assistant.io/integrations/environment_canada", - "requirements": [ - "env_canada==0.0.27" - ], + "requirements": ["env_canada==0.0.29"], "dependencies": [], - "codeowners": [ - "@michaeldavie" - ] + "codeowners": ["@michaeldavie"] } diff --git a/homeassistant/components/jewish_calendar/manifest.json b/homeassistant/components/jewish_calendar/manifest.json index 08182daedd0..b343963153d 100644 --- a/homeassistant/components/jewish_calendar/manifest.json +++ b/homeassistant/components/jewish_calendar/manifest.json @@ -3,7 +3,7 @@ "name": "Jewish calendar", "documentation": "https://www.home-assistant.io/integrations/jewish_calendar", "requirements": [ - "hdate==0.9.1" + "hdate==0.9.3" ], "dependencies": [], "codeowners": [ diff --git a/homeassistant/components/myq/manifest.json b/homeassistant/components/myq/manifest.json index 73265b61c83..115c9cf515b 100644 --- a/homeassistant/components/myq/manifest.json +++ b/homeassistant/components/myq/manifest.json @@ -3,7 +3,7 @@ "name": "Myq", "documentation": "https://www.home-assistant.io/integrations/myq", "requirements": [ - "pymyq==2.0.0" + "pymyq==2.0.1" ], "dependencies": [], "codeowners": [] diff --git a/homeassistant/components/saj/manifest.json b/homeassistant/components/saj/manifest.json index 2dd701e9c7c..4d02ab74840 100644 --- a/homeassistant/components/saj/manifest.json +++ b/homeassistant/components/saj/manifest.json @@ -3,7 +3,7 @@ "name": "SAJ", "documentation": "https://www.home-assistant.io/integrations/saj", "requirements": [ - "pysaj==0.0.12" + "pysaj==0.0.13" ], "dependencies": [], "codeowners": [ diff --git a/homeassistant/components/songpal/manifest.json b/homeassistant/components/songpal/manifest.json index 55b02b66a59..b090a90d719 100644 --- a/homeassistant/components/songpal/manifest.json +++ b/homeassistant/components/songpal/manifest.json @@ -3,7 +3,7 @@ "name": "Songpal", "documentation": "https://www.home-assistant.io/integrations/songpal", "requirements": [ - "python-songpal==0.11.1" + "python-songpal==0.11.2" ], "dependencies": [], "codeowners": [ diff --git a/homeassistant/const.py b/homeassistant/const.py index f6f1a4f2de2..4e25e8c7dc3 100644 --- a/homeassistant/const.py +++ b/homeassistant/const.py @@ -1,7 +1,7 @@ """Constants used by Home Assistant components.""" MAJOR_VERSION = 0 MINOR_VERSION = 101 -PATCH_VERSION = "0" +PATCH_VERSION = "1" __short_version__ = "{}.{}".format(MAJOR_VERSION, MINOR_VERSION) __version__ = "{}.{}".format(__short_version__, PATCH_VERSION) REQUIRED_PYTHON_VER = (3, 6, 1) diff --git a/homeassistant/requirements.py b/homeassistant/requirements.py index 95738084f1f..74469ef2fcd 100644 --- a/homeassistant/requirements.py +++ b/homeassistant/requirements.py @@ -35,13 +35,21 @@ async def async_get_integration_with_requirements( This can raise IntegrationNotFound if manifest or integration is invalid, RequirementNotFound if there was some type of failure to install requirements. + + Does not handle circular dependencies. """ integration = await async_get_integration(hass, domain) - if hass.config.skip_pip or not integration.requirements: + if hass.config.skip_pip: return integration - await async_process_requirements(hass, integration.domain, integration.requirements) + if integration.requirements: + await async_process_requirements( + hass, integration.domain, integration.requirements + ) + + for dependency in integration.dependencies: + await async_get_integration_with_requirements(hass, dependency) return integration diff --git a/homeassistant/setup.py b/homeassistant/setup.py index 07de3b2942d..314938feeed 100644 --- a/homeassistant/setup.py +++ b/homeassistant/setup.py @@ -132,6 +132,17 @@ async def _async_setup_component( log_error(str(err)) return False + # Some integrations fail on import because they call functions incorrectly. + # So we do it before validating config to catch these errors. + try: + component = integration.get_component() + except ImportError: + log_error("Unable to import component", False) + return False + except Exception: # pylint: disable=broad-except + _LOGGER.exception("Setup failed for %s: unknown error", domain) + return False + processed_config = await conf_util.async_process_component_config( hass, config, integration ) @@ -143,12 +154,6 @@ async def _async_setup_component( start = timer() _LOGGER.info("Setting up %s", domain) - try: - component = integration.get_component() - except ImportError: - log_error("Unable to import component", False) - return False - if hasattr(component, "PLATFORM_SCHEMA"): # Entity components have their own warning warn_task = None diff --git a/requirements_all.txt b/requirements_all.txt index df9cafe7aa0..6621104f26d 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -456,7 +456,7 @@ enocean==0.50 enturclient==0.2.0 # homeassistant.components.environment_canada -env_canada==0.0.27 +env_canada==0.0.29 # homeassistant.components.envirophat # envirophat==0.0.6 @@ -622,7 +622,7 @@ hass-nabucasa==0.22 hbmqtt==0.9.5 # homeassistant.components.jewish_calendar -hdate==0.9.1 +hdate==0.9.3 # homeassistant.components.heatmiser heatmiserV3==0.9.1 @@ -1328,7 +1328,7 @@ pymsteams==0.1.12 pymusiccast==0.1.6 # homeassistant.components.myq -pymyq==2.0.0 +pymyq==2.0.1 # homeassistant.components.mysensors pymysensors==0.18.0 @@ -1423,7 +1423,7 @@ pyrepetier==3.0.5 pysabnzbd==1.1.0 # homeassistant.components.saj -pysaj==0.0.12 +pysaj==0.0.13 # homeassistant.components.sony_projector pysdcp==1 @@ -1564,7 +1564,7 @@ python-ripple-api==0.0.3 python-sochain-api==0.0.2 # homeassistant.components.songpal -python-songpal==0.11.1 +python-songpal==0.11.2 # homeassistant.components.synologydsm python-synology==0.2.0 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 36f423860d0..4861c5793d6 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -230,7 +230,7 @@ hass-nabucasa==0.22 hbmqtt==0.9.5 # homeassistant.components.jewish_calendar -hdate==0.9.1 +hdate==0.9.3 # homeassistant.components.here_travel_time herepy==0.6.3.1 diff --git a/tests/test_requirements.py b/tests/test_requirements.py index 780b175778e..548ea645360 100644 --- a/tests/test_requirements.py +++ b/tests/test_requirements.py @@ -112,7 +112,17 @@ async def test_install_missing_package(hass): async def test_get_integration_with_requirements(hass): """Check getting an integration with loaded requirements.""" hass.config.skip_pip = False - mock_integration(hass, MockModule("test_component", requirements=["hello==1.0.0"])) + mock_integration( + hass, MockModule("test_component_dep", requirements=["test-comp-dep==1.0.0"]) + ) + mock_integration( + hass, + MockModule( + "test_component", + requirements=["test-comp==1.0.0"], + dependencies=["test_component_dep"], + ), + ) with patch( "homeassistant.util.package.is_installed", return_value=False @@ -126,8 +136,13 @@ async def test_get_integration_with_requirements(hass): assert integration assert integration.domain == "test_component" - assert len(mock_is_installed.mock_calls) == 1 - assert len(mock_inst.mock_calls) == 1 + assert len(mock_is_installed.mock_calls) == 2 + assert mock_is_installed.mock_calls[0][1][0] == "test-comp==1.0.0" + assert mock_is_installed.mock_calls[1][1][0] == "test-comp-dep==1.0.0" + + assert len(mock_inst.mock_calls) == 2 + assert mock_inst.mock_calls[0][1][0] == "test-comp==1.0.0" + assert mock_inst.mock_calls[1][1][0] == "test-comp-dep==1.0.0" async def test_install_with_wheels_index(hass): diff --git a/tests/test_setup.py b/tests/test_setup.py index 9612c1784ef..8fd25091eb6 100644 --- a/tests/test_setup.py +++ b/tests/test_setup.py @@ -527,3 +527,11 @@ async def test_when_setup_already_loaded(hass): setup.async_when_setup(hass, "test", mock_callback) await hass.async_block_till_done() assert calls == ["test", "test"] + + +async def test_setup_import_blows_up(hass): + """Test that we handle it correctly when importing integration blows up.""" + with mock.patch( + "homeassistant.loader.Integration.get_component", side_effect=ValueError + ): + assert not await setup.async_setup_component(hass, "sun", {})