mirror of
https://github.com/home-assistant/core.git
synced 2025-08-09 13:38:19 +00:00
Add hassfest check to help with future dependency updates (#149624)
This commit is contained in:
parent
fe2bd8d09e
commit
f350a1a1fa
@ -43,6 +43,13 @@ PACKAGE_CHECK_VERSION_RANGE = {
|
|||||||
"urllib3": "SemVer",
|
"urllib3": "SemVer",
|
||||||
"yarl": "SemVer",
|
"yarl": "SemVer",
|
||||||
}
|
}
|
||||||
|
PACKAGE_CHECK_PREPARE_UPDATE: dict[str, int] = {
|
||||||
|
# In the form dict("dependencyX": n+1)
|
||||||
|
# - dependencyX should be the name of the referenced dependency
|
||||||
|
# - current major version +1
|
||||||
|
# Pandas will only fully support Python 3.14 in v3.
|
||||||
|
"pandas": 3,
|
||||||
|
}
|
||||||
PACKAGE_CHECK_VERSION_RANGE_EXCEPTIONS: dict[str, dict[str, set[str]]] = {
|
PACKAGE_CHECK_VERSION_RANGE_EXCEPTIONS: dict[str, dict[str, set[str]]] = {
|
||||||
# In the form dict("domain": {"package": {"dependency1", "dependency2"}})
|
# In the form dict("domain": {"package": {"dependency1", "dependency2"}})
|
||||||
# - domain is the integration domain
|
# - domain is the integration domain
|
||||||
@ -53,6 +60,10 @@ PACKAGE_CHECK_VERSION_RANGE_EXCEPTIONS: dict[str, dict[str, set[str]]] = {
|
|||||||
# geocachingapi > reverse_geocode > scipy > numpy
|
# geocachingapi > reverse_geocode > scipy > numpy
|
||||||
"scipy": {"numpy"}
|
"scipy": {"numpy"}
|
||||||
},
|
},
|
||||||
|
"noaa_tides": {
|
||||||
|
# https://github.com/GClunies/noaa_coops/pull/69
|
||||||
|
"noaa-coops": {"pandas"}
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
PACKAGE_REGEX = re.compile(
|
PACKAGE_REGEX = re.compile(
|
||||||
@ -568,7 +579,7 @@ def check_dependency_version_range(
|
|||||||
version == "Any"
|
version == "Any"
|
||||||
or (convention := PACKAGE_CHECK_VERSION_RANGE.get(pkg)) is None
|
or (convention := PACKAGE_CHECK_VERSION_RANGE.get(pkg)) is None
|
||||||
or all(
|
or all(
|
||||||
_is_dependency_version_range_valid(version_part, convention)
|
_is_dependency_version_range_valid(version_part, convention, pkg)
|
||||||
for version_part in version.split(";", 1)[0].split(",")
|
for version_part in version.split(";", 1)[0].split(",")
|
||||||
)
|
)
|
||||||
):
|
):
|
||||||
@ -582,22 +593,35 @@ def check_dependency_version_range(
|
|||||||
return False
|
return False
|
||||||
|
|
||||||
|
|
||||||
def _is_dependency_version_range_valid(version_part: str, convention: str) -> bool:
|
def _is_dependency_version_range_valid(
|
||||||
|
version_part: str, convention: str, pkg: str | None = None
|
||||||
|
) -> bool:
|
||||||
|
prepare_update = PACKAGE_CHECK_PREPARE_UPDATE.get(pkg) if pkg else None
|
||||||
version_match = PIP_VERSION_RANGE_SEPARATOR.match(version_part.strip())
|
version_match = PIP_VERSION_RANGE_SEPARATOR.match(version_part.strip())
|
||||||
operator = version_match.group(1)
|
operator = version_match.group(1)
|
||||||
version = version_match.group(2)
|
version = version_match.group(2)
|
||||||
|
awesome = AwesomeVersion(version)
|
||||||
|
|
||||||
if operator in (">", ">=", "!="):
|
if operator in (">", ">=", "!="):
|
||||||
# Lower version binding and version exclusion are fine
|
# Lower version binding and version exclusion are fine
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
if prepare_update is not None:
|
||||||
|
if operator in ("==", "~="):
|
||||||
|
# Only current major version allowed which prevents updates to the next one
|
||||||
|
return False
|
||||||
|
# Allow upper constraints for major version + 1
|
||||||
|
if operator == "<" and awesome.section(0) < prepare_update + 1:
|
||||||
|
return False
|
||||||
|
if operator == "<=" and awesome.section(0) < prepare_update:
|
||||||
|
return False
|
||||||
|
|
||||||
if convention == "SemVer":
|
if convention == "SemVer":
|
||||||
if operator == "==":
|
if operator == "==":
|
||||||
# Explicit version with wildcard is allowed only on major version
|
# Explicit version with wildcard is allowed only on major version
|
||||||
# e.g. ==1.* is allowed, but ==1.2.* is not
|
# e.g. ==1.* is allowed, but ==1.2.* is not
|
||||||
return version.endswith(".*") and version.count(".") == 1
|
return version.endswith(".*") and version.count(".") == 1
|
||||||
|
|
||||||
awesome = AwesomeVersion(version)
|
|
||||||
if operator in ("<", "<="):
|
if operator in ("<", "<="):
|
||||||
# Upper version binding only allowed on major version
|
# Upper version binding only allowed on major version
|
||||||
# e.g. <=3 is allowed, but <=3.1 is not
|
# e.g. <=3 is allowed, but <=3.1 is not
|
||||||
|
@ -1,11 +1,17 @@
|
|||||||
"""Tests for hassfest requirements."""
|
"""Tests for hassfest requirements."""
|
||||||
|
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
from unittest.mock import patch
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from script.hassfest.model import Config, Integration
|
from script.hassfest.model import Config, Integration
|
||||||
from script.hassfest.requirements import validate_requirements_format
|
from script.hassfest.requirements import (
|
||||||
|
PACKAGE_CHECK_PREPARE_UPDATE,
|
||||||
|
PACKAGE_CHECK_VERSION_RANGE,
|
||||||
|
check_dependency_version_range,
|
||||||
|
validate_requirements_format,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture
|
@pytest.fixture
|
||||||
@ -105,3 +111,41 @@ def test_validate_requirements_format_github_custom(integration: Integration) ->
|
|||||||
integration.path = Path("")
|
integration.path = Path("")
|
||||||
assert validate_requirements_format(integration)
|
assert validate_requirements_format(integration)
|
||||||
assert len(integration.errors) == 0
|
assert len(integration.errors) == 0
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize(
|
||||||
|
("version", "result"),
|
||||||
|
[
|
||||||
|
(">2", True),
|
||||||
|
(">=2.0", True),
|
||||||
|
(">=2.0,<4", True),
|
||||||
|
("<4", True),
|
||||||
|
("<=3.0", True),
|
||||||
|
(">=2.0,<4;python_version<'3.14'", True),
|
||||||
|
("<3", False),
|
||||||
|
("==2.*", False),
|
||||||
|
("~=2.0", False),
|
||||||
|
("<=2.100", False),
|
||||||
|
(">2,<3", False),
|
||||||
|
(">=2.0,<3", False),
|
||||||
|
(">=2.0,<3;python_version<'3.14'", False),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
def test_dependency_version_range_prepare_update(
|
||||||
|
version: str, result: bool, integration: Integration
|
||||||
|
) -> None:
|
||||||
|
"""Test dependency version range check for prepare update is working correctly."""
|
||||||
|
with (
|
||||||
|
patch.dict(PACKAGE_CHECK_VERSION_RANGE, {"numpy-test": "SemVer"}, clear=True),
|
||||||
|
patch.dict(PACKAGE_CHECK_PREPARE_UPDATE, {"numpy-test": 3}, clear=True),
|
||||||
|
):
|
||||||
|
assert (
|
||||||
|
check_dependency_version_range(
|
||||||
|
integration,
|
||||||
|
"test",
|
||||||
|
pkg="numpy-test",
|
||||||
|
version=version,
|
||||||
|
package_exceptions=set(),
|
||||||
|
)
|
||||||
|
== result
|
||||||
|
)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user