Simon Strandgaard
commited on
Commit
·
e49f146
1
Parent(s):
bd55e57
purge_old_runs() added
Browse files- src/purge/__init__.py +0 -0
- src/purge/purge_old_runs.py +35 -0
- src/purge/tests/__init__.py +0 -0
- src/purge/tests/test_purge_old_runs.py +85 -0
src/purge/__init__.py
ADDED
File without changes
|
src/purge/purge_old_runs.py
ADDED
@@ -0,0 +1,35 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import logging
|
2 |
+
import os
|
3 |
+
import shutil
|
4 |
+
import datetime
|
5 |
+
|
6 |
+
logger = logging.getLogger(__name__)
|
7 |
+
|
8 |
+
def purge_old_runs(run_dir: str, max_age_hours: float = 1.0) -> None:
|
9 |
+
"""
|
10 |
+
Deletes runs in the specified run_dir older than max_age_hours.
|
11 |
+
"""
|
12 |
+
# raise exception if the run_dir is not an absolute path
|
13 |
+
if not os.path.isabs(run_dir):
|
14 |
+
raise ValueError(f"run_dir must be an absolute path: {run_dir}")
|
15 |
+
|
16 |
+
now = datetime.datetime.now()
|
17 |
+
cutoff = now - datetime.timedelta(hours=max_age_hours)
|
18 |
+
|
19 |
+
for run_id in os.listdir(run_dir):
|
20 |
+
run_path = os.path.join(run_dir, run_id)
|
21 |
+
if not os.path.isdir(run_path):
|
22 |
+
continue # Skip files
|
23 |
+
|
24 |
+
try:
|
25 |
+
# Get the modification time of the directory
|
26 |
+
mtime = datetime.datetime.fromtimestamp(os.path.getmtime(run_path))
|
27 |
+
|
28 |
+
if mtime < cutoff:
|
29 |
+
logger.debug(f"Deleting old run: {run_id} from {run_dir}")
|
30 |
+
shutil.rmtree(run_path) # Delete the directory and all its contents
|
31 |
+
else:
|
32 |
+
logger.debug(f"Skipping {run_id} in {run_dir}, last modified: {mtime}")
|
33 |
+
|
34 |
+
except Exception as e:
|
35 |
+
logger.error(f"Error processing {run_id} in {run_dir}: {e}")
|
src/purge/tests/__init__.py
ADDED
File without changes
|
src/purge/tests/test_purge_old_runs.py
ADDED
@@ -0,0 +1,85 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import unittest
|
2 |
+
import os
|
3 |
+
import shutil
|
4 |
+
import time
|
5 |
+
from src.purge.purge_old_runs import purge_old_runs
|
6 |
+
|
7 |
+
class TestPurgeOldRuns(unittest.TestCase):
|
8 |
+
def setUp(self):
|
9 |
+
"""
|
10 |
+
Set up test environment before each test.
|
11 |
+
"""
|
12 |
+
# Create a temporary directory for the runs
|
13 |
+
self.test_run_dir = os.path.abspath(os.path.join(os.path.dirname(__file__), "test_run"))
|
14 |
+
if os.path.exists(self.test_run_dir):
|
15 |
+
shutil.rmtree(self.test_run_dir)
|
16 |
+
os.makedirs(self.test_run_dir, exist_ok=True)
|
17 |
+
|
18 |
+
# Create some dummy run directories with different modification times
|
19 |
+
self.create_dummy_run("run1", hours_old=0.5)
|
20 |
+
self.create_dummy_run("run2", hours_old=1.5) # Should be purged
|
21 |
+
self.create_dummy_run("run3", hours_old=2) # Should be purged
|
22 |
+
self.create_dummy_run("run4", hours_old=0.25)
|
23 |
+
self.create_dummy_run("run5", hours_old=1) # Boundary condition, might be purged
|
24 |
+
self.create_dummy_run("run6", hours_old=0) # Today
|
25 |
+
self.create_dummy_file("not_a_run.txt") # a file that should be left alone
|
26 |
+
|
27 |
+
def tearDown(self):
|
28 |
+
"""
|
29 |
+
Clean up test environment after each test.
|
30 |
+
"""
|
31 |
+
# Remove the temporary run directory and its contents
|
32 |
+
if os.path.exists(self.test_run_dir):
|
33 |
+
shutil.rmtree(self.test_run_dir)
|
34 |
+
|
35 |
+
def create_dummy_run(self, run_id, hours_old):
|
36 |
+
"""
|
37 |
+
Creates a dummy run directory with a specific modification time.
|
38 |
+
"""
|
39 |
+
run_path = os.path.join(self.test_run_dir, run_id)
|
40 |
+
os.makedirs(run_path, exist_ok=True)
|
41 |
+
|
42 |
+
# Set the modification time of the directory
|
43 |
+
mtime = time.time() - (hours_old * 3600) # seconds
|
44 |
+
os.utime(run_path, (mtime, mtime))
|
45 |
+
|
46 |
+
def create_dummy_file(self, filename):
|
47 |
+
"""Create a dummy file in the test directory."""
|
48 |
+
filepath = os.path.join(self.test_run_dir, filename)
|
49 |
+
with open(filepath, "w") as f:
|
50 |
+
f.write("This is a dummy file.")
|
51 |
+
|
52 |
+
def test_purge_old_runs(self):
|
53 |
+
"""Tests the purge_old_runs function."""
|
54 |
+
max_age_hours = 0.95
|
55 |
+
purge_old_runs(self.test_run_dir, max_age_hours=max_age_hours) # Pass the directory
|
56 |
+
|
57 |
+
# Check which runs should have been purged
|
58 |
+
runs_to_keep = ["run1", "run4", "run6", "not_a_run.txt"]
|
59 |
+
runs_to_purge = ["run2", "run3", "run5"]
|
60 |
+
|
61 |
+
for run_id in runs_to_keep:
|
62 |
+
run_path = os.path.join(self.test_run_dir, run_id)
|
63 |
+
self.assertTrue(os.path.exists(run_path), f"Run {run_id} should not have been purged.")
|
64 |
+
|
65 |
+
for run_id in runs_to_purge:
|
66 |
+
run_path = os.path.join(self.test_run_dir, run_id)
|
67 |
+
self.assertFalse(os.path.exists(run_path), f"Run {run_id} should have been purged.")
|
68 |
+
|
69 |
+
def test_purge_no_runs(self):
|
70 |
+
"""Test when no runs are older than the max_age_hours."""
|
71 |
+
|
72 |
+
# Set all runs to be very recent.
|
73 |
+
for item in os.listdir(self.test_run_dir):
|
74 |
+
item_path = os.path.join(self.test_run_dir, item)
|
75 |
+
mtime = time.time() # Now
|
76 |
+
os.utime(item_path, (mtime, mtime))
|
77 |
+
|
78 |
+
max_age_hours = 1.0
|
79 |
+
purge_old_runs(self.test_run_dir, max_age_hours=max_age_hours) # Pass the directory
|
80 |
+
|
81 |
+
# All runs should still exist.
|
82 |
+
expected_runs = ["run1", "run2", "run3", "run4", "run5", "run6", "not_a_run.txt"]
|
83 |
+
for run_id in expected_runs:
|
84 |
+
run_path = os.path.join(self.test_run_dir, run_id)
|
85 |
+
self.assertTrue(os.path.exists(run_path), f"Run {run_id} should not have been purged.")
|