# Copyright 2026 IPSL / CNRS / Sorbonne University
# Authors: Kazem Ardaneh
#
# This work is licensed under the Creative Commons
# Attribution-NonCommercial-ShareAlike 4.0 International License.
# To view a copy of this license, visit
# http://creativecommons.org/licenses/by-nc-sa/4.0/
import os
from typing import Any
import unittest
import tempfile
import shutil
[docs]
class EasyDict(dict):
"""
A dictionary subclass that allows for attribute-style access to its items.
This class extends the built-in dict and overrides the __getattr__, __setattr__, and __delattr__ methods to enable accessing dictionary keys as attributes.
Original work: Copyright (c) 2022, NVIDIA CORPORATION & AFFILIATES.
Original source: https://github.com/NVlabs/edm
"""
def __getattr__(self, name: str) -> Any:
try:
return self[name]
except KeyError:
raise AttributeError(name)
def __setattr__(self, name: str, value: Any) -> None:
self[name] = value
def __delattr__(self, name: str) -> None:
del self[name]
[docs]
class FileUtils:
"""
Utility class for file and directory operations.
"""
[docs]
def __init__(self):
"""
Initialize the FileUtils class. This class does not maintain any state, so the constructor is empty.
"""
super().__init__()
[docs]
@staticmethod
def makedir(dirs):
"""
Create a directory if it does not exist.
Parameters
----------
dirs : str
The path of the directory to be created.
"""
if not os.path.exists(dirs):
os.makedirs(dirs)
[docs]
@staticmethod
def makefile(dirs, filename):
"""
Create an empty file in the specified directory.
Parameters
----------
dirs : str
The path of the directory where the file will be created.
filename : str
The name of the file to be created.
"""
filepath = os.path.join(dirs, filename)
with open(filepath, "a"):
pass
# ============================================================================
# Unit Tests for EasyDict
# ============================================================================
[docs]
class TestEasyDict(unittest.TestCase):
"""Unit tests for EasyDict class."""
[docs]
def __init__(self, methodName="runTest", logger=None):
super().__init__(methodName)
self.logger = logger
[docs]
def setUp(self):
"""Set up test fixtures."""
if self.logger:
self.logger.info("Test setup - EasyDict tests")
# ------------------------------------------------------------------------
# Initialization Tests
# ------------------------------------------------------------------------
[docs]
def test_empty_initialization(self):
"""Test initializing an empty EasyDict."""
if self.logger:
self.logger.info("Testing empty EasyDict initialization")
ed = EasyDict()
self.assertIsInstance(ed, dict)
self.assertIsInstance(ed, EasyDict)
self.assertEqual(len(ed), 0)
if self.logger:
self.logger.info("✅ empty initialization test passed")
[docs]
def test_initialization_with_dict(self):
"""Test initializing EasyDict with a dictionary."""
if self.logger:
self.logger.info("Testing EasyDict initialization with dictionary")
data = {"key1": "value1", "key2": 42, "key3": [1, 2, 3]}
ed = EasyDict(data)
self.assertEqual(ed["key1"], "value1")
self.assertEqual(ed["key2"], 42)
self.assertEqual(ed["key3"], [1, 2, 3])
self.assertEqual(len(ed), 3)
if self.logger:
self.logger.info("✅ initialization with dict test passed")
[docs]
def test_initialization_with_kwargs(self):
"""Test initializing EasyDict with keyword arguments."""
if self.logger:
self.logger.info("Testing EasyDict initialization with kwargs")
ed = EasyDict(key1="value1", key2=42, key3=[1, 2, 3])
self.assertEqual(ed["key1"], "value1")
self.assertEqual(ed["key2"], 42)
self.assertEqual(ed["key3"], [1, 2, 3])
self.assertEqual(len(ed), 3)
if self.logger:
self.logger.info("✅ initialization with kwargs test passed")
# ------------------------------------------------------------------------
# Attribute Access Tests
# ------------------------------------------------------------------------
[docs]
def test_attribute_get_set(self):
"""Test getting and setting attributes with dot notation."""
if self.logger:
self.logger.info("Testing attribute get/set with dot notation")
ed = EasyDict()
# Set attributes
ed.name = "test_name"
ed.value = 100
ed.nested = {"a": 1, "b": 2}
# Get attributes
self.assertEqual(ed.name, "test_name")
self.assertEqual(ed.value, 100)
self.assertEqual(ed.nested, {"a": 1, "b": 2})
# Verify dictionary access also works
self.assertEqual(ed["name"], "test_name")
self.assertEqual(ed["value"], 100)
self.assertEqual(ed["nested"], {"a": 1, "b": 2})
if self.logger:
self.logger.info("✅ attribute get/set test passed")
[docs]
def test_dict_get_set(self):
"""Test getting and setting with dictionary notation."""
if self.logger:
self.logger.info("Testing dictionary get/set with bracket notation")
ed = EasyDict()
# Set with bracket notation
ed["name"] = "test_name"
ed["value"] = 100
# Get with bracket notation
self.assertEqual(ed["name"], "test_name")
self.assertEqual(ed["value"], 100)
# Verify attribute access also works
self.assertEqual(ed.name, "test_name")
self.assertEqual(ed.value, 100)
if self.logger:
self.logger.info("✅ dictionary get/set test passed")
[docs]
def test_mixed_access(self):
"""Test mixing dot and bracket notation."""
if self.logger:
self.logger.info("Testing mixed dot and bracket notation")
ed = EasyDict()
# Set with dot, get with bracket
ed.key1 = "value1"
self.assertEqual(ed["key1"], "value1")
# Set with bracket, get with dot
ed["key2"] = "value2"
self.assertEqual(ed.key2, "value2")
if self.logger:
self.logger.info("✅ mixed access test passed")
[docs]
def test_attribute_error_for_nonexistent(self):
"""Test that accessing nonexistent attribute raises AttributeError."""
if self.logger:
self.logger.info("Testing AttributeError for nonexistent attribute")
ed = EasyDict()
with self.assertRaises(AttributeError):
_ = ed.nonexistent_attribute
if self.logger:
self.logger.info("✅ AttributeError test passed")
[docs]
def test_key_error_for_nonexistent(self):
"""Test that accessing nonexistent key raises KeyError."""
if self.logger:
self.logger.info("Testing KeyError for nonexistent key")
ed = EasyDict()
with self.assertRaises(KeyError):
_ = ed["nonexistent_key"]
if self.logger:
self.logger.info("✅ KeyError test passed")
[docs]
def test_delattr(self):
"""Test deleting attributes with delattr."""
if self.logger:
self.logger.info("Testing delattr functionality")
ed = EasyDict()
ed.key = "value"
self.assertIn("key", ed)
del ed.key
self.assertNotIn("key", ed)
with self.assertRaises(AttributeError):
_ = ed.key
if self.logger:
self.logger.info("✅ delattr test passed")
[docs]
def test_delitem(self):
"""Test deleting items with del."""
if self.logger:
self.logger.info("Testing delitem functionality")
ed = EasyDict()
ed["key"] = "value"
self.assertIn("key", ed)
del ed["key"]
self.assertNotIn("key", ed)
with self.assertRaises(KeyError):
_ = ed["key"]
if self.logger:
self.logger.info("✅ delitem test passed")
# ------------------------------------------------------------------------
# Dictionary Method Tests
# ------------------------------------------------------------------------
[docs]
def test_dict_methods(self):
"""Test that standard dictionary methods work."""
if self.logger:
self.logger.info("Testing dictionary methods")
ed = EasyDict(a=1, b=2, c=3)
# keys()
keys = ed.keys()
self.assertIn("a", keys)
self.assertIn("b", keys)
self.assertIn("c", keys)
self.assertEqual(len(keys), 3)
# values()
values = ed.values()
self.assertIn(1, values)
self.assertIn(2, values)
self.assertIn(3, values)
# items()
items = ed.items()
self.assertEqual(len(items), 3)
# get()
self.assertEqual(ed.get("a"), 1)
self.assertEqual(ed.get("nonexistent", "default"), "default")
# update()
ed.update({"d": 4, "e": 5})
self.assertEqual(ed.d, 4)
self.assertEqual(ed.e, 5)
# pop()
value = ed.pop("a")
self.assertEqual(value, 1)
self.assertNotIn("a", ed)
if self.logger:
self.logger.info("✅ dictionary methods test passed")
[docs]
def test_nested_easydict(self):
"""Test that nested dictionaries are not automatically converted."""
if self.logger:
self.logger.info("Testing nested dictionary behavior")
ed = EasyDict()
ed.nested = {"inner": "value"}
# Nested dict should be a regular dict, not EasyDict
self.assertIsInstance(ed.nested, dict)
self.assertNotIsInstance(ed.nested, EasyDict)
# But we can still access it
self.assertEqual(ed.nested["inner"], "value")
if self.logger:
self.logger.info("✅ nested dictionary test passed")
[docs]
def test_easydict_with_easydict(self):
"""Test nesting EasyDict inside EasyDict."""
if self.logger:
self.logger.info("Testing nesting EasyDict inside EasyDict")
inner = EasyDict(x=1, y=2)
outer = EasyDict()
outer.inner = inner
self.assertIsInstance(outer.inner, EasyDict)
self.assertEqual(outer.inner.x, 1)
self.assertEqual(outer.inner.y, 2)
if self.logger:
self.logger.info("✅ nested EasyDict test passed")
[docs]
def tearDown(self):
"""Clean up after tests."""
if self.logger:
self.logger.info("Test teardown - EasyDict tests completed")
# ============================================================================
# Unit Tests for FileUtils
# ============================================================================
[docs]
class TestFileUtils(unittest.TestCase):
"""Unit tests for FileUtils class."""
[docs]
def __init__(self, methodName="runTest", logger=None):
super().__init__(methodName)
self.logger = logger
[docs]
def setUp(self):
"""Set up test fixtures."""
self.temp_dir = tempfile.mkdtemp()
if self.logger:
self.logger.info(f"Test setup - created temp directory: {self.temp_dir}")
# ------------------------------------------------------------------------
# makedir Tests
# ------------------------------------------------------------------------
[docs]
def test_makedir_new_directory(self):
"""Test creating a new directory that doesn't exist."""
if self.logger:
self.logger.info("Testing makedir with new directory")
new_dir = os.path.join(self.temp_dir, "new_subdir", "nested", "path")
self.assertFalse(os.path.exists(new_dir))
FileUtils.makedir(new_dir)
self.assertTrue(os.path.exists(new_dir))
self.assertTrue(os.path.isdir(new_dir))
if self.logger:
self.logger.info(
f"✅ makedir new directory test passed - created: {new_dir}"
)
[docs]
def test_makedir_existing_directory(self):
"""Test calling makedir on an existing directory."""
if self.logger:
self.logger.info("Testing makedir with existing directory")
existing_dir = os.path.join(self.temp_dir, "existing_dir")
os.makedirs(existing_dir)
self.assertTrue(os.path.exists(existing_dir))
# This should not raise an exception
FileUtils.makedir(existing_dir)
self.assertTrue(os.path.exists(existing_dir))
if self.logger:
self.logger.info("✅ makedir existing directory test passed")
[docs]
def test_makedir_multiple_nested_directories(self):
"""Test creating multiple nested directories at once."""
if self.logger:
self.logger.info("Testing makedir with multiple nested directories")
nested_path = os.path.join(
self.temp_dir, "level1", "level2", "level3", "level4"
)
FileUtils.makedir(nested_path)
self.assertTrue(os.path.exists(nested_path))
self.assertTrue(os.path.isdir(os.path.join(self.temp_dir, "level1")))
self.assertTrue(os.path.isdir(os.path.join(self.temp_dir, "level1", "level2")))
self.assertTrue(
os.path.isdir(os.path.join(self.temp_dir, "level1", "level2", "level3"))
)
if self.logger:
self.logger.info("✅ makedir nested directories test passed")
# ------------------------------------------------------------------------
# makefile Tests
# ------------------------------------------------------------------------
[docs]
def test_makefile_new_file(self):
"""Test creating a new file in an existing directory."""
if self.logger:
self.logger.info("Testing makefile with new file")
filename = "test_file.txt"
expected_path = os.path.join(self.temp_dir, filename)
self.assertFalse(os.path.exists(expected_path))
FileUtils.makefile(self.temp_dir, filename)
self.assertTrue(os.path.exists(expected_path))
self.assertTrue(os.path.isfile(expected_path))
# Check file size (should be empty)
self.assertEqual(os.path.getsize(expected_path), 0)
if self.logger:
self.logger.info(
f"✅ makefile new file test passed - created: {expected_path}"
)
[docs]
def test_makefile_in_nonexistent_directory(self):
"""Test creating a file in a directory that doesn't exist."""
if self.logger:
self.logger.info("Testing makefile in nonexistent directory")
nonexistent_dir = os.path.join(self.temp_dir, "does", "not", "exist")
filename = "test.txt"
# This should fail because the directory doesn't exist
with self.assertRaises(FileNotFoundError):
FileUtils.makefile(nonexistent_dir, filename)
if self.logger:
self.logger.info("✅ makefile nonexistent directory test passed")
[docs]
def test_makefile_existing_file(self):
"""Test creating a file that already exists."""
if self.logger:
self.logger.info("Testing makefile with existing file")
filename = "existing.txt"
filepath = os.path.join(self.temp_dir, filename)
# Create the file first
with open(filepath, "w") as f:
f.write("some content")
original_size = os.path.getsize(filepath)
self.assertGreater(original_size, 0)
# Call makefile - should open in append mode, not overwrite
FileUtils.makefile(self.temp_dir, filename)
# File should still exist and have the same content
self.assertTrue(os.path.exists(filepath))
new_size = os.path.getsize(filepath)
self.assertEqual(new_size, original_size) # Size unchanged (appended nothing)
if self.logger:
self.logger.info("✅ makefile existing file test passed")
[docs]
def test_makefile_multiple_files(self):
"""Test creating multiple files."""
if self.logger:
self.logger.info("Testing makefile with multiple files")
filenames = ["file1.txt", "file2.log", "file3.data", "file4.csv"]
for filename in filenames:
FileUtils.makefile(self.temp_dir, filename)
expected_path = os.path.join(self.temp_dir, filename)
self.assertTrue(os.path.exists(expected_path))
self.assertTrue(os.path.isfile(expected_path))
# Verify all files were created
files_in_dir = os.listdir(self.temp_dir)
self.assertEqual(len(files_in_dir), len(filenames))
for filename in filenames:
self.assertIn(filename, files_in_dir)
if self.logger:
self.logger.info(
f"✅ makefile multiple files test passed - created {len(filenames)} files"
)
# ------------------------------------------------------------------------
# Combined Tests
# ------------------------------------------------------------------------
[docs]
def test_makedir_then_makefile(self):
"""Test creating a directory then a file inside it."""
if self.logger:
self.logger.info("Testing makedir then makefile sequence")
new_dir = os.path.join(self.temp_dir, "new_directory")
filename = "file_in_new_dir.txt"
expected_path = os.path.join(new_dir, filename)
# Directory doesn't exist yet
self.assertFalse(os.path.exists(new_dir))
# Create directory
FileUtils.makedir(new_dir)
self.assertTrue(os.path.exists(new_dir))
# Create file in that directory
FileUtils.makefile(new_dir, filename)
self.assertTrue(os.path.exists(expected_path))
self.assertTrue(os.path.isfile(expected_path))
if self.logger:
self.logger.info("✅ makedir then makefile test passed")
[docs]
def tearDown(self):
"""Clean up after tests."""
shutil.rmtree(self.temp_dir)
if self.logger:
self.logger.info(f"Test teardown - removed temp directory: {self.temp_dir}")