Files
minicalosim/bind/minicalo_tools.py
2024-12-13 12:33:43 +01:00

198 lines
6.9 KiB
Python

# this will be shared between isolated parts and exposed parts
import os
import tempfile
import pickle
import weakref
import atexit
class TempFileManager:
_instances = weakref.WeakSet() # Track instances for cleanup on program exit
def __init__(self, input_file=None, output_file=None, use_shm=True, file_permissions=0o644, slave = False):
"""
Initialize the TempFileManager.
:param input_file: Path to the input file (optional).
:param output_file: Path to the output file (optional).
:param use_shm: Whether to attempt using /dev/shm for temporary files.
"""
self.input_file = input_file
self.output_file = output_file
self.use_shm = use_shm and os.path.exists('/dev/shm')
self.file_permissions = file_permissions
self.slave = slave
if not self.slave:
# Register this instance for cleanup
TempFileManager._instances.add(self)
@staticmethod
def slave_init(input_file, output_file):
"""
Initialize a TempFileManager instance for use with external filenames.
:param input_file: Path to the input file.
:param output_file: Path to the output file.
:return: A TempFileManager instance with the provided filenames.
"""
return TempFileManager(input_file=input_file, output_file=output_file, slave=True)
def __enter__(self):
"""
Create temporary files when entering the context, if not already provided.
"""
if not self.input_file:
base_dir = '/dev/shm' if self.use_shm else None
self.input_file = tempfile.NamedTemporaryFile(delete=False, dir=base_dir, suffix=".pkl").name
if not self.output_file:
base_dir = '/dev/shm' if self.use_shm else None
self.output_file = tempfile.NamedTemporaryFile(delete=False, dir=base_dir, suffix=".pkl").name
return self
def dump_input(self, obj):
"""
Serialize and write the object to the input file.
:param obj: The object to serialize and write.
"""
with open(self.input_file, 'wb') as f:
pickle.dump(obj, f)
os.chmod(self.input_file, self.file_permissions)
def read_input(self):
"""
Read and deserialize the input file.
:return: The deserialized object.
"""
with open(self.input_file, 'rb') as f:
return pickle.load(f)
def dump_output(self, obj):
"""
Serialize and write the object to the output file.
:param obj: The object to serialize and write.
"""
with open(self.output_file, 'wb') as f:
pickle.dump(obj, f)
os.chmod(self.output_file, self.file_permissions)
def read_output(self):
"""
Read and deserialize the output file.
:return: The deserialized object.
"""
with open(self.output_file, 'rb') as f:
return pickle.load(f)
def cleanup(self):
"""
Cleanup temporary files when exiting the context.
"""
for file_path in [self.input_file, self.output_file]:
if file_path and os.path.exists(file_path):
try:
os.remove(file_path)
except OSError:
pass
def __exit__(self, exc_type, exc_val, exc_tb):
self.cleanup()
# Ensure all instances are cleaned up on exit
@atexit.register
def cleanup_all_temp_files():
for instance in TempFileManager._instances:
instance.cleanup()
import unittest
import os
import json
import stat
class TestTempFileManager(unittest.TestCase):
def test_file_creation_and_cleanup(self):
"""Test that temporary files are created and cleaned up properly."""
with TempFileManager() as tfm:
# Check if input and output files are created
self.assertTrue(os.path.exists(tfm.input_file))
self.assertTrue(os.path.exists(tfm.output_file))
input_path = tfm.input_file
output_path = tfm.output_file
# After context exit, files should be deleted
self.assertFalse(os.path.exists(input_path))
self.assertFalse(os.path.exists(output_path))
def test_dump_and_read(self):
"""Test dumping an object to input and reading it back."""
obj = {"key": "value", "number": 42}
with TempFileManager() as tfm:
# Dump object to input file
tfm.dump_input(obj)
# Read it back to ensure correctness
with open(tfm.input_file, 'rb') as f:
data = pickle.load(f)
self.assertEqual(data, obj)
def test_slave_init(self):
"""Test the slave_init static method for external file names."""
input_file = "/tmp/test_input.json"
output_file = "/tmp/test_output.json"
try:
# Create test input file
obj = {"key": "test"}
with open(input_file, 'wb') as f:
pickle.dump(obj, f)
# Initialize TempFileManager with slave_init
tfm = TempFileManager.slave_init(input_file, output_file)
# Read the input file and verify content
data = tfm.read_input()
self.assertEqual(data, obj)
# Write to the output file and verify
updated_obj = {"key": "updated"}
tfm.dump_output(updated_obj)
with open(output_file, 'rb') as f:
output_data = pickle.load(f)
self.assertEqual(output_data, updated_obj)
finally:
# Cleanup test files
if os.path.exists(input_file):
os.unlink(input_file)
if os.path.exists(output_file):
os.unlink(output_file)
def test_with_dev_shm(self):
"""Test that /dev/shm is used if available."""
if os.path.exists('/dev/shm'):
with TempFileManager() as tfm:
self.assertTrue(tfm.input_file.startswith('/dev/shm'))
self.assertTrue(tfm.output_file.startswith('/dev/shm'))
def test_without_dev_shm(self):
"""Test fallback to default location if /dev/shm is unavailable."""
with TempFileManager(use_shm=False) as tfm:
self.assertFalse(tfm.input_file.startswith('/dev/shm'))
self.assertFalse(tfm.output_file.startswith('/dev/shm'))
def test_permissions_after_dump(self):
"""Test that file permissions are correctly set after dumping content."""
with TempFileManager(file_permissions=0o600) as tfm:
# Dump content to the input file
tfm.dump_input({"key": "value"})
# Check that permissions are applied after dumping
permissions = stat.S_IMODE(os.stat(tfm.input_file).st_mode)
self.assertEqual(permissions, 0o600)
if __name__ == "__main__":
unittest.main()