Skip to content

Commit e80c97b

Browse files
committed
Change cache_dir location to prefer venv and project_directory
1 parent a9387df commit e80c97b

File tree

9 files changed

+114
-52
lines changed

9 files changed

+114
-52
lines changed

.taplo.toml

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
[formatting]
2+
# compatibility between toml-sort-fix pre-commit hook and panekj.even-betterer-toml extension
3+
align_comments = false
4+
array_trailing_comma = false
5+
compact_arrays = true
6+
compact_entries = false
7+
compact_inline_tables = true
8+
inline_table_expand = false
9+
reorder_keys = true

.vscode/settings.json

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,7 @@
11
{
2+
"[json]": {
3+
"editor.defaultFormatter": "esbenp.prettier-vscode"
4+
},
25
"[jsonc]": {
36
"editor.defaultFormatter": "esbenp.prettier-vscode"
47
},
@@ -14,8 +17,6 @@
1417
"editor.formatOnSave": true
1518
},
1619
"editor.formatOnSave": true,
17-
"evenBetterToml.formatter.alignComments": false,
18-
"evenBetterToml.formatter.allowedBlankLines": 2,
1920
"files.exclude": {
2021
"*.egg-info": true,
2122
".pytest_cache": true,
@@ -37,8 +38,14 @@
3738
"python.testing.pytestArgs": ["tests"],
3839
"python.testing.pytestEnabled": true,
3940
"python.testing.unittestEnabled": false,
41+
"sortLines.filterBlankLines": true,
4042
"yaml.completion": true,
4143
"yaml.customTags": ["!encrypted/pkcs1-oaep scalar", "!vault scalar"],
4244
"yaml.format.enable": false,
43-
"yaml.validate": true
45+
"yaml.validate": true,
46+
"evenBetterToml.formatter.alignComments": false,
47+
"evenBetterToml.formatter.arrayTrailingComma": true,
48+
"[toml]": {
49+
"editor.defaultFormatter": "panekj.even-betterer-toml"
50+
}
4451
}

pyproject.toml

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -397,7 +397,16 @@ known-first-party = ["ansible_compat"]
397397
known-third-party = ["packaging"]
398398

399399
[tool.ruff.lint.per-file-ignores]
400-
"test/**/*.py" = ["DOC402", "DOC501", "SLF001", "S101", "S404", "FBT001", "PLC2701"]
400+
"test/**/*.py" = [
401+
"DOC402",
402+
"DOC501",
403+
"FBT001",
404+
"PLC2701",
405+
"PLR0917",
406+
"S101",
407+
"S404",
408+
"SLF001"
409+
]
401410

402411
[tool.ruff.lint.pydocstyle]
403412
convention = "google"
@@ -430,4 +439,10 @@ sort_table_keys = true
430439
[tool.uv.pip]
431440
annotation-style = "line"
432441
custom-compile-command = "tox run deps"
433-
no-emit-package = ["ansible-core", "pip", "resolvelib", "typing_extensions", "uv"]
442+
no-emit-package = [
443+
"ansible-core",
444+
"pip",
445+
"resolvelib",
446+
"typing_extensions",
447+
"uv"
448+
]

src/ansible_compat/prerun.py

Lines changed: 22 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,29 @@
11
"""Utilities for configuring ansible runtime environment."""
22

3-
import hashlib
43
import os
54
from pathlib import Path
65

76

