Skip to content

Commit 81d1683

Browse files
asottilegaborbernat
authored andcommitted
cowardly refuse to delete a directory if it isn't a venv (#1340)
1 parent ef843dc commit 81d1683

File tree

3 files changed

+47
-1
lines changed

3 files changed

+47
-1
lines changed

docs/changelog/1340.feature.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Refuse to delete ``envdir`` if it doesn't look like a virtualenv - by :user:`asottile`.

src/tox/venv.py

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
from tox import reporter
1313
from tox.action import Action
1414
from tox.config.parallel import ENV_VAR_KEY as PARALLEL_ENV_VAR_KEY
15-
from tox.constants import PARALLEL_RESULT_JSON_PREFIX, PARALLEL_RESULT_JSON_SUFFIX
15+
from tox.constants import INFO, PARALLEL_RESULT_JSON_PREFIX, PARALLEL_RESULT_JSON_SUFFIX
1616
from tox.package.local import resolve_package
1717
from tox.util.lock import get_unique_file
1818
from tox.util.path import ensure_empty_dir
@@ -694,6 +694,31 @@ def tox_testenv_create(venv, action):
694694

695695
def cleanup_for_venv(venv):
696696
within_parallel = PARALLEL_ENV_VAR_KEY in os.environ
697+
# if the directory exists and it doesn't look like a virtualenv, produce
698+
# an error
699+
if venv.path.exists():
700+
dir_items = set(os.listdir(str(venv.path))) - {".lock", "log"}
701+
dir_items = {p for p in dir_items if not p.startswith(".tox-")}
702+
else:
703+
dir_items = set()
704+
705+
if not (
706+
# doesn't exist => OK
707+
not venv.path.exists()
708+
# does exist, but it's empty => OK
709+
or not dir_items
710+
# it exists and we're on windows with Lib and Scripts => OK
711+
or (INFO.IS_WIN and dir_items > {"Scripts", "Lib"})
712+
# non-windows, with lib and bin => OK
713+
or dir_items > {"bin", "lib"}
714+
):
715+
venv.status = "error"
716+
reporter.error(
717+
"cowardly refusing to delete `envdir` (it does not look like a virtualenv): "
718+
"{}".format(venv.path)
719+
)
720+
raise SystemExit(2)
721+
697722
if within_parallel:
698723
if venv.path.exists():
699724
# do not delete the log folder as that's used by parent

tests/unit/test_z_cmdline.py

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,26 @@ def test_envdir_equals_toxini_errors_out(cmd, initproj):
115115
result.assert_fail()
116116

117117

118+
def test_envdir_would_delete_some_directory(cmd, initproj):
119+
projdir = initproj(
120+
"example-123",
121+
filedefs={
122+
"tox.ini": """\
123+
[tox]
124+
125+
[testenv:venv]
126+
envdir=example
127+
commands=
128+
"""
129+
},
130+
)
131+
132+
result = cmd("-e", "venv")
133+
assert projdir.join("example/__init__.py").exists()
134+
result.assert_fail()
135+
assert "cowardly refusing to delete `envdir`" in result.out
136+
137+
118138
def test_run_custom_install_command_error(cmd, initproj):
119139
initproj(
120140
"interp123-0.5",

0 commit comments

Comments
 (0)