mkvenv: always pass locally-installed packages to pip
Let pip decide whether a new version should be installed or the current one is okay. This ensures that the virtual environment is updated (either upgraded or downgraded) whenever a new version of a package is requested. The hardest part here is figuring out if a package is installed in the venv (which also has to be done twice to account for the presence of either setuptools in Python <3.8, or importlib in Python >=3.8). Suggested-by: Peter Maydell <peter.maydell@linaro.org> Cc: John Snow <jsnow@redhat.com> Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
This commit is contained in:
parent
e8e4298fea
commit
47a90a51a9
@ -553,6 +553,74 @@ def pkgname_from_depspec(dep_spec: str) -> str:
|
||||
return match.group(0)
|
||||
|
||||
|
||||
def _get_path_importlib(package: str) -> Optional[str]:
|
||||
# pylint: disable=import-outside-toplevel
|
||||
# pylint: disable=no-name-in-module
|
||||
# pylint: disable=import-error
|
||||
try:
|
||||
# First preference: Python 3.8+ stdlib
|
||||
from importlib.metadata import ( # type: ignore
|
||||
PackageNotFoundError,
|
||||
distribution,
|
||||
)
|
||||
except ImportError as exc:
|
||||
logger.debug("%s", str(exc))
|
||||
# Second preference: Commonly available PyPI backport
|
||||
from importlib_metadata import ( # type: ignore
|
||||
PackageNotFoundError,
|
||||
distribution,
|
||||
)
|
||||
|
||||
try:
|
||||
return str(distribution(package).locate_file("."))
|
||||
except PackageNotFoundError:
|
||||
return None
|
||||
|
||||
|
||||
def _get_path_pkg_resources(package: str) -> Optional[str]:
|
||||
# pylint: disable=import-outside-toplevel
|
||||
# Bundled with setuptools; has a good chance of being available.
|
||||
import pkg_resources
|
||||
|
||||
try:
|
||||
return str(pkg_resources.get_distribution(package).location)
|
||||
except pkg_resources.DistributionNotFound:
|
||||
return None
|
||||
|
||||
|
||||
def _get_path(package: str) -> Optional[str]:
|
||||
try:
|
||||
return _get_path_importlib(package)
|
||||
except ImportError as exc:
|
||||
logger.debug("%s", str(exc))
|
||||
|
||||
try:
|
||||
return _get_path_pkg_resources(package)
|
||||
except ImportError as exc:
|
||||
logger.debug("%s", str(exc))
|
||||
raise Ouch(
|
||||
"Neither importlib.metadata nor pkg_resources found. "
|
||||
"Use Python 3.8+, or install importlib-metadata or setuptools."
|
||||
) from exc
|
||||
|
||||
|
||||
def _path_is_prefix(prefix: Optional[str], path: str) -> bool:
|
||||
try:
|
||||
return (
|
||||
prefix is not None and os.path.commonpath([prefix, path]) == prefix
|
||||
)
|
||||
except ValueError:
|
||||
return False
|
||||
|
||||
|
||||
def _is_system_package(package: str) -> bool:
|
||||
path = _get_path(package)
|
||||
return path is not None and not (
|
||||
_path_is_prefix(sysconfig.get_path("purelib"), path)
|
||||
or _path_is_prefix(sysconfig.get_path("platlib"), path)
|
||||
)
|
||||
|
||||
|
||||
def _get_version_importlib(package: str) -> Optional[str]:
|
||||
# pylint: disable=import-outside-toplevel
|
||||
# pylint: disable=no-name-in-module
|
||||
@ -741,8 +809,12 @@ def _do_ensure(
|
||||
for spec in dep_specs:
|
||||
matcher = distlib.version.LegacyMatcher(spec)
|
||||
ver = _get_version(matcher.name)
|
||||
if ver is None or not matcher.match(
|
||||
distlib.version.LegacyVersion(ver)
|
||||
if (
|
||||
ver is None
|
||||
# Always pass installed package to pip, so that they can be
|
||||
# updated if the requested version changes
|
||||
or not _is_system_package(matcher.name)
|
||||
or not matcher.match(distlib.version.LegacyVersion(ver))
|
||||
):
|
||||
absent.append(spec)
|
||||
else:
|
||||
|
Loading…
Reference in New Issue
Block a user