Ensure startup can proceed when there is package metadata cruft (#47706)
If a package fails to install or partially installed importlib version can return None. We now try pkg_resources first, then try importlib, and handle the case where version unexpectedly returns None
This commit is contained in:
parent
00bd591238
commit
9a686d148e
2 changed files with 57 additions and 2 deletions
|
@ -34,6 +34,9 @@ def is_installed(package: str) -> bool:
|
|||
Returns False when the package is not installed or doesn't meet req.
|
||||
"""
|
||||
try:
|
||||
pkg_resources.get_distribution(package)
|
||||
return True
|
||||
except (pkg_resources.ResolutionError, pkg_resources.ExtractionError):
|
||||
req = pkg_resources.Requirement.parse(package)
|
||||
except ValueError:
|
||||
# This is a zip file. We no longer use this in Home Assistant,
|
||||
|
@ -41,7 +44,14 @@ def is_installed(package: str) -> bool:
|
|||
req = pkg_resources.Requirement.parse(urlparse(package).fragment)
|
||||
|
||||
try:
|
||||
return version(req.project_name) in req
|
||||
installed_version = version(req.project_name)
|
||||
# This will happen when an install failed or
|
||||
# was aborted while in progress see
|
||||
# https://github.com/home-assistant/core/issues/47699
|
||||
if installed_version is None:
|
||||
_LOGGER.error("Installed version for %s resolved to None", req.project_name) # type: ignore
|
||||
return False
|
||||
return installed_version in req
|
||||
except PackageNotFoundError:
|
||||
return False
|
||||
|
||||
|
|
|
@ -239,10 +239,55 @@ async def test_async_get_user_site(mock_env_copy):
|
|||
|
||||
def test_check_package_global():
|
||||
"""Test for an installed package."""
|
||||
installed_package = list(pkg_resources.working_set)[0].project_name
|
||||
first_package = list(pkg_resources.working_set)[0]
|
||||
installed_package = first_package.project_name
|
||||
installed_version = first_package.version
|
||||
|
||||
assert package.is_installed(installed_package)
|
||||
assert package.is_installed(f"{installed_package}=={installed_version}")
|
||||
assert package.is_installed(f"{installed_package}>={installed_version}")
|
||||
assert package.is_installed(f"{installed_package}<={installed_version}")
|
||||
assert not package.is_installed(f"{installed_package}<{installed_version}")
|
||||
|
||||
|
||||
def test_check_package_version_does_not_match():
|
||||
"""Test for version mismatch."""
|
||||
installed_package = list(pkg_resources.working_set)[0].project_name
|
||||
assert not package.is_installed(f"{installed_package}==999.999.999")
|
||||
assert not package.is_installed(f"{installed_package}>=999.999.999")
|
||||
|
||||
|
||||
def test_check_package_zip():
|
||||
"""Test for an installed zip package."""
|
||||
assert not package.is_installed(TEST_ZIP_REQ)
|
||||
|
||||
|
||||
def test_get_distribution_falls_back_to_version():
|
||||
"""Test for get_distribution failing and fallback to version."""
|
||||
first_package = list(pkg_resources.working_set)[0]
|
||||
installed_package = first_package.project_name
|
||||
installed_version = first_package.version
|
||||
|
||||
with patch(
|
||||
"homeassistant.util.package.pkg_resources.get_distribution",
|
||||
side_effect=pkg_resources.ExtractionError,
|
||||
):
|
||||
assert package.is_installed(installed_package)
|
||||
assert package.is_installed(f"{installed_package}=={installed_version}")
|
||||
assert package.is_installed(f"{installed_package}>={installed_version}")
|
||||
assert package.is_installed(f"{installed_package}<={installed_version}")
|
||||
assert not package.is_installed(f"{installed_package}<{installed_version}")
|
||||
|
||||
|
||||
def test_check_package_previous_failed_install():
|
||||
"""Test for when a previously install package failed and left cruft behind."""
|
||||
first_package = list(pkg_resources.working_set)[0]
|
||||
installed_package = first_package.project_name
|
||||
installed_version = first_package.version
|
||||
|
||||
with patch(
|
||||
"homeassistant.util.package.pkg_resources.get_distribution",
|
||||
side_effect=pkg_resources.ExtractionError,
|
||||
), patch("homeassistant.util.package.version", return_value=None):
|
||||
assert not package.is_installed(installed_package)
|
||||
assert not package.is_installed(f"{installed_package}=={installed_version}")
|
||||
|
|
Loading…
Add table
Reference in a new issue