diff --git a/homeassistant/bootstrap.py b/homeassistant/bootstrap.py index f8c595255d9..2da2f4fb7b5 100644 --- a/homeassistant/bootstrap.py +++ b/homeassistant/bootstrap.py @@ -14,8 +14,9 @@ import logging from collections import defaultdict import homeassistant -import homeassistant.util as util import homeassistant.util.dt as date_util +import homeassistant.util.package as pkg_util +import homeassistant.util.location as loc_util import homeassistant.config as config_util import homeassistant.loader as loader import homeassistant.components as core_components @@ -60,6 +61,17 @@ def setup_component(hass, domain, config=None): return True +def _handle_requirements(component, name): + """ Installs requirements for component. """ + if hasattr(component, 'REQUIREMENTS'): + for req in component.REQUIREMENTS: + if not pkg_util.install_package(req): + _LOGGER.error('Not initializing %s because could not install ' + 'dependency %s', name, req) + return False + return True + + def _setup_component(hass, domain, config): """ Setup a component for Home Assistant. """ component = loader.get_component(domain) @@ -74,6 +86,9 @@ def _setup_component(hass, domain, config): return False + if not _handle_requirements(component, domain): + return False + try: if component.setup(hass, config): hass.config.components.append(component.DOMAIN) @@ -109,18 +124,22 @@ def prepare_setup_platform(hass, config, domain, platform_name): if platform is None: return None - # Already loaded or no dependencies - elif (platform_path in hass.config.components or - not hasattr(platform, 'DEPENDENCIES')): + # Already loaded + elif platform_path in hass.config.components: return platform # Load dependencies - for component in platform.DEPENDENCIES: - if not setup_component(hass, component, config): - _LOGGER.error( - 'Unable to prepare setup for platform %s because dependency ' - '%s could not be initialized', platform_path, component) - return None + if hasattr(platform, 'DEPENDENCIES'): + for component in platform.DEPENDENCIES: + if not setup_component(hass, component, config): + _LOGGER.error( + 'Unable to prepare setup for platform %s because ' + 'dependency %s could not be initialized', platform_path, + component) + return None + + if not _handle_requirements(platform, platform_path): + return None return platform @@ -276,7 +295,7 @@ def process_ha_core_config(hass, config): _LOGGER.info('Auto detecting location and temperature unit') - info = util.detect_location_info() + info = loc_util.detect_location_info() if info is None: _LOGGER.error('Could not detect location information') diff --git a/homeassistant/util/environment.py b/homeassistant/util/environment.py new file mode 100644 index 00000000000..f2e41dfb306 --- /dev/null +++ b/homeassistant/util/environment.py @@ -0,0 +1,7 @@ +""" Environement helpers. """ +import sys + + +def is_virtual(): + """ Return if we run in a virtual environtment. """ + return sys.base_prefix != sys.prefix diff --git a/homeassistant/util/package.py b/homeassistant/util/package.py new file mode 100644 index 00000000000..35f662c6cb7 --- /dev/null +++ b/homeassistant/util/package.py @@ -0,0 +1,20 @@ +"""Helpers to install PyPi packages.""" +import subprocess + +from . import environment as env + +# If we are not in a virtual environment, install in user space +INSTALL_USER = not env.is_virtual() + + +def install_package(package, upgrade=False, user=INSTALL_USER): + """Install a package on PyPi. Accepts pip compatible package strings. + Return boolean if install successfull.""" + # Not using 'import pip; pip.main([])' because it breaks the logger + args = ['python3', '-m', 'pip', 'install', '--disable-pip-version-check', + '--quiet', package] + if upgrade: + args.append('--upgrade') + if user: + args.append('--user') + return not subprocess.call(args)