This repository has been archived by the owner on Dec 21, 2022. It is now read-only.
forked from home-assistant/core
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathrequirements.py
95 lines (73 loc) · 3.04 KB
/
requirements.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
"""Module to handle installing requirements."""
import asyncio
from pathlib import Path
import logging
import os
from typing import Any, Dict, List, Optional
from homeassistant.exceptions import HomeAssistantError
import homeassistant.util.package as pkg_util
from homeassistant.core import HomeAssistant
from homeassistant.loader import async_get_integration, Integration
DATA_PIP_LOCK = "pip_lock"
DATA_PKG_CACHE = "pkg_cache"
CONSTRAINT_FILE = "package_constraints.txt"
PROGRESS_FILE = ".pip_progress"
_LOGGER = logging.getLogger(__name__)
class RequirementsNotFound(HomeAssistantError):
"""Raised when a component is not found."""
def __init__(self, domain: str, requirements: List) -> None:
"""Initialize a component not found error."""
super().__init__(f"Requirements for {domain} not found: {requirements}.")
self.domain = domain
self.requirements = requirements
async def async_get_integration_with_requirements(
hass: HomeAssistant, domain: str
) -> Integration:
"""Get an integration with installed requirements.
This can raise IntegrationNotFound if manifest or integration
is invalid, RequirementNotFound if there was some type of
failure to install requirements.
"""
integration = await async_get_integration(hass, domain)
if hass.config.skip_pip or not integration.requirements:
return integration
await async_process_requirements(hass, integration.domain, integration.requirements)
return integration
async def async_process_requirements(
hass: HomeAssistant, name: str, requirements: List[str]
) -> None:
"""Install the requirements for a component or platform.
This method is a coroutine. It will raise RequirementsNotFound
if an requirement can't be satisfied.
"""
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:
raise RequirementsNotFound(name, [req])
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