Skip to content

Commit ce0cc62

Browse files
f3flightpre-commit-ci[bot]gaborbernat
authored
Avoid cache collision between wheel and editable wheel builds (#3035)
Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: Bernát Gábor <[email protected]>
1 parent 6de9c2e commit ce0cc62

File tree

6 files changed

+90
-48
lines changed

6 files changed

+90
-48
lines changed

.pre-commit-config.yaml

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -4,35 +4,35 @@ repos:
44
hooks:
55
- id: end-of-file-fixer
66
- id: trailing-whitespace
7-
- repo: https://github.com/astral-sh/ruff-pre-commit
8-
rev: "v0.0.272"
9-
hooks:
10-
- id: ruff
11-
args: [--fix, --exit-non-zero-on-fix]
127
- repo: https://github.com/psf/black
138
rev: 23.3.0
149
hooks:
1510
- id: black
1611
- repo: https://github.com/tox-dev/tox-ini-fmt
17-
rev: "1.3.0"
12+
rev: "1.3.1"
1813
hooks:
1914
- id: tox-ini-fmt
2015
args: ["-p", "fix"]
2116
- repo: https://github.com/tox-dev/pyproject-fmt
22-
rev: "0.11.2"
17+
rev: "0.12.0"
2318
hooks:
2419
- id: pyproject-fmt
25-
additional_dependencies: ["tox>=4.6"]
20+
additional_dependencies: ["tox>=4.6.1"]
2621
- repo: https://github.com/pre-commit/mirrors-prettier
2722
rev: "v3.0.0-alpha.9-for-vscode"
2823
hooks:
2924
- id: prettier
3025
args: ["--print-width=120", "--prose-wrap=always"]
3126
- repo: https://github.com/asottile/blacken-docs
32-
rev: 1.13.0
27+
rev: 1.14.0
3328
hooks:
3429
- id: blacken-docs
3530
additional_dependencies: [black==23.3]
31+
- repo: https://github.com/astral-sh/ruff-pre-commit
32+
rev: "v0.0.272"
33+
hooks:
34+
- id: ruff
35+
args: [--fix, --exit-non-zero-on-fix]
3636
- repo: https://github.com/pre-commit/pygrep-hooks
3737
rev: v1.10.0
3838
hooks:

docs/changelog/3035.bugfix.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Avoid cache collision between editable wheel build and normal wheel build -- by :user:`f3flight`.

pyproject.toml

Lines changed: 32 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -56,16 +56,16 @@ dependencies = [
5656
"packaging>=23.1",
5757
"platformdirs>=3.5.3",
5858
"pluggy>=1",
59-
"pyproject-api>=1.5.1",
59+
"pyproject-api>=1.5.2",
6060
'tomli>=2.0.1; python_version < "3.11"',
6161
'typing-extensions>=4.6.3; python_version < "3.8"',
62-
"virtualenv>=20.23",
62+
"virtualenv>=20.23.1",
6363
]
6464
optional-dependencies.docs = [
6565
"furo>=2023.5.20",
6666
"sphinx>=7.0.1",
67-
"sphinx-argparse-cli>=1.11",
68-
"sphinx-autodoc-typehints!=1.23.4,>=1.23",
67+
"sphinx-argparse-cli>=1.11.1",
68+
"sphinx-autodoc-typehints!=1.23.4,>=1.23.2",
6969
"sphinx-copybutton>=0.5.2",
7070
"sphinx-inline-tabs>=2023.4.21",
7171
"sphinxcontrib-towncrier>=0.2.1a0",
@@ -75,7 +75,7 @@ optional-dependencies.testing = [
7575
"build[virtualenv]>=0.10",
7676
"covdefaults>=2.3",
7777
"detect-test-pollution>=1.1.1",
78-
"devpi-process>=0.3",
78+
"devpi-process>=0.3.1",
7979
"diff-cover>=7.6",
8080
"distlib>=0.3.6",
8181
"flaky>=3.7",
@@ -84,10 +84,10 @@ optional-dependencies.testing = [
8484
"psutil>=5.9.5",
8585
"pytest>=7.3.2",
8686
"pytest-cov>=4.1",
87-
"pytest-mock>=3.10",
87+
"pytest-mock>=3.11.1",
8888
"pytest-xdist>=3.3.1",
8989
"re-assert>=1.1",
90-
'time-machine>=2.9; implementation_name != "pypy"',
90+
'time-machine>=2.10; implementation_name != "pypy"',
9191
"wheel>=0.40",
9292
]
9393
urls.Documentation = "https://tox.wiki"
@@ -106,6 +106,31 @@ version.source = "vcs"
106106
[tool.black]
107107
line-length = 120
108108

109+
[tool.ruff]
110+
select = ["ALL"]
111+
line-length = 120
112+
target-version = "py37"
113+
isort = {known-first-party = ["tox", "tests"], required-imports = ["from __future__ import annotations"]}
114+
ignore = [
115+
"INP001", # no implicit namespaces here
116+
"D", # ignore documentation for now
117+
"ANN401", # Dynamically typed expressions (typing.Any) are disallowed in `arg`"
118+
"ANN101", # Missing type annotation for `self` in method
119+
"ANN102", # Missing type annotation for `cls` in classmethod"
120+
"D203", # `one-blank-line-before-class` (D203) and `no-blank-line-before-class` (D211) are incompatible
121+
"D212", # `multi-line-summary-first-line` (D212) and `multi-line-summary-second-line` (D213) are incompatible
122+
"S104", # Possible binding to all interface
123+
]
124+
[tool.ruff.per-file-ignores]
125+
"tests/**/*.py" = [
126+
"S101", # asserts allowed in tests...
127+
"FBT", # don"t care about booleans as positional arguments in tests
128+
"INP001", # no implicit namespace
129+
"D", # don"t care about documentation in tests
130+
"S603", # `subprocess` call: check for execution of untrusted input
131+
"PLR2004", # Magic value used in comparison, consider replacing with a constant variable
132+
]
133+
109134
[tool.pytest.ini_options]
110135
testpaths = ["tests"]
111136
addopts = "--tb=auto -ra --showlocals --no-success-flaky-report"
@@ -152,28 +177,3 @@ title_format = false
152177
issue_format = ":issue:`{issue}`"
153178
template = "docs/changelog/template.jinja2"
154179
# possible types, all default: feature, bugfix, doc, removal, misc
155-
156-
[tool.ruff]
157-
select = ["ALL"]
158-
line-length = 120
159-
target-version = "py37"
160-
isort = {known-first-party = ["tox", "tests"], required-imports = ["from __future__ import annotations"]}
161-
ignore = [
162-
"INP001", # no implicit namespaces here
163-
"D", # ignore documentation for now
164-
"ANN401", # Dynamically typed expressions (typing.Any) are disallowed in `arg`"
165-
"ANN101", # Missing type annotation for `self` in method
166-
"ANN102", # Missing type annotation for `cls` in classmethod"
167-
"D203", # `one-blank-line-before-class` (D203) and `no-blank-line-before-class` (D211) are incompatible
168-
"D212", # `multi-line-summary-first-line` (D212) and `multi-line-summary-second-line` (D213) are incompatible
169-
"S104", # Possible binding to all interface
170-
]
171-
[tool.ruff.per-file-ignores]
172-
"tests/**/*.py" = [
173-
"S101", # asserts allowed in tests...
174-
"FBT", # don"t care about booleans as positional arguments in tests
175-
"INP001", # no implicit namespace
176-
"D", # don"t care about documentation in tests
177-
"S603", # `subprocess` call: check for execution of untrusted input
178-
"PLR2004", # Magic value used in comparison, consider replacing with a constant variable
179-
]

src/tox/tox_env/python/virtual_env/package/pyproject.py

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -331,13 +331,13 @@ def __init__(self, root: Path, env: Pep517VirtualEnvPackager) -> None:
331331
self._tox_env = env
332332
self._backend_executor_: LocalSubProcessPep517Executor | None = None
333333
into: dict[str, Any] = {}
334-
pkg_cache = cached(
335-
into,
336-
key=lambda *args, **kwargs: "wheel" if "wheel_directory" in kwargs else "sdist", # noqa: ARG005
337-
)
338-
self.build_wheel = pkg_cache(self.build_wheel) # type: ignore[method-assign]
339-
self.build_sdist = pkg_cache(self.build_sdist) # type: ignore[method-assign]
340-
self.build_editable = pkg_cache(self.build_editable) # type: ignore[method-assign]
334+
335+
for build_type in ("editable", "sdist", "wheel"): # wrap build methods in a cache wrapper
336+
337+
def key(*args: Any, bound_return: str = build_type, **kwargs: Any) -> str: # noqa: ARG001
338+
return bound_return
339+
340+
setattr(self, f"build_{build_type}", cached(into, key=key)(getattr(self, f"build_{build_type}")))
341341

342342
@property
343343
def backend_cmd(self) -> Sequence[str]:

tests/demo_pkg_inline/build.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,16 @@ def get_requires_for_build_wheel(config_settings: dict[str, str] | None = None)
117117
return [] # pragma: no cover # only executed in non-host pythons
118118

119119

120+
if os.environ.get("BACKEND_HAS_EDITABLE"):
121+
122+
def build_editable(
123+
wheel_directory: str,
124+
config_settings: dict[str, str] | None = None,
125+
metadata_directory: str | None = None,
126+
) -> str:
127+
return build_wheel(wheel_directory, config_settings, metadata_directory)
128+
129+
120130
def build_sdist(sdist_directory: str, config_settings: dict[str, str] | None = None) -> str: # noqa: ARG001
121131
result = f"{name}-{version}.tar.gz" # pragma: win32 cover
122132
with tarfile.open(str(Path(sdist_directory) / result), "w:gz") as tar: # pragma: win32 cover

tests/tox_env/python/virtual_env/package/test_package_pyproject.py

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -263,3 +263,34 @@ def test_project_package_with_deps(tox_project: ToxProjectCreator, demo_pkg_setu
263263
else:
264264
assert found_calls[0] == (".pkg", "install_requires")
265265
assert found_calls[1] == (".pkg", "install_deps")
266+
267+
268+
def test_pyproject_build_editable_and_wheel(tox_project: ToxProjectCreator, demo_pkg_inline: Path) -> None:
269+
# test that build wheel and build editable are cached separately
270+
271+
ini = """
272+
[testenv:.pkg]
273+
set_env= BACKEND_HAS_EDITABLE=1
274+
[testenv:a,b]
275+
package = editable
276+
[testenv:c,d]
277+
package = wheel
278+
"""
279+
proj = tox_project({"tox.ini": ini}, base=demo_pkg_inline)
280+
execute_calls = proj.patch_execute(lambda r: 0 if "install" in r.run_id else None)
281+
282+
result = proj.run("r", "-e", "a,b,c,d", "--notest", "--workdir", str(proj.path / ".tox"))
283+
284+
result.assert_success()
285+
found_calls = [(i[0][0].conf.name, i[0][3].run_id) for i in execute_calls.call_args_list]
286+
assert found_calls == [
287+
(".pkg", "_optional_hooks"),
288+
(".pkg", "get_requires_for_build_wheel"),
289+
(".pkg", "build_editable"),
290+
("a", "install_package"),
291+
("b", "install_package"),
292+
(".pkg", "build_wheel"),
293+
("c", "install_package"),
294+
("d", "install_package"),
295+
(".pkg", "_exit"),
296+
]

0 commit comments

Comments
 (0)