Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Ensure get_requires* hook is called before prepare_metadata* #3044

Merged
merged 10 commits into from
Jun 20, 2023
1 change: 1 addition & 0 deletions docs/changelog/3043.bugfix.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Ensure that ``get_requires*`` build hooks are called before ``prepare_metadata*``
gaborbernat marked this conversation as resolved.
Show resolved Hide resolved
34 changes: 25 additions & 9 deletions src/tox/tox_env/python/virtual_env/package/pyproject.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import sys
from collections import defaultdict
from contextlib import contextmanager
from itertools import chain
from pathlib import Path
from threading import RLock
from typing import TYPE_CHECKING, Any, Dict, Generator, Iterator, NoReturn, Optional, Sequence, cast
Expand Down Expand Up @@ -103,6 +104,7 @@ def __init__(self, create_args: ToxEnvCreateArgs) -> None:
self._pkg_lock = RLock() # can build only one package at a time
self.root = self.conf["package_root"]
self._package_paths: set[Path] = set()
self._installed_build_requirements: set[Requirement] = set()

@staticmethod
def id() -> str: # noqa: A003
Expand Down Expand Up @@ -157,14 +159,19 @@ def _setup_env(self) -> None:
if "editable" in self.builds:
if not self._frontend.optional_hooks["build_editable"]:
raise BuildEditableNotSupportedError
build_requires = self._frontend.get_requires_for_build_editable().requires
self._install(build_requires, PythonPackageToxEnv.__name__, "requires_for_build_editable")
self._setup_build_requires("editable")
if "wheel" in self.builds:
build_requires = self._frontend.get_requires_for_build_wheel().requires
self._install(build_requires, PythonPackageToxEnv.__name__, "requires_for_build_wheel")
self._setup_build_requires("wheel")
if "sdist" in self.builds or "external" in self.builds:
build_requires = self._frontend.get_requires_for_build_sdist().requires
self._install(build_requires, PythonPackageToxEnv.__name__, "requires_for_build_sdist")
self._setup_build_requires("sdist")

def _setup_build_requires(self, of_type: str) -> None:
hook = getattr(self._frontend, f"get_requires_for_build_{of_type}")
build_requires = hook().requires # already cached via Pep517VirtualEnvFrontend
pending = [req for req in build_requires if req not in self._installed_build_requirements]
gaborbernat marked this conversation as resolved.
Show resolved Hide resolved
if pending:
self._install(pending, PythonPackageToxEnv.__name__, f"requires_for_build_{of_type}")
self._installed_build_requirements.update(pending)

def _teardown(self) -> None:
executor = self._frontend.backend_executor
Expand Down Expand Up @@ -312,8 +319,10 @@ def _ensure_meta_present(self, for_env: EnvConfigSet) -> None:
self.setup()
end = self._frontend
if for_env["package"] == "editable":
self._setup_build_requires("editable")
gaborbernat marked this conversation as resolved.
Show resolved Hide resolved
dist_info = end.prepare_metadata_for_build_editable(self.meta_folder, self._wheel_config_settings).metadata
else:
self._setup_build_requires("wheel")
dist_info = end.prepare_metadata_for_build_wheel(self.meta_folder, self._wheel_config_settings).metadata
self._distribution_meta = Distribution.at(str(dist_info))

Expand All @@ -332,12 +341,19 @@ def __init__(self, root: Path, env: Pep517VirtualEnvPackager) -> None:
self._backend_executor_: LocalSubProcessPep517Executor | None = None
into: dict[str, Any] = {}

for build_type in ("editable", "sdist", "wheel"): # wrap build methods in a cache wrapper
build_types = ["editable", "sdist", "wheel"]
for hook in chain(
(f"build_{build_type}" for build_type in build_types),
(f"get_requires_for_build_{build_type}" for build_type in build_types),
(f"prepare_metadata_for_build_{build_type}" for build_type in build_types),
): # wrap build methods in a cache wrapper
if not hasattr(self, hook):
continue

def key(*args: Any, bound_return: str = build_type, **kwargs: Any) -> str: # noqa: ARG001
def key(*args: Any, bound_return: str = hook, **kwargs: Any) -> str: # noqa: ARG001
return bound_return

setattr(self, f"build_{build_type}", cached(into, key=key)(getattr(self, f"build_{build_type}")))
setattr(self, hook, cached(into, key=key)(getattr(self, hook)))

@property
def backend_cmd(self) -> Sequence[str]:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,8 @@ def test_tox_install_pkg_sdist(tox_project: ToxProjectCreator, pkg_with_extras_p
(".pkg_external_sdist_meta", "install_requires", ["setuptools", "wheel"]),
(".pkg_external_sdist_meta", "_optional_hooks", []),
(".pkg_external_sdist_meta", "get_requires_for_build_sdist", []),
(".pkg_external_sdist_meta", "get_requires_for_build_wheel", []), # required before prepare_metadata*
(".pkg_external_sdist_meta", "install_requires_for_build_wheel", ["wheel"]),
(".pkg_external_sdist_meta", "prepare_metadata_for_build_wheel", []),
("py", "install_package_deps", deps),
("py", "install_package", ["--force-reinstall", "--no-deps", str(pkg_with_extras_project_sdist)]),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -213,6 +213,7 @@ def test_pyproject_deps_static_with_dynamic( # noqa: PLR0913
expected_calls = [
(".pkg", "_optional_hooks"),
(".pkg", "get_requires_for_build_sdist"),
(".pkg", "get_requires_for_build_wheel"),
(".pkg", "build_wheel"),
(".pkg", "build_sdist"),
("py", "install_package_deps"),
Expand All @@ -239,10 +240,10 @@ def test_pyproject_no_build_editable_fallback(tox_project: ToxProjectCreator, de

expected_calls = [
(".pkg", "_optional_hooks"),
(".pkg", "get_requires_for_build_wheel"),
(".pkg", "build_wheel"),
(".pkg", "get_requires_for_build_sdist"),
("a", "install_package"),
(".pkg", "get_requires_for_build_sdist"),
("b", "install_package"),
(".pkg", "_exit"),
]
Expand Down
1 change: 1 addition & 0 deletions tests/tox_env/test_tox_env_runner.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ def test_package_only(
expected_calls = [
(".pkg", "_optional_hooks"),
(".pkg", "get_requires_for_build_sdist"),
(".pkg", "get_requires_for_build_wheel"),
(".pkg", "build_wheel"),
(".pkg", "build_sdist"),
(".pkg", "_exit"),
Expand Down