Skip to content

Commit 2a29a1b

Browse files
feat: Write CACHEDIR.TAG file (#2805)
Signed-off-by: Bernát Gábor <[email protected]> Co-authored-by: Neil Ramsay <[email protected]> Co-authored-by: Bernát Gábor <[email protected]>
1 parent d619967 commit 2a29a1b

File tree

4 files changed

+75
-0
lines changed

4 files changed

+75
-0
lines changed

docs/changelog/2803.feature.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Write CACHEDIR.TAG file on creation - by "user:`neilramsay`.

src/virtualenv/create/creator.py

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
import logging
55
import os
66
import sys
7+
import textwrap
78
from abc import ABC, abstractmethod
89
from argparse import ArgumentTypeError
910
from ast import literal_eval
@@ -157,10 +158,23 @@ def run(self):
157158
LOGGER.debug("delete %s", self.dest)
158159
safe_delete(self.dest)
159160
self.create()
161+
self.add_cachedir_tag()
160162
self.set_pyenv_cfg()
161163
if not self.no_vcs_ignore:
162164
self.setup_ignore_vcs()
163165

166+
def add_cachedir_tag(self):
167+
"""Generate a file indicating that this is not meant to be backed up."""
168+
cachedir_tag_file = self.dest / "CACHEDIR.TAG"
169+
if not cachedir_tag_file.exists():
170+
cachedir_tag_text = textwrap.dedent("""
171+
Signature: 8a477f597d28d172789f06886806bc55
172+
# This file is a cache directory tag created by Python virtualenv.
173+
# For information about cache directory tags, see:
174+
# https://bford.info/cachedir/
175+
""").strip()
176+
cachedir_tag_file.write_text(cachedir_tag_text, encoding="utf-8")
177+
164178
def set_pyenv_cfg(self):
165179
self.pyenv_cfg.content = OrderedDict()
166180
self.pyenv_cfg["home"] = os.path.dirname(os.path.abspath(self.interpreter.system_executable))
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
from __future__ import annotations
2+
3+
import shutil
4+
import sys
5+
from subprocess import check_output, run
6+
from typing import TYPE_CHECKING
7+
8+
import pytest
9+
10+
from virtualenv import cli_run
11+
12+
if TYPE_CHECKING:
13+
from pathlib import Path
14+
15+
# gtar => gnu-tar on macOS
16+
TAR = next((target for target in ("gtar", "tar") if shutil.which(target)), None)
17+
18+
19+
def compatible_is_tar_present() -> bool:
20+
return TAR and "--exclude-caches" in check_output(args=[TAR, "--help"], text=True)
21+
22+
23+
@pytest.mark.skipif(sys.platform == "win32", reason="Windows does not have tar")
24+
@pytest.mark.skipif(not compatible_is_tar_present(), reason="Compatible tar is not installed")
25+
def test_cachedir_tag_ignored_by_tag(tmp_path: Path) -> None:
26+
venv = tmp_path / ".venv"
27+
cli_run(["--activators", "", "--without-pip", str(venv)])
28+
29+
args = [TAR, "--create", "--file", "/dev/null", "--exclude-caches", "--verbose", venv.name]
30+
tar_result = run(args=args, capture_output=True, text=True, cwd=tmp_path)
31+
assert tar_result.stdout == ".venv/\n.venv/CACHEDIR.TAG\n"
32+
assert tar_result.stderr == f"{TAR}: .venv/: contains a cache directory tag CACHEDIR.TAG; contents not dumped\n"

tests/unit/create/test_creator.py

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
import stat
1212
import subprocess
1313
import sys
14+
import textwrap
1415
import zipfile
1516
from collections import OrderedDict
1617
from itertools import product
@@ -223,6 +224,33 @@ def list_to_str(iterable):
223224
assert git_ignore.splitlines() == [comment, "*"]
224225

225226

227+
def test_create_cachedir_tag(tmp_path):
228+
cachedir_tag_file = tmp_path / "CACHEDIR.TAG"
229+
cli_run([str(tmp_path), "--without-pip", "--activators", ""])
230+
231+
expected = """
232+
Signature: 8a477f597d28d172789f06886806bc55
233+
# This file is a cache directory tag created by Python virtualenv.
234+
# For information about cache directory tags, see:
235+
# https://bford.info/cachedir/
236+
"""
237+
assert cachedir_tag_file.read_text(encoding="utf-8") == textwrap.dedent(expected).strip()
238+
239+
240+
def test_create_cachedir_tag_exists(tmp_path: Path) -> None:
241+
cachedir_tag_file = tmp_path / "CACHEDIR.TAG"
242+
cachedir_tag_file.write_text("magic", encoding="utf-8")
243+
cli_run([str(tmp_path), "--without-pip", "--activators", ""])
244+
assert cachedir_tag_file.read_text(encoding="utf-8") == "magic"
245+
246+
247+
def test_create_cachedir_tag_exists_override(tmp_path: Path) -> None:
248+
cachedir_tag_file = tmp_path / "CACHEDIR.TAG"
249+
cachedir_tag_file.write_text("magic", encoding="utf-8")
250+
cli_run([str(tmp_path), "--without-pip", "--activators", ""])
251+
assert cachedir_tag_file.read_text(encoding="utf-8") == "magic"
252+
253+
226254
def test_create_vcs_ignore_exists(tmp_path):
227255
git_ignore = tmp_path / ".gitignore"
228256
git_ignore.write_text("magic", encoding="utf-8")

0 commit comments

Comments
 (0)