computefence 0.1.2__tar.gz → 0.2.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {computefence-0.1.2 → computefence-0.2.0}/PKG-INFO +1 -1
- computefence-0.2.0/computefence/__init__.py +1 -0
- computefence-0.2.0/computefence/checks/storage.py +136 -0
- {computefence-0.1.2 → computefence-0.2.0}/computefence/cli.py +2 -2
- {computefence-0.1.2 → computefence-0.2.0}/computefence.egg-info/PKG-INFO +1 -1
- computefence-0.1.2/computefence/__init__.py +0 -1
- computefence-0.1.2/computefence/checks/storage.py +0 -64
- {computefence-0.1.2 → computefence-0.2.0}/LICENSE +0 -0
- {computefence-0.1.2 → computefence-0.2.0}/README.md +0 -0
- {computefence-0.1.2 → computefence-0.2.0}/computefence/checks/__init__.py +0 -0
- {computefence-0.1.2 → computefence-0.2.0}/computefence/checks/dataset.py +0 -0
- {computefence-0.1.2 → computefence-0.2.0}/computefence/checks/environment.py +0 -0
- {computefence-0.1.2 → computefence-0.2.0}/computefence.egg-info/SOURCES.txt +0 -0
- {computefence-0.1.2 → computefence-0.2.0}/computefence.egg-info/dependency_links.txt +0 -0
- {computefence-0.1.2 → computefence-0.2.0}/computefence.egg-info/entry_points.txt +0 -0
- {computefence-0.1.2 → computefence-0.2.0}/computefence.egg-info/requires.txt +0 -0
- {computefence-0.1.2 → computefence-0.2.0}/computefence.egg-info/top_level.txt +0 -0
- {computefence-0.1.2 → computefence-0.2.0}/pyproject.toml +0 -0
- {computefence-0.1.2 → computefence-0.2.0}/setup.cfg +0 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = "0.2.0"
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
import os
|
|
2
|
+
import shutil
|
|
3
|
+
from pathlib import Path
|
|
4
|
+
|
|
5
|
+
# Persistent volume mount points used by the common GPU rental providers.
|
|
6
|
+
MOUNT_CANDIDATES = ["/workspace", "/runpod-volume", "/vast"]
|
|
7
|
+
|
|
8
|
+
GB = 1024 ** 3
|
|
9
|
+
BLOCKER_BELOW_GB = 5
|
|
10
|
+
WARNING_BELOW_GB = 20
|
|
11
|
+
DISK_FIX = "Free up disk space or attach a larger volume before launching"
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def check_storage():
|
|
15
|
+
results = []
|
|
16
|
+
|
|
17
|
+
# Read HuggingFace cache environment variables
|
|
18
|
+
hf_home = os.environ.get("HF_HOME")
|
|
19
|
+
hf_hub_cache = os.environ.get("HF_HUB_CACHE")
|
|
20
|
+
xdg_cache = os.environ.get("XDG_CACHE_HOME")
|
|
21
|
+
|
|
22
|
+
# Detect mounted persistent volumes
|
|
23
|
+
mounted_volumes = []
|
|
24
|
+
for path in MOUNT_CANDIDATES:
|
|
25
|
+
if Path(path).exists():
|
|
26
|
+
mounted_volumes.append(path)
|
|
27
|
+
|
|
28
|
+
# Check HF_HOME
|
|
29
|
+
if hf_home is None:
|
|
30
|
+
if mounted_volumes:
|
|
31
|
+
results.append({
|
|
32
|
+
"status": "warn",
|
|
33
|
+
"message": f"HF_HOME is not set. Mounted volume detected at {mounted_volumes[0]} but HuggingFace will cache to default local path.",
|
|
34
|
+
"fix": f"export HF_HOME={mounted_volumes[0]}/.cache/huggingface"
|
|
35
|
+
})
|
|
36
|
+
else:
|
|
37
|
+
results.append({
|
|
38
|
+
"status": "warn",
|
|
39
|
+
"message": "HF_HOME is not set. HuggingFace will use default local cache.",
|
|
40
|
+
"fix": "Set HF_HOME to a persistent volume path before training"
|
|
41
|
+
})
|
|
42
|
+
elif hf_home.startswith("/root") or hf_home.startswith("/tmp"):
|
|
43
|
+
results.append({
|
|
44
|
+
"status": "warn",
|
|
45
|
+
"message": f"HF_HOME appears to point to ephemeral storage: {hf_home}",
|
|
46
|
+
"fix": f"export HF_HOME=/workspace/.cache/huggingface"
|
|
47
|
+
})
|
|
48
|
+
elif mounted_volumes and not any(hf_home.startswith(v) for v in mounted_volumes):
|
|
49
|
+
results.append({
|
|
50
|
+
"status": "warn",
|
|
51
|
+
"message": f"HF_HOME ({hf_home}) does not appear to be on a mounted volume.",
|
|
52
|
+
"fix": f"export HF_HOME={mounted_volumes[0]}/.cache/huggingface"
|
|
53
|
+
})
|
|
54
|
+
else:
|
|
55
|
+
results.append({
|
|
56
|
+
"status": "pass",
|
|
57
|
+
"message": f"HF_HOME is set to {hf_home}"
|
|
58
|
+
})
|
|
59
|
+
|
|
60
|
+
# Report mounted volumes found
|
|
61
|
+
if mounted_volumes:
|
|
62
|
+
results.append({
|
|
63
|
+
"status": "pass",
|
|
64
|
+
"message": f"Persistent volume(s) detected: {', '.join(mounted_volumes)}"
|
|
65
|
+
})
|
|
66
|
+
else:
|
|
67
|
+
results.append({
|
|
68
|
+
"status": "warn",
|
|
69
|
+
"message": "No mounted persistent volumes detected at /workspace, /runpod-volume, or /vast",
|
|
70
|
+
"fix": "Attach a network volume before training to persist checkpoints and cache"
|
|
71
|
+
})
|
|
72
|
+
|
|
73
|
+
return results
|
|
74
|
+
|
|
75
|
+
def check_disk_headroom():
|
|
76
|
+
"""Report free space on the workspace volume(s) and the root disk.
|
|
77
|
+
|
|
78
|
+
Uses shutil.disk_usage rather than shelling out to df: it returns the same
|
|
79
|
+
numbers as exact bytes, with no output parsing and no subprocess.
|
|
80
|
+
"""
|
|
81
|
+
results = []
|
|
82
|
+
|
|
83
|
+
# Build the target list, skipping any path that shares a device with one
|
|
84
|
+
# already queued so a single filesystem is not reported twice.
|
|
85
|
+
targets = []
|
|
86
|
+
seen_devices = set()
|
|
87
|
+
for path in MOUNT_CANDIDATES + ["/"]:
|
|
88
|
+
candidate = Path(path)
|
|
89
|
+
if not candidate.exists():
|
|
90
|
+
continue
|
|
91
|
+
try:
|
|
92
|
+
device = candidate.stat().st_dev
|
|
93
|
+
except OSError:
|
|
94
|
+
device = None
|
|
95
|
+
if device is not None:
|
|
96
|
+
if device in seen_devices:
|
|
97
|
+
continue
|
|
98
|
+
seen_devices.add(device)
|
|
99
|
+
targets.append(candidate)
|
|
100
|
+
|
|
101
|
+
for target in targets:
|
|
102
|
+
label = "Root disk (/)" if str(target) == "/" else f"Volume {target}"
|
|
103
|
+
|
|
104
|
+
try:
|
|
105
|
+
usage = shutil.disk_usage(target)
|
|
106
|
+
except OSError as e:
|
|
107
|
+
results.append({
|
|
108
|
+
"status": "warn",
|
|
109
|
+
"message": f"{label} — could not read disk usage: {e}",
|
|
110
|
+
"fix": DISK_FIX
|
|
111
|
+
})
|
|
112
|
+
continue
|
|
113
|
+
|
|
114
|
+
free_gb = usage.free / GB
|
|
115
|
+
total_gb = usage.total / GB
|
|
116
|
+
summary = f"{label} — {free_gb:.1f} GB free of {total_gb:.1f} GB"
|
|
117
|
+
|
|
118
|
+
if free_gb < BLOCKER_BELOW_GB:
|
|
119
|
+
results.append({
|
|
120
|
+
"status": "fail",
|
|
121
|
+
"message": f"{summary} (below {BLOCKER_BELOW_GB} GB)",
|
|
122
|
+
"fix": DISK_FIX
|
|
123
|
+
})
|
|
124
|
+
elif free_gb < WARNING_BELOW_GB:
|
|
125
|
+
results.append({
|
|
126
|
+
"status": "warn",
|
|
127
|
+
"message": f"{summary} (below {WARNING_BELOW_GB} GB)",
|
|
128
|
+
"fix": DISK_FIX
|
|
129
|
+
})
|
|
130
|
+
else:
|
|
131
|
+
results.append({
|
|
132
|
+
"status": "pass",
|
|
133
|
+
"message": summary
|
|
134
|
+
})
|
|
135
|
+
|
|
136
|
+
return results
|
|
@@ -3,7 +3,7 @@ from rich.console import Console
|
|
|
3
3
|
|
|
4
4
|
from computefence import __version__
|
|
5
5
|
from computefence.checks.environment import check_environment
|
|
6
|
-
from computefence.checks.storage import check_storage
|
|
6
|
+
from computefence.checks.storage import check_disk_headroom, check_storage
|
|
7
7
|
from computefence.checks.dataset import check_dataset
|
|
8
8
|
|
|
9
9
|
console = Console()
|
|
@@ -53,7 +53,7 @@ def doctor():
|
|
|
53
53
|
# Run every check up front so the verdict line can be counted and printed
|
|
54
54
|
# above the individual results.
|
|
55
55
|
env_results = check_environment()
|
|
56
|
-
storage_results = check_storage()
|
|
56
|
+
storage_results = check_storage() + check_disk_headroom()
|
|
57
57
|
dataset_results = check_dataset(
|
|
58
58
|
dataset_path=dataset,
|
|
59
59
|
input_column=input_column,
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
__version__ = "0.1.2"
|
|
@@ -1,64 +0,0 @@
|
|
|
1
|
-
import os
|
|
2
|
-
from pathlib import Path
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
def check_storage():
|
|
6
|
-
results = []
|
|
7
|
-
|
|
8
|
-
# Read HuggingFace cache environment variables
|
|
9
|
-
hf_home = os.environ.get("HF_HOME")
|
|
10
|
-
hf_hub_cache = os.environ.get("HF_HUB_CACHE")
|
|
11
|
-
xdg_cache = os.environ.get("XDG_CACHE_HOME")
|
|
12
|
-
|
|
13
|
-
# Detect mounted persistent volumes
|
|
14
|
-
mounted_volumes = []
|
|
15
|
-
for path in ["/workspace", "/runpod-volume", "/vast"]:
|
|
16
|
-
if Path(path).exists():
|
|
17
|
-
mounted_volumes.append(path)
|
|
18
|
-
|
|
19
|
-
# Check HF_HOME
|
|
20
|
-
if hf_home is None:
|
|
21
|
-
if mounted_volumes:
|
|
22
|
-
results.append({
|
|
23
|
-
"status": "warn",
|
|
24
|
-
"message": f"HF_HOME is not set. Mounted volume detected at {mounted_volumes[0]} but HuggingFace will cache to default local path.",
|
|
25
|
-
"fix": f"export HF_HOME={mounted_volumes[0]}/.cache/huggingface"
|
|
26
|
-
})
|
|
27
|
-
else:
|
|
28
|
-
results.append({
|
|
29
|
-
"status": "warn",
|
|
30
|
-
"message": "HF_HOME is not set. HuggingFace will use default local cache.",
|
|
31
|
-
"fix": "Set HF_HOME to a persistent volume path before training"
|
|
32
|
-
})
|
|
33
|
-
elif hf_home.startswith("/root") or hf_home.startswith("/tmp"):
|
|
34
|
-
results.append({
|
|
35
|
-
"status": "warn",
|
|
36
|
-
"message": f"HF_HOME appears to point to ephemeral storage: {hf_home}",
|
|
37
|
-
"fix": f"export HF_HOME=/workspace/.cache/huggingface"
|
|
38
|
-
})
|
|
39
|
-
elif mounted_volumes and not any(hf_home.startswith(v) for v in mounted_volumes):
|
|
40
|
-
results.append({
|
|
41
|
-
"status": "warn",
|
|
42
|
-
"message": f"HF_HOME ({hf_home}) does not appear to be on a mounted volume.",
|
|
43
|
-
"fix": f"export HF_HOME={mounted_volumes[0]}/.cache/huggingface"
|
|
44
|
-
})
|
|
45
|
-
else:
|
|
46
|
-
results.append({
|
|
47
|
-
"status": "pass",
|
|
48
|
-
"message": f"HF_HOME is set to {hf_home}"
|
|
49
|
-
})
|
|
50
|
-
|
|
51
|
-
# Report mounted volumes found
|
|
52
|
-
if mounted_volumes:
|
|
53
|
-
results.append({
|
|
54
|
-
"status": "pass",
|
|
55
|
-
"message": f"Persistent volume(s) detected: {', '.join(mounted_volumes)}"
|
|
56
|
-
})
|
|
57
|
-
else:
|
|
58
|
-
results.append({
|
|
59
|
-
"status": "warn",
|
|
60
|
-
"message": "No mounted persistent volumes detected at /workspace, /runpod-volume, or /vast",
|
|
61
|
-
"fix": "Attach a network volume before training to persist checkpoints and cache"
|
|
62
|
-
})
|
|
63
|
-
|
|
64
|
-
return results
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|