8-
def get_cache_dir(project_dir: Path) -> Path:
9-
"""Compute cache directory to be used based on project path."""
10-
# we only use the basename instead of the full path in order to ensure that
11-
# we would use the same key regardless the location of the user home
12-
# directory or where the project is clones (as long the project folder uses
13-
# the same name).
14-
basename = project_dir.resolve().name.encode(encoding="utf-8")
15-
# 6 chars of entropy should be enough
16-
cache_key = hashlib.sha256(basename).hexdigest()[:6]
17-
cache_dir = (
18-
Path(os.getenv("XDG_CACHE_HOME", "~/.cache")).expanduser()
19-
/ "ansible-compat"
20-
/ cache_key
21-
)
7+
def get_cache_dir(project_dir: Path, *, isolated: bool = True) -> Path:
8+
"""Compute cache directory to be used based on project path.
9+
10+
Args:
11+
project_dir: Path to the project directory.
12+
isolated: Whether to use isolated cache directory.
13+
14+
Returns:
15+
Cache directory path.
16+
"""
17+
if "VIRTUAL_ENV" in os.environ:
18+
cache_dir = Path(os.environ["VIRTUAL_ENV"]) / ".ansible"
19+
elif isolated:
20+
cache_dir = project_dir / ".ansible"
21+
else:
22+
cache_dir = Path(os.environ.get("ANSIBLE_HOME", "~/.ansible")).expanduser()
23+
24+
# Ensure basic folder structure exists so `ansible-galaxy list` does not
25+
# fail with: None of the provided paths were usable. Please specify a valid path with
26+
for name in ("roles", "collections"): # pragma: no cover
27+
(cache_dir / name).mkdir(parents=True, exist_ok=True)
28+
2229
return cache_dir

