mirror of
https://github.com/home-assistant/core.git
synced 2025-04-25 01:38:02 +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:
|
||||
UV_PRERELEASE: allow
|
||||
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 .
|
||||
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
|
||||
. venv/bin/activate
|
||||
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)"
|
||||
- name: Restore pre-commit environment from cache
|
||||
id: cache-precommit
|
||||
@ -476,7 +476,7 @@ jobs:
|
||||
- name: Generate partial uv restore key
|
||||
id: generate-uv-key
|
||||
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 "key=uv-${{ env.UV_CACHE_VERSION }}-${uv_version}-${{
|
||||
env.HA_SHORT_VERSION }}-$(date -u '+%Y-%m-%dT%H:%M:%s')" >> $GITHUB_OUTPUT
|
||||
@ -525,7 +525,7 @@ jobs:
|
||||
python -m venv venv
|
||||
. venv/bin/activate
|
||||
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 -r requirements.txt
|
||||
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
|
||||
. venv/bin/activate
|
||||
python --version
|
||||
pip install "$(grep '^uv' < requirements_test.txt)"
|
||||
pip install "$(grep '^uv' < requirements.txt)"
|
||||
uv pip install -r requirements.txt
|
||||
|
||||
- name: Get information
|
||||
|
@ -83,7 +83,7 @@ repos:
|
||||
pass_filenames: false
|
||||
language: script
|
||||
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
|
||||
name: hassfest-metadata
|
||||
entry: script/run-in-env.sh python3 -m script.hassfest -p metadata
|
||||
|
@ -42,7 +42,6 @@ orjson==3.10.7
|
||||
packaging>=23.1
|
||||
paho-mqtt==1.6.1
|
||||
Pillow==10.4.0
|
||||
pip>=21.3.1
|
||||
psutil-home-assistant==0.0.1
|
||||
PyJWT==2.9.0
|
||||
pymicro-vad==1.0.1
|
||||
@ -59,6 +58,7 @@ SQLAlchemy==2.0.31
|
||||
typing-extensions>=4.12.2,<5.0
|
||||
ulid-transform==1.0.2
|
||||
urllib3>=1.26.5,<2
|
||||
uv==0.4.8
|
||||
voluptuous-openapi==0.0.5
|
||||
voluptuous-serialize==2.6.0
|
||||
voluptuous==0.15.2
|
||||
|
@ -94,12 +94,11 @@ def install_package(
|
||||
|
||||
Return boolean if install successful.
|
||||
"""
|
||||
# Not using 'import pip; pip.main([])' because it breaks the logger
|
||||
_LOGGER.info("Attempting install of %s", package)
|
||||
env = os.environ.copy()
|
||||
args = [sys.executable, "-m", "pip", "install", "--quiet", package]
|
||||
args = ["uv", "pip", "install", "--quiet", package]
|
||||
if timeout:
|
||||
args += ["--timeout", str(timeout)]
|
||||
env["HTTP_TIMEOUT"] = str(timeout)
|
||||
if upgrade:
|
||||
args.append("--upgrade")
|
||||
if constraints is not None:
|
||||
@ -109,7 +108,7 @@ def install_package(
|
||||
# This only works if not running in venv
|
||||
args += ["--user"]
|
||||
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(
|
||||
args,
|
||||
stdin=PIPE,
|
||||
|
@ -54,7 +54,6 @@ dependencies = [
|
||||
"pyOpenSSL==24.2.1",
|
||||
"orjson==3.10.7",
|
||||
"packaging>=23.1",
|
||||
"pip>=21.3.1",
|
||||
"psutil-home-assistant==0.0.1",
|
||||
"python-slugify==8.0.4",
|
||||
"PyYAML==6.0.2",
|
||||
@ -66,6 +65,7 @@ dependencies = [
|
||||
# Temporary setting an upper bound, to prevent compat issues with urllib3>=2
|
||||
# https://github.com/home-assistant/core/issues/97248
|
||||
"urllib3>=1.26.5,<2",
|
||||
"uv==0.4.8",
|
||||
"voluptuous==0.15.2",
|
||||
"voluptuous-serialize==2.6.0",
|
||||
"voluptuous-openapi==0.0.5",
|
||||
|
@ -29,7 +29,6 @@ Pillow==10.4.0
|
||||
pyOpenSSL==24.2.1
|
||||
orjson==3.10.7
|
||||
packaging>=23.1
|
||||
pip>=21.3.1
|
||||
psutil-home-assistant==0.0.1
|
||||
python-slugify==8.0.4
|
||||
PyYAML==6.0.2
|
||||
@ -38,6 +37,7 @@ SQLAlchemy==2.0.31
|
||||
typing-extensions>=4.12.2,<5.0
|
||||
ulid-transform==1.0.2
|
||||
urllib3>=1.26.5,<2
|
||||
uv==0.4.8
|
||||
voluptuous==0.15.2
|
||||
voluptuous-serialize==2.6.0
|
||||
voluptuous-openapi==0.0.5
|
||||
|
@ -51,4 +51,3 @@ types-pytz==2024.1.0.20240417
|
||||
types-PyYAML==6.0.12.20240311
|
||||
types-requests==2.31.0.3
|
||||
types-xmltodict==0.13.0.3
|
||||
uv==0.4.8
|
||||
|
@ -172,8 +172,9 @@ def _generate_files(config: Config) -> list[File]:
|
||||
+ 10
|
||||
) * 1000
|
||||
|
||||
package_versions = _get_package_versions(
|
||||
Path("requirements_test.txt"), {"pipdeptree", "tqdm", "uv"}
|
||||
package_versions = _get_package_versions(Path("requirements.txt"), {"uv"})
|
||||
package_versions |= _get_package_versions(
|
||||
Path("requirements_test.txt"), {"pipdeptree", "tqdm"}
|
||||
)
|
||||
package_versions |= _get_package_versions(
|
||||
Path("requirements_test_pre_commit.txt"), {"ruff"}
|
||||
|
@ -1,12 +1,13 @@
|
||||
"""Test Home Assistant package util methods."""
|
||||
|
||||
import asyncio
|
||||
from collections.abc import Generator
|
||||
from importlib.metadata import metadata
|
||||
import logging
|
||||
import os
|
||||
from subprocess import PIPE
|
||||
import sys
|
||||
from unittest.mock import MagicMock, call, patch
|
||||
from unittest.mock import MagicMock, Mock, call, patch
|
||||
|
||||
import pytest
|
||||
|
||||
@ -24,7 +25,7 @@ TEST_ZIP_REQ = "file://{}#{}".format(
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def mock_sys():
|
||||
def mock_sys() -> Generator[MagicMock]:
|
||||
"""Mock sys."""
|
||||
with patch("homeassistant.util.package.sys", spec=object) as sys_mock:
|
||||
sys_mock.executable = "python3"
|
||||
@ -32,19 +33,19 @@ def mock_sys():
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def deps_dir():
|
||||
def deps_dir() -> str:
|
||||
"""Return path to deps directory."""
|
||||
return os.path.abspath("/deps_dir")
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def lib_dir(deps_dir):
|
||||
def lib_dir(deps_dir) -> str:
|
||||
"""Return path to lib directory."""
|
||||
return os.path.join(deps_dir, "lib_dir")
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def mock_popen(lib_dir):
|
||||
def mock_popen(lib_dir) -> Generator[MagicMock]:
|
||||
"""Return a Popen mock."""
|
||||
with patch("homeassistant.util.package.Popen") as popen_mock:
|
||||
popen_mock.return_value.__enter__ = popen_mock
|
||||
@ -57,7 +58,7 @@ def mock_popen(lib_dir):
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def mock_env_copy():
|
||||
def mock_env_copy() -> Generator[Mock]:
|
||||
"""Mock os.environ.copy."""
|
||||
with patch("homeassistant.util.package.os.environ.copy") as env_copy:
|
||||
env_copy.return_value = {}
|
||||
@ -65,14 +66,14 @@ def mock_env_copy():
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def mock_venv():
|
||||
def mock_venv() -> Generator[MagicMock]:
|
||||
"""Mock homeassistant.util.package.is_virtual_env."""
|
||||
with patch("homeassistant.util.package.is_virtual_env") as mock:
|
||||
mock.return_value = True
|
||||
yield mock
|
||||
|
||||
|
||||
def mock_async_subprocess():
|
||||
def mock_async_subprocess() -> Generator[MagicMock]:
|
||||
"""Return an async Popen mock."""
|
||||
async_popen = MagicMock()
|
||||
|
||||
@ -85,13 +86,14 @@ def mock_async_subprocess():
|
||||
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."""
|
||||
env = mock_env_copy()
|
||||
assert package.install_package(TEST_NEW_REQ, False)
|
||||
assert mock_popen.call_count == 2
|
||||
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,
|
||||
stdout=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
|
||||
|
||||
|
||||
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."""
|
||||
env = mock_env_copy()
|
||||
assert package.install_package(TEST_NEW_REQ)
|
||||
assert mock_popen.call_count == 2
|
||||
assert mock_popen.mock_calls[0] == call(
|
||||
[
|
||||
mock_sys.executable,
|
||||
"-m",
|
||||
"uv",
|
||||
"pip",
|
||||
"install",
|
||||
"--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_sys.platform = "linux"
|
||||
args = [
|
||||
mock_sys.executable,
|
||||
"-m",
|
||||
"uv",
|
||||
"pip",
|
||||
"install",
|
||||
"--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
|
||||
|
||||
|
||||
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."""
|
||||
target = "target_folder"
|
||||
with pytest.raises(AssertionError):
|
||||
package.install_package(TEST_NEW_REQ, False, target=target)
|
||||
|
||||
|
||||
def test_install_error(
|
||||
caplog: pytest.LogCaptureFixture, mock_sys, mock_popen, mock_venv
|
||||
) -> None:
|
||||
@pytest.mark.usefixtures("mock_sys", "mock_venv")
|
||||
def test_install_error(caplog: pytest.LogCaptureFixture, mock_popen) -> None:
|
||||
"""Test an install that errors out."""
|
||||
caplog.set_level(logging.WARNING)
|
||||
mock_popen.return_value.returncode = 1
|
||||
@ -169,7 +188,8 @@ def test_install_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."""
|
||||
env = mock_env_copy()
|
||||
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.mock_calls[0] == call(
|
||||
[
|
||||
mock_sys.executable,
|
||||
"-m",
|
||||
"uv",
|
||||
"pip",
|
||||
"install",
|
||||
"--quiet",
|
||||
|
Loading…
x
Reference in New Issue
Block a user