@@ -11,6 +11,8 @@
Commands:
command Description
create create a venv
+ post_init
+ post-venv initialization
--------------------------------------------------
@@ -23,6 +25,15 @@
-h, --help show this help message and exit
--gen GEN Regenerate console_scripts for given packages, if found.
+--------------------------------------------------
+
+usage: mkvenv post_init [-h] [--gen GEN] [--binpath BINPATH]
+
+options:
+ -h, --help show this help message and exit
+ --gen GEN Regenerate console_scripts for given packages, if found.
+ --binpath BINPATH Path where console script shims should be generated
+
"""
# Copyright (C) 2022-2023 Red Hat, Inc.
@@ -59,6 +70,7 @@
# Do not add any mandatory dependencies from outside the stdlib:
# This script *must* be usable standalone!
+DirType = Union[str, bytes, "os.PathLike[str]", "os.PathLike[bytes]"]
logger = logging.getLogger("mkvenv")
@@ -89,23 +101,42 @@ def __init__(self, *args: Any, **kwargs: Any) -> None:
self.script_packages = kwargs.pop("script_packages", ())
super().__init__(*args, **kwargs)
- # The EnvBuilder class is cute and toggles this setting off
- # before post_setup, but we need it to decide if we want to
- # generate shims or not.
- self._system_site_packages = self.system_site_packages
+ # Make the context available post-creation:
+ self._context: Optional[SimpleNamespace] = None
+
+ def ensure_directories(self, env_dir: DirType) -> SimpleNamespace:
+ logger.debug("ensure_directories(env_dir=%s)", env_dir)
+ self._context = super().ensure_directories(env_dir)
+ return self._context
+
+ def create(self, env_dir: DirType) -> None:
+ logger.debug("create(env_dir=%s)", env_dir)
+ super().create(env_dir)
+ assert self._context is not None
+ self.post_post_setup(self._context)
def post_setup(self, context: SimpleNamespace) -> None:
logger.debug("post_setup(...)")
-
- # Generate console_script entry points for system packages:
- if self._system_site_packages:
- generate_console_scripts(
- context.env_exe, context.bin_path, self.script_packages
- )
-
# print the python executable to stdout for configure.
print(context.env_exe)
+ def post_post_setup(self, context: SimpleNamespace) -> None:
+ """
+ The final, final hook. Enter the venv and run commands inside of it.
+ """
+ args = [
+ context.env_exe,
+ __file__,
+ "post_init",
+ "--binpath",
+ context.bin_path,
+ ]
+ if self.system_site_packages:
+ scripts = ",".join(self.script_packages)
+ if scripts:
+ args += ["--gen", scripts]
+ subprocess.run(args, check=True)
+
def need_ensurepip() -> bool:
"""
@@ -359,6 +390,13 @@ def generate_console_scripts(
"""
Generate script shims for console_script entry points in @packages.
"""
+ logger.debug(
+ "generate_console_scripts(python_path=%s, bin_path=%s, packages=%s)",
+ python_path,
+ bin_path,
+ packages,
+ )
+
if not packages:
return
@@ -392,6 +430,17 @@ def _get_entry_points() -> Iterator[Dict[str, str]]:
logger.debug("wrote '%s'", script_path)
+def post_venv_setup(bin_path: str, packages: Sequence[str] = ()) -> None:
+ """
+ This is intended to be run *inside the venv* after it is created.
+ """
+ python_path = sys.executable
+ logger.debug(
+ "post_venv_setup(bin_path=%s, packages=%s)", bin_path, packages
+ )
+ generate_console_scripts(python_path, bin_path, packages)
+
+
def _add_create_subcommand(subparsers: Any) -> None:
subparser = subparsers.add_parser("create", help="create a venv")
subparser.add_argument(
@@ -408,6 +457,24 @@ def _add_create_subcommand(subparsers: Any) -> None:
)
+def _add_post_init_subcommand(subparsers: Any) -> None:
+ subparser = subparsers.add_parser(
+ "post_init", help="post-venv initialization"
+ )
+ subparser.add_argument(
+ "--gen",
+ type=str,
+ action="append",
+ help="Regenerate console_scripts for given packages, if found.",
+ )
+ subparser.add_argument(
+ "--binpath",
+ type=str,
+ action="store",
+ help="Path where console script shims should be generated",
+ )
+
+
def main() -> int:
"""CLI interface to make_qemu_venv. See module docstring."""
if os.environ.get("DEBUG") or os.environ.get("GITLAB_CI"):
@@ -429,19 +496,23 @@ def main() -> int:
)
_add_create_subcommand(subparsers)
+ _add_post_init_subcommand(subparsers)
args = parser.parse_args()
+ script_packages = []
+ for element in args.gen or ():
+ script_packages.extend(element.split(","))
+
try:
if args.command == "create":
- script_packages = []
- for element in args.gen or ():
- script_packages.extend(element.split(","))
make_venv(
args.target,
system_site_packages=True,
clear=True,
script_packages=script_packages,
)
+ if args.command == "post_init":
+ post_venv_setup(args.binpath, script_packages)
logger.debug("mkvenv.py %s: exiting", args.command)
except Ouch as exc:
print("\n*** Ouch! ***\n", file=sys.stderr)
This patch is meant to ensure that console entry point scripts will always generate on Python 3.7 installations where we may not have access to importlib.metadata. By running it from a separate process *inside* the venv, we can be assured to have access to setuptools and by extension pkg_resources as a fallback. It isn't strictly necessary to do this for Python 3.8+, which will always have importlib.metadata available. Signed-off-by: John Snow <jsnow@redhat.com> --- python/scripts/mkvenv.py | 99 ++++++++++++++++++++++++++++++++++------ 1 file changed, 85 insertions(+), 14 deletions(-)