[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[PATCH v2 08/27] mkvenv: add console script entry point generation
|
From: |
Paolo Bonzini |
|
Subject: |
[PATCH v2 08/27] mkvenv: add console script entry point generation |
|
Date: |
Tue, 16 May 2023 12:58:49 +0200 |
From: John Snow <jsnow@redhat.com>
When creating a virtual environment that inherits system packages,
script entry points (like "meson", "sphinx-build", etc) are not
re-generated with the correct shebang. When you are *inside* of the
venv, this is not a problem, but if you are *outside* of it, you will
not have a script that engages the virtual environment appropriately.
Add a mechanism that generates new entry points for pre-existing
packages so that we can use these scripts to run "meson",
"sphinx-build", "pip", unambiguously inside the venv.
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
Signed-off-by: John Snow <jsnow@redhat.com>
Message-Id: <20230511035435.734312-9-jsnow@redhat.com>
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
---
python/scripts/mkvenv.py | 114 +++++++++++++++++++++++++++++++++++++++
python/setup.cfg | 3 ++
2 files changed, 117 insertions(+)
diff --git a/python/scripts/mkvenv.py b/python/scripts/mkvenv.py
index 5ac22f584fab..c4c542524144 100644
--- a/python/scripts/mkvenv.py
+++ b/python/scripts/mkvenv.py
@@ -60,6 +60,7 @@
from types import SimpleNamespace
from typing import (
Any,
+ Iterator,
Optional,
Sequence,
Tuple,
@@ -69,6 +70,7 @@
import warnings
import distlib.database
+import distlib.scripts
import distlib.version
@@ -334,6 +336,113 @@ def _stringify(data: Union[str, bytes]) -> str:
print(builder.get_value("env_exe"))
+def _gen_importlib(packages: Sequence[str]) -> Iterator[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,
+ )
+
+ def _generator() -> Iterator[str]:
+ for package in packages:
+ try:
+ entry_points = distribution(package).entry_points
+ except PackageNotFoundError:
+ continue
+
+ # The EntryPoints type is only available in 3.10+,
+ # treat this as a vanilla list and filter it ourselves.
+ entry_points = filter(
+ lambda ep: ep.group == "console_scripts", entry_points
+ )
+
+ for entry_point in entry_points:
+ yield f"{entry_point.name} = {entry_point.value}"
+
+ return _generator()
+
+
+def _gen_pkg_resources(packages: Sequence[str]) -> Iterator[str]:
+ # pylint: disable=import-outside-toplevel
+ # Bundled with setuptools; has a good chance of being available.
+ import pkg_resources
+
+ def _generator() -> Iterator[str]:
+ for package in packages:
+ try:
+ eps = pkg_resources.get_entry_map(package, "console_scripts")
+ except pkg_resources.DistributionNotFound:
+ continue
+
+ for entry_point in eps.values():
+ yield str(entry_point)
+
+ return _generator()
+
+
+def generate_console_scripts(
+ packages: Sequence[str],
+ python_path: Optional[str] = None,
+ bin_path: Optional[str] = None,
+) -> None:
+ """
+ Generate script shims for console_script entry points in @packages.
+ """
+ if python_path is None:
+ python_path = sys.executable
+ if bin_path is None:
+ bin_path = sysconfig.get_path("scripts")
+ assert bin_path is not None
+
+ logger.debug(
+ "generate_console_scripts(packages=%s, python_path=%s, bin_path=%s)",
+ packages,
+ python_path,
+ bin_path,
+ )
+
+ if not packages:
+ return
+
+ def _get_entry_points() -> Iterator[str]:
+ """Python 3.7 compatibility shim for iterating entry points."""
+ # Python 3.8+, or Python 3.7 with importlib_metadata installed.
+ try:
+ return _gen_importlib(packages)
+ except ImportError as exc:
+ logger.debug("%s", str(exc))
+
+ # Python 3.7 with setuptools installed.
+ try:
+ return _gen_pkg_resources(packages)
+ except ImportError as exc:
+ logger.debug("%s", str(exc))
+ raise Ouch(
+ "Neither importlib.metadata nor pkg_resources found, "
+ "can't generate console script shims.\n"
+ "Use Python 3.8+, or install importlib-metadata or setuptools."
+ ) from exc
+
+ maker = distlib.scripts.ScriptMaker(None, bin_path)
+ maker.variants = {""}
+ maker.clobber = False
+
+ for entry_point in _get_entry_points():
+ for filename in maker.make(entry_point):
+ logger.debug("wrote console_script '%s'", filename)
+
+
def pkgname_from_depspec(dep_spec: str) -> str:
"""
Parse package name out of a PEP-508 depspec.
@@ -512,6 +621,7 @@ def _do_ensure(
)
dist_path = distlib.database.DistributionPath(include_egg=True)
absent = []
+ present = []
for spec in dep_specs:
matcher = distlib.version.LegacyMatcher(spec)
dist = dist_path.get_distribution(matcher.name)
@@ -519,6 +629,10 @@ def _do_ensure(
absent.append(spec)
else:
logger.info("found %s", dist)
+ present.append(matcher.name)
+
+ if present:
+ generate_console_scripts(present)
if absent:
# Some packages are missing or aren't a suitable version,
diff --git a/python/setup.cfg b/python/setup.cfg
index d680374b2950..826a2771ba5d 100644
--- a/python/setup.cfg
+++ b/python/setup.cfg
@@ -119,6 +119,9 @@ ignore_missing_imports = True
[mypy-distlib.database]
ignore_missing_imports = True
+[mypy-distlib.scripts]
+ignore_missing_imports = True
+
[mypy-distlib.version]
ignore_missing_imports = True
--
2.40.1
- [PATCH v2 03/27] python: add mkvenv.py, (continued)
- [PATCH v2 03/27] python: add mkvenv.py, Paolo Bonzini, 2023/05/16
- [PATCH v2 06/27] mkvenv: add ensure subcommand, Paolo Bonzini, 2023/05/16
- [PATCH v2 05/27] mkvenv: add nested venv workaround, Paolo Bonzini, 2023/05/16
- [PATCH v2 04/27] mkvenv: add better error message for broken or missing ensurepip, Paolo Bonzini, 2023/05/16
- [PATCH v2 02/27] python: update pylint configuration, Paolo Bonzini, 2023/05/16
- [PATCH v2 07/27] mkvenv: add --diagnose option to explain "ensure" failures, Paolo Bonzini, 2023/05/16
- [PATCH v2 13/27] tests/vm: Configure netbsd to use Python 3.10, Paolo Bonzini, 2023/05/16
- [PATCH v2 08/27] mkvenv: add console script entry point generation,
Paolo Bonzini <=
- [PATCH v2 15/27] python: add vendor.py utility, Paolo Bonzini, 2023/05/16
- [PATCH v2 20/27] tests: Use configure-provided pyvenv for tests, Paolo Bonzini, 2023/05/16
- [PATCH v2 11/27] mkvenv: work around broken pip installations on Debian 10, Paolo Bonzini, 2023/05/16
- [PATCH v2 19/27] qemu.git: drop meson git submodule, Paolo Bonzini, 2023/05/16
- [PATCH v2 23/27] configure: add --enable-pypi and --disable-pypi, Paolo Bonzini, 2023/05/16
- [PATCH v2 16/27] configure: create a python venv unconditionally, Paolo Bonzini, 2023/05/16
- [PATCH v2 12/27] tests/docker: add python3-venv dependency, Paolo Bonzini, 2023/05/16
- [PATCH v2 14/27] tests/vm: add py310-expat to NetBSD, Paolo Bonzini, 2023/05/16