Skip to content

Commit cc1c43a

Browse files
committed
Add virtualenv seeder plugin to install build
Implements a seeder plugin that extends the seed function of virtualenv. The plugin allows us to seed the `build` package into a new virtual env and work around missing `setuptools` and `wheel` commands in Python 3.12+ virtual envs. Related: python-wheel-build#126 Signed-off-by: Christian Heimes <[email protected]>
1 parent d80da7c commit cc1c43a

File tree

3 files changed

+55
-2
lines changed

3 files changed

+55
-2
lines changed

pyproject.toml

+4-1
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,9 @@ resolve_source = "fromager.sources:default_resolve_source"
5454
build_sdist = "fromager.sources:default_build_sdist"
5555
build_wheel = "fromager.wheels:default_build_wheel"
5656

57+
[project.entry-points."virtualenv.seed"]
58+
fromager = "fromager.virtualenv:FromagerSeeder"
59+
5760
[tool.coverage.run]
5861
branch = true
5962
parallel = true
@@ -130,5 +133,5 @@ exclude = [
130133

131134
[[tool.mypy.overrides]]
132135
# packages without typing annotations and stubs
133-
module = ["pyproject_hooks", "requests_mock", "resolver", "stevedore"]
136+
module = ["pyproject_hooks", "requests_mock", "resolver", "stevedore", "virtualenv.*"]
134137
ignore_missing_imports = true

src/fromager/build_environment.py

+9-1
Original file line numberDiff line numberDiff line change
@@ -88,9 +88,17 @@ def _createenv(self) -> None:
8888
logger.info("reusing build environment in %s", self.path)
8989
return
9090

91+
# use our seeder plugin to install `build` command
9192
logger.debug("creating build environment in %s", self.path)
9293
external_commands.run(
93-
[sys.executable, "-m", "virtualenv", str(self.path)],
94+
[
95+
sys.executable,
96+
"-m",
97+
"virtualenv",
98+
"--download",
99+
"--seeder=fromager",
100+
str(self.path),
101+
],
94102
network_isolation=False,
95103
)
96104
logger.info("created build environment in %s", self.path)

src/fromager/virtualenv.py

+42
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
"""Virtual seed plugin"""
2+
3+
import argparse
4+
import typing
5+
6+
from virtualenv.config.cli.parser import VirtualEnvOptions
7+
from virtualenv.seed.embed.via_app_data.via_app_data import FromAppData
8+
from virtualenv.seed.wheels import Version
9+
10+
11+
class FromagerSeeder(FromAppData):
12+
"""Custom virtualenv seeder to install build command"""
13+
14+
extra_packages: typing.ClassVar[dict[str, Version]] = {"build": Version.bundle}
15+
16+
def __init__(self, options: VirtualEnvOptions) -> None:
17+
for distribution, default in self.extra_packages.items():
18+
setattr(self, f"no_{distribution}", False)
19+
setattr(self, f"{distribution}_version", default)
20+
21+
super().__init__(options)
22+
if not self.download:
23+
raise argparse.ArgumentError(
24+
None, "--download is required to install extra packages"
25+
)
26+
27+
@classmethod
28+
def distributions(cls) -> dict[str, Version]:
29+
dist = super().distributions()
30+
dist.update(cls.extra_packages)
31+
return dist
32+
33+
@classmethod
34+
def add_parser_arguments(
35+
cls, parser: argparse.ArgumentParser, interpreter, app_data
36+
) -> None:
37+
super().add_parser_arguments(parser, interpreter, app_data)
38+
# virtualenv no longer installs setuptool and wheels for
39+
# Python >= 3.12. force installation
40+
for action in parser._actions:
41+
if action.dest in {"setuptools", "wheel"}:
42+
action.default = Version.bundle

0 commit comments

Comments
 (0)