Restore support for packages being installed from urls with fragments (#109267)
parent
5d3364521f
commit
8afcd53af6
|
@ -9,6 +9,7 @@ import os
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from subprocess import PIPE, Popen
|
from subprocess import PIPE, Popen
|
||||||
import sys
|
import sys
|
||||||
|
from urllib.parse import urlparse
|
||||||
|
|
||||||
from packaging.requirements import InvalidRequirement, Requirement
|
from packaging.requirements import InvalidRequirement, Requirement
|
||||||
|
|
||||||
|
@ -40,14 +41,32 @@ def is_installed(requirement_str: str) -> bool:
|
||||||
expected input is a pip compatible package specifier (requirement string)
|
expected input is a pip compatible package specifier (requirement string)
|
||||||
e.g. "package==1.0.0" or "package>=1.0.0,<2.0.0"
|
e.g. "package==1.0.0" or "package>=1.0.0,<2.0.0"
|
||||||
|
|
||||||
|
For backward compatibility, it also accepts a URL with a fragment
|
||||||
|
e.g. "git+https://github.com/pypa/pip#pip>=1"
|
||||||
|
|
||||||
Returns True when the requirement is met.
|
Returns True when the requirement is met.
|
||||||
Returns False when the package is not installed or doesn't meet req.
|
Returns False when the package is not installed or doesn't meet req.
|
||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
req = Requirement(requirement_str)
|
req = Requirement(requirement_str)
|
||||||
except InvalidRequirement:
|
except InvalidRequirement:
|
||||||
_LOGGER.error("Invalid requirement '%s'", requirement_str)
|
if "#" not in requirement_str:
|
||||||
return False
|
_LOGGER.error("Invalid requirement '%s'", requirement_str)
|
||||||
|
return False
|
||||||
|
|
||||||
|
# This is likely a URL with a fragment
|
||||||
|
# example: git+https://github.com/pypa/pip#pip>=1
|
||||||
|
|
||||||
|
# fragment support was originally used to install zip files, and
|
||||||
|
# we no longer do this in Home Assistant. However, custom
|
||||||
|
# components started using it to install packages from git
|
||||||
|
# urls which would make it would be a breaking change to
|
||||||
|
# remove it.
|
||||||
|
try:
|
||||||
|
req = Requirement(urlparse(requirement_str).fragment)
|
||||||
|
except InvalidRequirement:
|
||||||
|
_LOGGER.error("Invalid requirement '%s'", requirement_str)
|
||||||
|
return False
|
||||||
|
|
||||||
try:
|
try:
|
||||||
if (installed_version := version(req.name)) is None:
|
if (installed_version := version(req.name)) is None:
|
||||||
|
|
|
@ -217,7 +217,7 @@ async def test_async_get_user_site(mock_env_copy) -> None:
|
||||||
assert ret == os.path.join(deps_dir, "lib_dir")
|
assert ret == os.path.join(deps_dir, "lib_dir")
|
||||||
|
|
||||||
|
|
||||||
def test_check_package_global() -> None:
|
def test_check_package_global(caplog: pytest.LogCaptureFixture) -> None:
|
||||||
"""Test for an installed package."""
|
"""Test for an installed package."""
|
||||||
pkg = metadata("homeassistant")
|
pkg = metadata("homeassistant")
|
||||||
installed_package = pkg["name"]
|
installed_package = pkg["name"]
|
||||||
|
@ -229,10 +229,19 @@ def test_check_package_global() -> None:
|
||||||
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}")
|
assert not package.is_installed(f"{installed_package}<{installed_version}")
|
||||||
|
|
||||||
|
assert package.is_installed("-1 invalid_package") is False
|
||||||
|
assert "Invalid requirement '-1 invalid_package'" in caplog.text
|
||||||
|
|
||||||
def test_check_package_zip() -> None:
|
|
||||||
"""Test for an installed zip package."""
|
def test_check_package_fragment(caplog: pytest.LogCaptureFixture) -> None:
|
||||||
|
"""Test for an installed package with a fragment."""
|
||||||
assert not package.is_installed(TEST_ZIP_REQ)
|
assert not package.is_installed(TEST_ZIP_REQ)
|
||||||
|
assert package.is_installed("git+https://github.com/pypa/pip#pip>=1")
|
||||||
|
assert not package.is_installed("git+https://github.com/pypa/pip#-1 invalid")
|
||||||
|
assert (
|
||||||
|
"Invalid requirement 'git+https://github.com/pypa/pip#-1 invalid'"
|
||||||
|
in caplog.text
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def test_get_is_installed() -> None:
|
def test_get_is_installed() -> None:
|
||||||
|
|
Loading…
Reference in New Issue