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.")
|