diff --git a/CONTRIBUTORS b/CONTRIBUTORS index 0eab06c58..cf1ac350e 100644 --- a/CONTRIBUTORS +++ b/CONTRIBUTORS @@ -80,6 +80,7 @@ Marc Schlaich Marius Gedminas Mariusz Rusiniak Mark Hirota +Masen Furer Matt Good Matt Jeffery Matthew Kenigsberg diff --git a/docs/changelog/2515.bugfix.rst b/docs/changelog/2515.bugfix.rst new file mode 100644 index 000000000..686fc1ac1 --- /dev/null +++ b/docs/changelog/2515.bugfix.rst @@ -0,0 +1,2 @@ +Multiple tox instances no longer clobber the ``.tox`` directory when +``provision_tox_env`` is used. - by :user:`masenf` diff --git a/docs/config.rst b/docs/config.rst index 445d54c2c..d3b28ff72 100644 --- a/docs/config.rst +++ b/docs/config.rst @@ -76,6 +76,11 @@ Global settings are defined under the ``tox`` section as: When tox is invoked with the ``--no-provision`` flag, the provision won't be attempted, tox will fail instead. + .. versionchanged:: 3.27.0 + + When provisioning, tox will take a lock to ensure exclusive access to the + `provision_tox_env` and avoid clobbering by other tox instances. + .. warning:: The new virtual environment will only contain dependencies specified by the :conf:`requires` keyword. diff --git a/src/tox/session/commands/provision.py b/src/tox/session/commands/provision.py index 8508f5cf3..738bcb84e 100644 --- a/src/tox/session/commands/provision.py +++ b/src/tox/session/commands/provision.py @@ -4,6 +4,8 @@ import os from tox.exception import InvocationError +from tox.reporter import verbosity0 +from tox.util.lock import hold_lock def provision_tox(provision_venv, args): @@ -21,5 +23,9 @@ def provision_tox(provision_venv, args): def ensure_meta_env_up_to_date(provision_venv): - if provision_venv.setupenv(): - provision_venv.finishvenv() + config = provision_venv.envconfig.config + lock_file = config.toxworkdir.join("{}.lock".format(config.provision_tox_env)) + + with hold_lock(lock_file, verbosity0): + if provision_venv.setupenv(): + provision_venv.finishvenv() diff --git a/tests/integration/test_provision_int.py b/tests/integration/test_provision_int.py index 05fb1a667..f1763e494 100644 --- a/tests/integration/test_provision_int.py +++ b/tests/integration/test_provision_int.py @@ -134,3 +134,34 @@ def test_provision_interrupt_child(initproj, monkeypatch, capfd, signal_type): out, "\n".join(repr(i) for i in all_process), ) + + +@pytest.mark.skipif("sys.platform == 'win32'", reason="pyenv does not exists on Windows") +def test_provision_race(initproj, cmd, monkeypatch): + initproj( + "pkg123-0.7", + filedefs={ + "tox.ini": """\ + [tox] + skipsdist=True + minversion = 3.7.0 + requires = + setuptools == 40.6.3 + [testenv] + commands=python -c "import sys; print(sys.executable); raise SystemExit(1)" + [testenv:x2] + """, + }, + ) + + procs = [ + subprocess.Popen( + [sys.executable, "-m", "tox", "-e", "py", "-vv"], + stdout=subprocess.PIPE, + ) + for _ in range(2) + ] + for proc in procs: + stdout, stderr = proc.communicate() + assert proc.returncode != 0 + assert b".tox/.tox/bin/python -m virtualenv" in stdout