src/ansible_compat/runtime.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -209,8 +209,8 @@ def __init__(
209209
if "PYTHONWARNINGS" not in self.environ: # pragma: no cover
210210
self.environ["PYTHONWARNINGS"] = "ignore:Blowfish has been deprecated"
211211

212-
if isolated:
213-
self.cache_dir = get_cache_dir(self.project_dir)
212+
self.cache_dir = get_cache_dir(self.project_dir, isolated=self.isolated)
213+
214214
self.config = AnsibleConfig(cache_dir=self.cache_dir)
215215

216216
# Add the sys.path to the collection paths if not isolated

test/test_prerun.py

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,12 @@
11
"""Tests for ansible_compat.prerun module."""
22

3+
from __future__ import annotations
4+
35
from pathlib import Path
6+
from typing import TYPE_CHECKING
7+
8+
if TYPE_CHECKING:
9+
from _pytest.monkeypatch import MonkeyPatch
410

511
from ansible_compat.prerun import get_cache_dir
612

@@ -10,3 +16,25 @@ def test_get_cache_dir_relative() -> None:
1016
relative_path = Path()
1117
abs_path = relative_path.resolve()
1218
assert get_cache_dir(relative_path) == get_cache_dir(abs_path)
19+
20+
21+
def test_get_cache_dir_no_isolation_no_venv(monkeypatch: MonkeyPatch) -> None:
22+
"""Test behaviors of get_cache_dir.
23+
24+
Args:
25+
monkeypatch: Pytest fixture for monkeypatching
26+
"""
27+
monkeypatch.delenv("VIRTUAL_ENV", raising=False)
28+
monkeypatch.delenv("ANSIBLE_HOME", raising=False)
29+
assert get_cache_dir(Path(), isolated=False) == Path("~/.ansible").expanduser()
30+
31+
32+
def test_get_cache_dir_isolation_no_venv(monkeypatch: MonkeyPatch) -> None:
33+
"""Test behaviors of get_cache_dir.
34+
35+
Args:
36+
monkeypatch: Pytest fixture for monkeypatching
37+
"""
38+
monkeypatch.delenv("VIRTUAL_ENV", raising=False)
39+
monkeypatch.delenv("ANSIBLE_HOME", raising=False)
40+
assert get_cache_dir(Path(), isolated=True) == Path() / ".ansible"

test/test_runtime.py

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -654,10 +654,9 @@ def test_upgrade_collection(runtime_tmp: Runtime) -> None:
654654
runtime_tmp.require_collection("community.molecule", "0.1.0")
655655

656656

657-
def test_require_collection_no_cache_dir() -> None:
657+
def test_require_collection_not_isolated() -> None:
658658
"""Check require_collection without a cache directory."""
659-
runtime = Runtime()
660-
assert not runtime.cache_dir
659+
runtime = Runtime(isolated=False)
661660
runtime.require_collection("community.molecule", "0.1.0", install=True)
662661

663662

@@ -1024,6 +1023,11 @@ def test_runtime_has_playbook() -> None:
10241023
"""Tests has_playbook method."""
10251024
runtime = Runtime(require_module=True)
10261025

1026+
runtime.prepare_environment(
1027+
required_collections={"community.molecule": "0.1.0"},
1028+
install_local=True,
1029+
)
1030+
10271031
assert not runtime.has_playbook("this-does-not-exist.yml")
10281032
# call twice to ensure cache is used:
10291033
assert not runtime.has_playbook("this-does-not-exist.yml")

test/test_runtime_scan_path.py

Lines changed: 18 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22

33
import json
44
import textwrap
5-
from dataclasses import dataclass, fields
65
from pathlib import Path
76

87
import pytest
@@ -19,26 +18,11 @@
1918
V2_COLLECTION_FULL_NAME = f"{V2_COLLECTION_NAMESPACE}.{V2_COLLECTION_NAME}"
2019

2120

22-
@dataclass
23-
class ScanSysPath:
24-
"""Parameters for scan tests."""
25-
26-
scan: bool
27-
raises_not_found: bool
28-
29-
def __str__(self) -> str:
30-
"""Return a string representation of the object."""
31-
parts = [
32-
f"{field.name}{str(getattr(self, field.name))[0]}" for field in fields(self)
33-
]
34-
return "-".join(parts)
35-
36-
3721
@pytest.mark.parametrize(
38-
("param"),
22+
("scan", "raises_not_found"),
3923
(
40-
ScanSysPath(scan=False, raises_not_found=True),
41-
ScanSysPath(scan=True, raises_not_found=False),
24+
pytest.param(False, True, id="0"),
25+
pytest.param(True, False, id="1"),
4226
),
4327
ids=str,
4428
)
@@ -47,16 +31,23 @@ def test_scan_sys_path(
4731
monkeypatch: MonkeyPatch,
4832
runtime_tmp: Runtime,
4933
tmp_path: Path,
50-
param: ScanSysPath,
34+
scan: bool,
35+
raises_not_found: bool,
5136
) -> None:
5237
"""Confirm sys path is scanned for collections.
5338
54-
:param venv_module: Fixture for a virtual environment
55-
:param monkeypatch: Fixture for monkeypatching
56-
:param runtime_tmp: Fixture for a Runtime object
57-
:param tmp_dir: Fixture for a temporary directory
58-
:param param: The parameters for the test
39+
Args:
40+
venv_module: Fixture for a virtual environment
41+
monkeypatch: Fixture for monkeypatching
42+
runtime_tmp: Fixture for a Runtime object
43+
tmp_path: Fixture for a temporary directory
44+
scan: Whether to scan the sys path
45+
raises_not_found: Whether the collection is expected to be found
5946
"""
47+
# Isolated the test from the others, so ansible will not find collections
48+
# that might be installed by other tests.
49+
monkeypatch.setenv("VIRTUAL_ENV", venv_module.project.as_posix())
50+
monkeypatch.setenv("ANSIBLE_HOME", tmp_path.as_posix())
6051
first_site_package_dir = venv_module.site_package_dirs()[0]
6152

6253
installed_to = (
@@ -76,7 +67,7 @@ def test_scan_sys_path(
7667
# Confirm the collection is installed
7768
assert installed_to.exists()
7869
# Set the sys scan path environment variable
79-
monkeypatch.setenv("ANSIBLE_COLLECTIONS_SCAN_SYS_PATH", str(param.scan))
70+
monkeypatch.setenv("ANSIBLE_COLLECTIONS_SCAN_SYS_PATH", str(scan))
8071
# Set the ansible collections paths to avoid bleed from other tests
8172
monkeypatch.setenv("ANSIBLE_COLLECTIONS_PATH", str(tmp_path))
8273

@@ -91,7 +82,7 @@ def test_scan_sys_path(
9182
)
9283

9384
proc = venv_module.python_script_run(script)
94-
if param.raises_not_found:
85+
if raises_not_found:
9586
assert proc.returncode != 0, (proc.stdout, proc.stderr)
9687
assert "InvalidPrerequisiteError" in proc.stderr
9788
assert "'community.molecule' not found" in proc.stderr

tox.ini

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,7 @@ passenv =
7474
LANG
7575
LC_*
7676
setenv =
77+
ANSIBLE_HOME = {envdir}/.ansible
7778
ANSIBLE_DEVEL_WARNING='false'
7879
COVERAGE_FILE = {env:COVERAGE_FILE:{envdir}/.coverage.{envname}}
7980
COVERAGE_PROCESS_START={toxinidir}/pyproject.toml

0 commit comments

Comments
 (0)