Skip to content

Commit 2476e95

Browse files
authored
Create session views of the build wheel/sdist into temp_dir (#2614)
Resolves #2612
1 parent 6305d9a commit 2476e95

File tree

11 files changed

+81
-19
lines changed

11 files changed

+81
-19
lines changed

.pre-commit-config.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ repos:
5252
hooks:
5353
- id: flake8
5454
additional_dependencies:
55-
- flake8-bugbear==22.10.27
55+
- flake8-bugbear==22.12.6
5656
- flake8-comprehensions==3.10.1
5757
- flake8-pytest-style==1.6
5858
- flake8-spellcheck==0.28

docs/changelog/2612.bugfix.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Create session views of the build wheel/sdist into the :ref:`temp_dir` folder - by :user:`gaborbernat`.

docs/config.rst

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -172,7 +172,7 @@ Core
172172

173173
.. conf::
174174
:keys: temp_dir
175-
:default: {tox_root}/.tmp
175+
:default: {tox_root}/.temp
176176

177177
Directory where to put tox temporary files. For example: we create a hard link (if possible, otherwise new copy) in
178178
this directory for the project package. This ensures tox works correctly when having parallel runs (as each session

pyproject.toml

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[build-system]
22
build-backend = "hatchling.build"
3-
requires = ["hatchling>=1.11.1", "hatch-vcs>=0.2"]
3+
requires = ["hatchling>=1.11.1", "hatch-vcs>=0.2.1"]
44

55
[project]
66
name = "tox"
@@ -24,8 +24,8 @@ dependencies = [
2424
"cachetools>=5.2",
2525
"chardet>=5.1",
2626
"colorama>=0.4.6",
27-
"packaging>=21.3",
28-
"platformdirs>=2.5.4",
27+
"packaging>=22",
28+
"platformdirs>=2.6",
2929
"pluggy>=1",
3030
"pyproject-api>=1.2.1",
3131
'tomli>=2.0.1; python_version < "3.11"',
@@ -35,7 +35,7 @@ dependencies = [
3535
'typing-extensions>=4.4; python_version < "3.8"',
3636
]
3737
optional-dependencies.docs = [
38-
"furo>=2022.9.29",
38+
"furo>=2022.12.7",
3939
"sphinx>=5.3",
4040
"sphinx-argparse-cli>=1.10",
4141
"sphinx-autodoc-typehints>=1.19.5",
@@ -51,7 +51,7 @@ optional-dependencies.testing = [
5151
"diff-cover>=7.2",
5252
"distlib>=0.3.6",
5353
"flaky>=3.7",
54-
"hatch-vcs>=0.2",
54+
"hatch-vcs>=0.2.1",
5555
"hatchling>=1.11.1",
5656
"psutil>=5.9.4",
5757
"pytest>=7.2",

src/tox/config/sets.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -199,8 +199,8 @@ def work_dir_builder(conf: Config, env_name: str | None) -> Path: # noqa: U100
199199
self.add_config(
200200
keys=["temp_dir"],
201201
of_type=Path,
202-
default=lambda conf, _: cast(Path, self["tox_root"]) / ".tmp", # noqa: U100, U101
203-
desc="temporary directory cleaned at start",
202+
default=lambda conf, _: cast(Path, self["work_dir"]) / ".tmp", # noqa: U100, U101
203+
desc="a folder for temporary files (is not cleaned at start)",
204204
)
205205

206206
def _on_duplicate_conf(self, key: str, definition: ConfigDefinition[V]) -> None: # noqa: U100

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

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030
)
3131
from tox.tox_env.register import ToxEnvRegister
3232
from tox.tox_env.runner import RunToxEnv
33+
from tox.util.file_view import create_session_view
3334

3435
from ..api import VirtualEnv
3536
from .util import dependencies_with_extras
@@ -99,6 +100,7 @@ def __init__(self, create_args: ToxEnvCreateArgs) -> None:
99100
self._package_name: str | None = None
100101
self._pkg_lock = RLock() # can build only one package at a time
101102
self.root = self.conf["package_root"]
103+
self._package_paths: set[Path] = set()
102104

103105
@staticmethod
104106
def id() -> str:
@@ -164,6 +166,10 @@ def _teardown(self) -> None:
164166
pass
165167
finally:
166168
executor.close()
169+
for path in self._package_paths:
170+
if path.exists():
171+
logging.debug("delete package %s", path)
172+
path.unlink()
167173
super()._teardown()
168174

169175
def perform_packaging(self, for_env: EnvConfigSet) -> list[Package]:
@@ -189,7 +195,10 @@ def perform_packaging(self, for_env: EnvConfigSet) -> list[Package]:
189195
elif of_type == "sdist":
190196
self.setup()
191197
with self._pkg_lock:
192-
package = SdistPackage(self._frontend.build_sdist(sdist_directory=self.pkg_dir).sdist, deps)
198+
sdist = self._frontend.build_sdist(sdist_directory=self.pkg_dir).sdist
199+
sdist = create_session_view(sdist, self._package_temp_path)
200+
self._package_paths.add(sdist)
201+
package = SdistPackage(sdist, deps)
193202
elif of_type in {"wheel", "editable"}:
194203
w_env = self._wheel_build_envs.get(for_env["wheel_build_env"])
195204
if w_env is not None and w_env is not self:
@@ -199,16 +208,22 @@ def perform_packaging(self, for_env: EnvConfigSet) -> list[Package]:
199208
self.setup()
200209
method = "build_editable" if of_type == "editable" else "build_wheel"
201210
with self._pkg_lock:
202-
path = getattr(self._frontend, method)(
211+
wheel = getattr(self._frontend, method)(
203212
wheel_directory=self.pkg_dir,
204213
metadata_directory=self.meta_folder,
205214
config_settings=self._wheel_config_settings,
206215
).wheel
207-
package = (EditablePackage if of_type == "editable" else WheelPackage)(path, deps)
216+
wheel = create_session_view(wheel, self._package_temp_path)
217+
self._package_paths.add(wheel)
218+
package = (EditablePackage if of_type == "editable" else WheelPackage)(wheel, deps)
208219
else: # pragma: no cover # for when we introduce new packaging types and don't implement
209220
raise TypeError(f"cannot handle package type {of_type}") # pragma: no cover
210221
return [package]
211222

223+
@property
224+
def _package_temp_path(self) -> Path:
225+
return cast(Path, self.core["temp_dir"]) / "package"
226+
212227
def _load_deps(self, for_env: EnvConfigSet) -> list[Requirement]:
213228
# first check if this is statically available via PEP-621
214229
deps = self._load_deps_from_static(for_env)

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
from copy import deepcopy
44

5-
from packaging.markers import Variable
5+
from packaging.markers import Variable # type: ignore[attr-defined]
66
from packaging.requirements import Requirement
77

88

src/tox/util/file_view.py

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
from __future__ import annotations
2+
3+
import logging
4+
import os
5+
import shutil
6+
from itertools import chain
7+
from os.path import commonpath
8+
from pathlib import Path
9+
10+
11+
def create_session_view(package: Path, temp_path: Path) -> Path:
12+
"""Allows using the file after you no longer holding a lock to it by moving it into a temp folder"""
13+
# we'll number the active instances, and use the max value as session folder for a new build
14+
# note we cannot change package names as PEP-491 (wheel binary format)
15+
# is strict about file name structure
16+
17+
temp_path.mkdir(parents=True, exist_ok=True)
18+
exists = [i.name for i in temp_path.iterdir()]
19+
file_id = max(chain((0,), (int(i) for i in exists if str(i).isnumeric())))
20+
session_dir = temp_path / str(file_id + 1)
21+
session_dir.mkdir()
22+
session_package = session_dir / package.name
23+
24+
links = False # if we can do hard links do that, otherwise just copy
25+
if hasattr(os, "link"):
26+
try:
27+
os.link(package, session_package)
28+
links = True
29+
except (OSError, NotImplementedError):
30+
pass
31+
if not links:
32+
shutil.copyfile(package, session_package)
33+
operation = "links" if links else "copied"
34+
common = commonpath((session_package, package))
35+
rel_session, rel_package = session_package.relative_to(common), package.relative_to(common)
36+
logging.debug("package %s %s to %s (%s)", rel_session, operation, rel_package, common)
37+
return session_package

tests/tox_env/python/test_python_runner.py

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,3 +88,14 @@ def test_journal_package_dir(tmp_path: Path) -> None:
8888
"type": "dir",
8989
},
9090
}
91+
92+
93+
def test_package_temp_dir_view(tox_project: ToxProjectCreator, demo_pkg_inline: Path) -> None:
94+
project = tox_project({"tox.ini": "[testenv]\npackage=wheel"})
95+
result = project.run("r", "-vv", "-e", "py", "--root", str(demo_pkg_inline))
96+
result.assert_success()
97+
wheel_name = "demo_pkg_inline-1.0.0-py3-none-any.whl"
98+
session_path = Path(".tmp") / "package" / "1" / wheel_name
99+
msg = f" D package {session_path} links to {Path('.pkg') / 'dist'/ wheel_name} ({project.path/ '.tox'}) "
100+
assert msg in result.out
101+
assert f" D delete package {project.path / '.tox' / session_path}" in result.out

tox.ini

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -91,7 +91,7 @@ description = do a release, required posarg of the version number
9191
skip_install = true
9292
deps =
9393
gitpython>=3.1.29
94-
packaging>=21.3
94+
packaging>=22
9595
towncrier>=22.8
9696
commands =
9797
python {toxinidir}/tasks/release.py --version {posargs}

whitelist.txt

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,8 @@
1-
0rc1
21
0x3
32
10ms
43
1s
54
2s
65
5s
7-
a0
86
abi
97
addinivalue
108
addnodes
@@ -30,6 +28,7 @@ chdir
3028
codec
3129
colorama
3230
commenters
31+
commonpath
3332
conftest
3433
contnode
3534
copytree
@@ -40,11 +39,9 @@ ctrl
4039
cygwin
4140
deinit
4241
delenv
43-
dev1
4442
devenv
4543
devnull
4644
devpi
47-
devrelease
4845
distinfo
4946
distlib
5047
divmod
@@ -81,6 +78,7 @@ getresult
8178
getsockname
8279
getsourcelines
8380
groupby
81+
hardlink
8482
hookimpl
8583
hookspec
8684
hookspecs
@@ -91,6 +89,7 @@ instream
9189
intersphinx
9290
isalpha
9391
isatty
92+
isnumeric
9493
isspace
9594
iterdir
9695
levelname
@@ -117,7 +116,6 @@ pluggy
117116
pos
118117
posargs
119118
posix
120-
prerelease
121119
prereleases
122120
prj
123121
psutil

0 commit comments

Comments
 (0)