mkvenv: use pip's vendored distlib as a fallback
authorJohn Snow <jsnow@redhat.com>
Tue, 16 May 2023 07:05:51 +0000 (09:05 +0200)
committerPaolo Bonzini <pbonzini@redhat.com>
Thu, 18 May 2023 06:53:51 +0000 (08:53 +0200)
distlib is usually not installed on Linux distribution, but it is vendored
into pip.  Because the virtual environment has pip via ensurepip, we
can piggy-back on pip's vendored version.  This could break if they move
our cheese in the future, but the fix would be simply to require distlib.

If it is debundled, as it is on msys, it is simply available directly.

Signed-off-by: John Snow <jsnow@redhat.com>
[Move to toplevel. - Paolo]
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
python/scripts/mkvenv.py
python/setup.cfg

index f17c3d3606ba15b6e9113b1057d6acd15be652d0..fb91f922d2b9a22a43f3735ab62b6c6572c70e62 100644 (file)
@@ -69,10 +69,25 @@ from typing import (
 import venv
 import warnings
 
-import distlib.database
-import distlib.scripts
-import distlib.version
 
+# Try to load distlib, with a fallback to pip's vendored version.
+# HAVE_DISTLIB is checked below, just-in-time, so that mkvenv does not fail
+# outside the venv or before a potential call to ensurepip in checkpip().
+HAVE_DISTLIB = True
+try:
+    import distlib.database
+    import distlib.scripts
+    import distlib.version
+except ImportError:
+    try:
+        # Reach into pip's cookie jar.  pylint and flake8 don't understand
+        # that these imports will be used via distlib.xxx.
+        from pip._vendor import distlib
+        import pip._vendor.distlib.database  # noqa, pylint: disable=unused-import
+        import pip._vendor.distlib.scripts  # noqa, pylint: disable=unused-import
+        import pip._vendor.distlib.version  # noqa, pylint: disable=unused-import
+    except ImportError:
+        HAVE_DISTLIB = False
 
 # Do not add any mandatory dependencies from outside the stdlib:
 # This script *must* be usable standalone!
@@ -664,6 +679,10 @@ def ensure(
         bellwether for the presence of 'sphinx'.
     """
     print(f"mkvenv: checking for {', '.join(dep_specs)}", file=sys.stderr)
+
+    if not HAVE_DISTLIB:
+        raise Ouch("a usable distlib could not be found, please install it")
+
     try:
         _do_ensure(dep_specs, online, wheels_dir)
     except subprocess.CalledProcessError as exc:
index 826a2771ba5dc039013a5f23efc9f46870fe7be7..fc3fae5b1076f2f3453ad3e75a693ea0d603defc 100644 (file)
@@ -125,6 +125,24 @@ ignore_missing_imports = True
 [mypy-distlib.version]
 ignore_missing_imports = True
 
+[mypy-pip]
+ignore_missing_imports = True
+
+[mypy-pip._vendor]
+ignore_missing_imports = True
+
+[mypy-pip._vendor.distlib]
+ignore_missing_imports = True
+
+[mypy-pip._vendor.distlib.database]
+ignore_missing_imports = True
+
+[mypy-pip._vendor.distlib.scripts]
+ignore_missing_imports = True
+
+[mypy-pip._vendor.distlib.version]
+ignore_missing_imports = True
+
 [pylint.messages control]
 # Disable the message, report, category or checker with the given id(s). You
 # can either give multiple identifiers separated by comma (,) or put this