mirror of
https://github.com/home-assistant/core.git
synced 2025-04-26 02:07:54 +00:00
Use uv at runtime too (#125110)
This commit is contained in:
parent
b3377fe5fb
commit
7555f209b6
2
.github/workflows/builder.yml
vendored
2
.github/workflows/builder.yml
vendored
@ -126,7 +126,7 @@ jobs:
|
|||||||
env:
|
env:
|
||||||
UV_PRERELEASE: allow
|
UV_PRERELEASE: allow
|
||||||
run: |
|
run: |
|
||||||
python3 -m pip install "$(grep '^uv' < requirements_test.txt)"
|
python3 -m pip install "$(grep '^uv' < requirements.txt)"
|
||||||
uv pip install packaging tomli
|
uv pip install packaging tomli
|
||||||
uv pip install .
|
uv pip install .
|
||||||
python3 script/version_bump.py nightly --set-nightly-version "${{ needs.init.outputs.version }}"
|
python3 script/version_bump.py nightly --set-nightly-version "${{ needs.init.outputs.version }}"
|
||||||
|
6
.github/workflows/ci.yaml
vendored
6
.github/workflows/ci.yaml
vendored
@ -252,7 +252,7 @@ jobs:
|
|||||||
python -m venv venv
|
python -m venv venv
|
||||||
. venv/bin/activate
|
. venv/bin/activate
|
||||||
python --version
|
python --version
|
||||||
pip install "$(grep '^uv' < requirements_test.txt)"
|
pip install "$(grep '^uv' < requirements.txt)"
|
||||||
uv pip install "$(cat requirements_test.txt | grep pre-commit)"
|
uv pip install "$(cat requirements_test.txt | grep pre-commit)"
|
||||||
- name: Restore pre-commit environment from cache
|
- name: Restore pre-commit environment from cache
|
||||||
id: cache-precommit
|
id: cache-precommit
|
||||||
@ -476,7 +476,7 @@ jobs:
|
|||||||
- name: Generate partial uv restore key
|
- name: Generate partial uv restore key
|
||||||
id: generate-uv-key
|
id: generate-uv-key
|
||||||
run: |
|
run: |
|
||||||
uv_version=$(cat requirements_test.txt | grep uv | cut -d '=' -f 3)
|
uv_version=$(cat requirements.txt | grep uv | cut -d '=' -f 3)
|
||||||
echo "version=${uv_version}" >> $GITHUB_OUTPUT
|
echo "version=${uv_version}" >> $GITHUB_OUTPUT
|
||||||
echo "key=uv-${{ env.UV_CACHE_VERSION }}-${uv_version}-${{
|
echo "key=uv-${{ env.UV_CACHE_VERSION }}-${uv_version}-${{
|
||||||
env.HA_SHORT_VERSION }}-$(date -u '+%Y-%m-%dT%H:%M:%s')" >> $GITHUB_OUTPUT
|
env.HA_SHORT_VERSION }}-$(date -u '+%Y-%m-%dT%H:%M:%s')" >> $GITHUB_OUTPUT
|
||||||
@ -525,7 +525,7 @@ jobs:
|
|||||||
python -m venv venv
|
python -m venv venv
|
||||||
. venv/bin/activate
|
. venv/bin/activate
|
||||||
python --version
|
python --version
|
||||||
pip install "$(grep '^uv' < requirements_test.txt)"
|
pip install "$(grep '^uv' < requirements.txt)"
|
||||||
uv pip install -U "pip>=21.3.1" setuptools wheel
|
uv pip install -U "pip>=21.3.1" setuptools wheel
|
||||||
uv pip install -r requirements.txt
|
uv pip install -r requirements.txt
|
||||||
python -m script.gen_requirements_all ci
|
python -m script.gen_requirements_all ci
|
||||||
|
2
.github/workflows/wheels.yml
vendored
2
.github/workflows/wheels.yml
vendored
@ -46,7 +46,7 @@ jobs:
|
|||||||
python -m venv venv
|
python -m venv venv
|
||||||
. venv/bin/activate
|
. venv/bin/activate
|
||||||
python --version
|
python --version
|
||||||
pip install "$(grep '^uv' < requirements_test.txt)"
|
pip install "$(grep '^uv' < requirements.txt)"
|
||||||
uv pip install -r requirements.txt
|
uv pip install -r requirements.txt
|
||||||
|
|
||||||
- name: Get information
|
- name: Get information
|
||||||
|
@ -83,7 +83,7 @@ repos:
|
|||||||
pass_filenames: false
|
pass_filenames: false
|
||||||
language: script
|
language: script
|
||||||
types: [text]
|
types: [text]
|
||||||
files: ^(homeassistant/.+/(icons|manifest|strings)\.json|homeassistant/brands/.*\.json|homeassistant/.+/services\.yaml|script/hassfest/(?!metadata|mypy_config).+\.py|requirements_test.txt)$
|
files: ^(homeassistant/.+/(icons|manifest|strings)\.json|homeassistant/brands/.*\.json|homeassistant/.+/services\.yaml|script/hassfest/(?!metadata|mypy_config).+\.py|requirements.+\.txt)$
|
||||||
- id: hassfest-metadata
|
- id: hassfest-metadata
|
||||||
name: hassfest-metadata
|
name: hassfest-metadata
|
||||||
entry: script/run-in-env.sh python3 -m script.hassfest -p metadata
|
entry: script/run-in-env.sh python3 -m script.hassfest -p metadata
|
||||||
|
@ -42,7 +42,6 @@ orjson==3.10.7
|
|||||||
packaging>=23.1
|
packaging>=23.1
|
||||||
paho-mqtt==1.6.1
|
paho-mqtt==1.6.1
|
||||||
Pillow==10.4.0
|
Pillow==10.4.0
|
||||||
pip>=21.3.1
|
|
||||||
psutil-home-assistant==0.0.1
|
psutil-home-assistant==0.0.1
|
||||||
PyJWT==2.9.0
|
PyJWT==2.9.0
|
||||||
pymicro-vad==1.0.1
|
pymicro-vad==1.0.1
|
||||||
@ -59,6 +58,7 @@ SQLAlchemy==2.0.31
|
|||||||
typing-extensions>=4.12.2,<5.0
|
typing-extensions>=4.12.2,<5.0
|
||||||
ulid-transform==1.0.2
|
ulid-transform==1.0.2
|
||||||
urllib3>=1.26.5,<2
|
urllib3>=1.26.5,<2
|
||||||
|
uv==0.4.8
|
||||||
voluptuous-openapi==0.0.5
|
voluptuous-openapi==0.0.5
|
||||||
voluptuous-serialize==2.6.0
|
voluptuous-serialize==2.6.0
|
||||||
voluptuous==0.15.2
|
voluptuous==0.15.2
|
||||||
|
@ -94,12 +94,11 @@ def install_package(
|
|||||||
|
|
||||||
Return boolean if install successful.
|
Return boolean if install successful.
|
||||||
"""
|
"""
|
||||||
# Not using 'import pip; pip.main([])' because it breaks the logger
|
|
||||||
_LOGGER.info("Attempting install of %s", package)
|
_LOGGER.info("Attempting install of %s", package)
|
||||||
env = os.environ.copy()
|
env = os.environ.copy()
|
||||||
args = [sys.executable, "-m", "pip", "install", "--quiet", package]
|
args = ["uv", "pip", "install", "--quiet", package]
|
||||||
if timeout:
|
if timeout:
|
||||||
args += ["--timeout", str(timeout)]
|
env["HTTP_TIMEOUT"] = str(timeout)
|
||||||
if upgrade:
|
if upgrade:
|
||||||
args.append("--upgrade")
|
args.append("--upgrade")
|
||||||
if constraints is not None:
|
if constraints is not None:
|
||||||
@ -109,7 +108,7 @@ def install_package(
|
|||||||
# This only works if not running in venv
|
# This only works if not running in venv
|
||||||
args += ["--user"]
|
args += ["--user"]
|
||||||
env["PYTHONUSERBASE"] = os.path.abspath(target)
|
env["PYTHONUSERBASE"] = os.path.abspath(target)
|
||||||
_LOGGER.debug("Running pip command: args=%s", args)
|
_LOGGER.debug("Running uv pip command: args=%s", args)
|
||||||
with Popen(
|
with Popen(
|
||||||
args,
|
args,
|
||||||
stdin=PIPE,
|
stdin=PIPE,
|
||||||
|
@ -54,7 +54,6 @@ dependencies = [
|
|||||||
"pyOpenSSL==24.2.1",
|
"pyOpenSSL==24.2.1",
|
||||||
"orjson==3.10.7",
|
"orjson==3.10.7",
|
||||||
"packaging>=23.1",
|
"packaging>=23.1",
|
||||||
"pip>=21.3.1",
|
|
||||||
"psutil-home-assistant==0.0.1",
|
"psutil-home-assistant==0.0.1",
|
||||||
"python-slugify==8.0.4",
|
"python-slugify==8.0.4",
|
||||||
"PyYAML==6.0.2",
|
"PyYAML==6.0.2",
|
||||||
@ -66,6 +65,7 @@ dependencies = [
|
|||||||
# Temporary setting an upper bound, to prevent compat issues with urllib3>=2
|
# Temporary setting an upper bound, to prevent compat issues with urllib3>=2
|
||||||
# https://github.com/home-assistant/core/issues/97248
|
# https://github.com/home-assistant/core/issues/97248
|
||||||
"urllib3>=1.26.5,<2",
|
"urllib3>=1.26.5,<2",
|
||||||
|
"uv==0.4.8",
|
||||||
"voluptuous==0.15.2",
|
"voluptuous==0.15.2",
|
||||||
"voluptuous-serialize==2.6.0",
|
"voluptuous-serialize==2.6.0",
|
||||||
"voluptuous-openapi==0.0.5",
|
"voluptuous-openapi==0.0.5",
|
||||||
|
@ -29,7 +29,6 @@ Pillow==10.4.0
|
|||||||
pyOpenSSL==24.2.1
|
pyOpenSSL==24.2.1
|
||||||
orjson==3.10.7
|
orjson==3.10.7
|
||||||
packaging>=23.1
|
packaging>=23.1
|
||||||
pip>=21.3.1
|
|
||||||
psutil-home-assistant==0.0.1
|
psutil-home-assistant==0.0.1
|
||||||
python-slugify==8.0.4
|
python-slugify==8.0.4
|
||||||
PyYAML==6.0.2
|
PyYAML==6.0.2
|
||||||
@ -38,6 +37,7 @@ SQLAlchemy==2.0.31
|
|||||||
typing-extensions>=4.12.2,<5.0
|
typing-extensions>=4.12.2,<5.0
|
||||||
ulid-transform==1.0.2
|
ulid-transform==1.0.2
|
||||||
urllib3>=1.26.5,<2
|
urllib3>=1.26.5,<2
|
||||||
|
uv==0.4.8
|
||||||
voluptuous==0.15.2
|
voluptuous==0.15.2
|
||||||
voluptuous-serialize==2.6.0
|
voluptuous-serialize==2.6.0
|
||||||
voluptuous-openapi==0.0.5
|
voluptuous-openapi==0.0.5
|
||||||
|
@ -51,4 +51,3 @@ types-pytz==2024.1.0.20240417
|
|||||||
types-PyYAML==6.0.12.20240311
|
types-PyYAML==6.0.12.20240311
|
||||||
types-requests==2.31.0.3
|
types-requests==2.31.0.3
|
||||||
types-xmltodict==0.13.0.3
|
types-xmltodict==0.13.0.3
|
||||||
uv==0.4.8
|
|
||||||
|
@ -172,8 +172,9 @@ def _generate_files(config: Config) -> list[File]:
|
|||||||
+ 10
|
+ 10
|
||||||
) * 1000
|
) * 1000
|
||||||
|
|
||||||
package_versions = _get_package_versions(
|
package_versions = _get_package_versions(Path("requirements.txt"), {"uv"})
|
||||||
Path("requirements_test.txt"), {"pipdeptree", "tqdm", "uv"}
|
package_versions |= _get_package_versions(
|
||||||
|
Path("requirements_test.txt"), {"pipdeptree", "tqdm"}
|
||||||
)
|
)
|
||||||
package_versions |= _get_package_versions(
|
package_versions |= _get_package_versions(
|
||||||
Path("requirements_test_pre_commit.txt"), {"ruff"}
|
Path("requirements_test_pre_commit.txt"), {"ruff"}
|
||||||
|
@ -1,12 +1,13 @@
|
|||||||
"""Test Home Assistant package util methods."""
|
"""Test Home Assistant package util methods."""
|
||||||
|
|
||||||
import asyncio
|
import asyncio
|
||||||
|
from collections.abc import Generator
|
||||||
from importlib.metadata import metadata
|
from importlib.metadata import metadata
|
||||||
import logging
|
import logging
|
||||||
import os
|
import os
|
||||||
from subprocess import PIPE
|
from subprocess import PIPE
|
||||||
import sys
|
import sys
|
||||||
from unittest.mock import MagicMock, call, patch
|
from unittest.mock import MagicMock, Mock, call, patch
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
@ -24,7 +25,7 @@ TEST_ZIP_REQ = "file://{}#{}".format(
|
|||||||
|
|
||||||
|
|
||||||
@pytest.fixture
|
@pytest.fixture
|
||||||
def mock_sys():
|
def mock_sys() -> Generator[MagicMock]:
|
||||||
"""Mock sys."""
|
"""Mock sys."""
|
||||||
with patch("homeassistant.util.package.sys", spec=object) as sys_mock:
|
with patch("homeassistant.util.package.sys", spec=object) as sys_mock:
|
||||||
sys_mock.executable = "python3"
|
sys_mock.executable = "python3"
|
||||||
@ -32,19 +33,19 @@ def mock_sys():
|
|||||||
|
|
||||||
|
|
||||||
@pytest.fixture
|
@pytest.fixture
|
||||||
def deps_dir():
|
def deps_dir() -> str:
|
||||||
"""Return path to deps directory."""
|
"""Return path to deps directory."""
|
||||||
return os.path.abspath("/deps_dir")
|
return os.path.abspath("/deps_dir")
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture
|
@pytest.fixture
|
||||||
def lib_dir(deps_dir):
|
def lib_dir(deps_dir) -> str:
|
||||||
"""Return path to lib directory."""
|
"""Return path to lib directory."""
|
||||||
return os.path.join(deps_dir, "lib_dir")
|
return os.path.join(deps_dir, "lib_dir")
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture
|
@pytest.fixture
|
||||||
def mock_popen(lib_dir):
|
def mock_popen(lib_dir) -> Generator[MagicMock]:
|
||||||
"""Return a Popen mock."""
|
"""Return a Popen mock."""
|
||||||
with patch("homeassistant.util.package.Popen") as popen_mock:
|
with patch("homeassistant.util.package.Popen") as popen_mock:
|
||||||
popen_mock.return_value.__enter__ = popen_mock
|
popen_mock.return_value.__enter__ = popen_mock
|
||||||
@ -57,7 +58,7 @@ def mock_popen(lib_dir):
|
|||||||
|
|
||||||
|
|
||||||
@pytest.fixture
|
@pytest.fixture
|
||||||
def mock_env_copy():
|
def mock_env_copy() -> Generator[Mock]:
|
||||||
"""Mock os.environ.copy."""
|
"""Mock os.environ.copy."""
|
||||||
with patch("homeassistant.util.package.os.environ.copy") as env_copy:
|
with patch("homeassistant.util.package.os.environ.copy") as env_copy:
|
||||||
env_copy.return_value = {}
|
env_copy.return_value = {}
|
||||||
@ -65,14 +66,14 @@ def mock_env_copy():
|
|||||||
|
|
||||||
|
|
||||||
@pytest.fixture
|
@pytest.fixture
|
||||||
def mock_venv():
|
def mock_venv() -> Generator[MagicMock]:
|
||||||
"""Mock homeassistant.util.package.is_virtual_env."""
|
"""Mock homeassistant.util.package.is_virtual_env."""
|
||||||
with patch("homeassistant.util.package.is_virtual_env") as mock:
|
with patch("homeassistant.util.package.is_virtual_env") as mock:
|
||||||
mock.return_value = True
|
mock.return_value = True
|
||||||
yield mock
|
yield mock
|
||||||
|
|
||||||
|
|
||||||
def mock_async_subprocess():
|
def mock_async_subprocess() -> Generator[MagicMock]:
|
||||||
"""Return an async Popen mock."""
|
"""Return an async Popen mock."""
|
||||||
async_popen = MagicMock()
|
async_popen = MagicMock()
|
||||||
|
|
||||||
@ -85,13 +86,14 @@ def mock_async_subprocess():
|
|||||||
return async_popen
|
return async_popen
|
||||||
|
|
||||||
|
|
||||||
def test_install(mock_sys, mock_popen, mock_env_copy, mock_venv) -> None:
|
@pytest.mark.usefixtures("mock_sys", "mock_venv")
|
||||||
|
def test_install(mock_popen: MagicMock, mock_env_copy: MagicMock) -> None:
|
||||||
"""Test an install attempt on a package that doesn't exist."""
|
"""Test an install attempt on a package that doesn't exist."""
|
||||||
env = mock_env_copy()
|
env = mock_env_copy()
|
||||||
assert package.install_package(TEST_NEW_REQ, False)
|
assert package.install_package(TEST_NEW_REQ, False)
|
||||||
assert mock_popen.call_count == 2
|
assert mock_popen.call_count == 2
|
||||||
assert mock_popen.mock_calls[0] == call(
|
assert mock_popen.mock_calls[0] == call(
|
||||||
[mock_sys.executable, "-m", "pip", "install", "--quiet", TEST_NEW_REQ],
|
["uv", "pip", "install", "--quiet", TEST_NEW_REQ],
|
||||||
stdin=PIPE,
|
stdin=PIPE,
|
||||||
stdout=PIPE,
|
stdout=PIPE,
|
||||||
stderr=PIPE,
|
stderr=PIPE,
|
||||||
@ -101,15 +103,33 @@ def test_install(mock_sys, mock_popen, mock_env_copy, mock_venv) -> None:
|
|||||||
assert mock_popen.return_value.communicate.call_count == 1
|
assert mock_popen.return_value.communicate.call_count == 1
|
||||||
|
|
||||||
|
|
||||||
def test_install_upgrade(mock_sys, mock_popen, mock_env_copy, mock_venv) -> None:
|
@pytest.mark.usefixtures("mock_sys", "mock_venv")
|
||||||
|
def test_install_with_timeout(mock_popen: MagicMock, mock_env_copy: MagicMock) -> None:
|
||||||
|
"""Test an install attempt on a package that doesn't exist with a timeout set."""
|
||||||
|
env = mock_env_copy()
|
||||||
|
assert package.install_package(TEST_NEW_REQ, False, timeout=10)
|
||||||
|
assert mock_popen.call_count == 2
|
||||||
|
env["HTTP_TIMEOUT"] = "10"
|
||||||
|
assert mock_popen.mock_calls[0] == call(
|
||||||
|
["uv", "pip", "install", "--quiet", TEST_NEW_REQ],
|
||||||
|
stdin=PIPE,
|
||||||
|
stdout=PIPE,
|
||||||
|
stderr=PIPE,
|
||||||
|
env=env,
|
||||||
|
close_fds=False,
|
||||||
|
)
|
||||||
|
assert mock_popen.return_value.communicate.call_count == 1
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.usefixtures("mock_sys", "mock_venv")
|
||||||
|
def test_install_upgrade(mock_popen, mock_env_copy) -> None:
|
||||||
"""Test an upgrade attempt on a package."""
|
"""Test an upgrade attempt on a package."""
|
||||||
env = mock_env_copy()
|
env = mock_env_copy()
|
||||||
assert package.install_package(TEST_NEW_REQ)
|
assert package.install_package(TEST_NEW_REQ)
|
||||||
assert mock_popen.call_count == 2
|
assert mock_popen.call_count == 2
|
||||||
assert mock_popen.mock_calls[0] == call(
|
assert mock_popen.mock_calls[0] == call(
|
||||||
[
|
[
|
||||||
mock_sys.executable,
|
"uv",
|
||||||
"-m",
|
|
||||||
"pip",
|
"pip",
|
||||||
"install",
|
"install",
|
||||||
"--quiet",
|
"--quiet",
|
||||||
@ -133,8 +153,7 @@ def test_install_target(mock_sys, mock_popen, mock_env_copy, mock_venv) -> None:
|
|||||||
mock_venv.return_value = False
|
mock_venv.return_value = False
|
||||||
mock_sys.platform = "linux"
|
mock_sys.platform = "linux"
|
||||||
args = [
|
args = [
|
||||||
mock_sys.executable,
|
"uv",
|
||||||
"-m",
|
|
||||||
"pip",
|
"pip",
|
||||||
"install",
|
"install",
|
||||||
"--quiet",
|
"--quiet",
|
||||||
@ -150,16 +169,16 @@ def test_install_target(mock_sys, mock_popen, mock_env_copy, mock_venv) -> None:
|
|||||||
assert mock_popen.return_value.communicate.call_count == 1
|
assert mock_popen.return_value.communicate.call_count == 1
|
||||||
|
|
||||||
|
|
||||||
def test_install_target_venv(mock_sys, mock_popen, mock_env_copy, mock_venv) -> None:
|
@pytest.mark.usefixtures("mock_sys", "mock_popen", "mock_env_copy", "mock_venv")
|
||||||
|
def test_install_target_venv() -> None:
|
||||||
"""Test an install with a target in a virtual environment."""
|
"""Test an install with a target in a virtual environment."""
|
||||||
target = "target_folder"
|
target = "target_folder"
|
||||||
with pytest.raises(AssertionError):
|
with pytest.raises(AssertionError):
|
||||||
package.install_package(TEST_NEW_REQ, False, target=target)
|
package.install_package(TEST_NEW_REQ, False, target=target)
|
||||||
|
|
||||||
|
|
||||||
def test_install_error(
|
@pytest.mark.usefixtures("mock_sys", "mock_venv")
|
||||||
caplog: pytest.LogCaptureFixture, mock_sys, mock_popen, mock_venv
|
def test_install_error(caplog: pytest.LogCaptureFixture, mock_popen) -> None:
|
||||||
) -> None:
|
|
||||||
"""Test an install that errors out."""
|
"""Test an install that errors out."""
|
||||||
caplog.set_level(logging.WARNING)
|
caplog.set_level(logging.WARNING)
|
||||||
mock_popen.return_value.returncode = 1
|
mock_popen.return_value.returncode = 1
|
||||||
@ -169,7 +188,8 @@ def test_install_error(
|
|||||||
assert record.levelname == "ERROR"
|
assert record.levelname == "ERROR"
|
||||||
|
|
||||||
|
|
||||||
def test_install_constraint(mock_sys, mock_popen, mock_env_copy, mock_venv) -> None:
|
@pytest.mark.usefixtures("mock_sys", "mock_venv")
|
||||||
|
def test_install_constraint(mock_popen, mock_env_copy) -> None:
|
||||||
"""Test install with constraint file on not installed package."""
|
"""Test install with constraint file on not installed package."""
|
||||||
env = mock_env_copy()
|
env = mock_env_copy()
|
||||||
constraints = "constraints_file.txt"
|
constraints = "constraints_file.txt"
|
||||||
@ -177,8 +197,7 @@ def test_install_constraint(mock_sys, mock_popen, mock_env_copy, mock_venv) -> N
|
|||||||
assert mock_popen.call_count == 2
|
assert mock_popen.call_count == 2
|
||||||
assert mock_popen.mock_calls[0] == call(
|
assert mock_popen.mock_calls[0] == call(
|
||||||
[
|
[
|
||||||
mock_sys.executable,
|
"uv",
|
||||||
"-m",
|
|
||||||
"pip",
|
"pip",
|
||||||
"install",
|
"install",
|
||||||
"--quiet",
|
"--quiet",
|
||||||
|
Loading…
x
Reference in New Issue
Block a user