"""Module to handle installing requirements.""" import asyncio from pathlib import Path import logging import os from typing import Any, Dict, List, Optional import homeassistant.util.package as pkg_util from homeassistant.core import HomeAssistant DATA_PIP_LOCK = 'pip_lock' DATA_PKG_CACHE = 'pkg_cache' CONSTRAINT_FILE = 'package_constraints.txt' PROGRESS_FILE = '.pip_progress' _LOGGER = logging.getLogger(__name__) async def async_process_requirements(hass: HomeAssistant, name: str, requirements: List[str]) -> bool: """Install the requirements for a component or platform. This method is a coroutine. """ pip_lock = hass.data.get(DATA_PIP_LOCK) if pip_lock is None: pip_lock = hass.data[DATA_PIP_LOCK] = asyncio.Lock() kwargs = pip_kwargs(hass.config.config_dir) async with pip_lock: for req in requirements: if pkg_util.is_installed(req): continue ret = await hass.async_add_executor_job( _install, hass, req, kwargs ) if not ret: _LOGGER.error("Not initializing %s because could not install " "requirement %s", name, req) return False return True def _install(hass: HomeAssistant, req: str, kwargs: Dict) -> bool: """Install requirement.""" progress_path = Path(hass.config.path(PROGRESS_FILE)) progress_path.touch() try: return pkg_util.install_package(req, **kwargs) finally: progress_path.unlink() def pip_kwargs(config_dir: Optional[str]) -> Dict[str, Any]: """Return keyword arguments for PIP install.""" is_docker = pkg_util.is_docker_env() kwargs = { 'constraints': os.path.join(os.path.dirname(__file__), CONSTRAINT_FILE), 'no_cache_dir': is_docker, } if 'WHEELS_LINKS' in os.environ: kwargs['find_links'] = os.environ['WHEELS_LINKS'] if not (config_dir is None or pkg_util.is_virtual_env()) and \ not is_docker: kwargs['target'] = os.path.join(config_dir, 'deps') return kwargs