102 lines
4.0 KiB
Python
102 lines
4.0 KiB
Python
import os
|
|
import importlib.util
|
|
from pathlib import Path
|
|
import json
|
|
|
|
|
|
class Config(dict):
|
|
"""A dictionary that allows dot notation access for configuration settings."""
|
|
def __init__(self, data=None):
|
|
super().__init__()
|
|
if data:
|
|
for key, value in data.items():
|
|
# Set the key-value pair using the key and the converted value
|
|
self[key] = self._convert(value)
|
|
|
|
def _convert(self, value):
|
|
"""Recursively convert nested dictionaries to Config."""
|
|
if isinstance(value, dict):
|
|
return Config(value) # Convert all nested dicts to Config
|
|
elif isinstance(value, list):
|
|
return [self._convert(item) for item in value] # Convert items in lists
|
|
elif isinstance(value, Path):
|
|
return str(value) # Convert Path objects to string
|
|
return value
|
|
|
|
def __getattr__(self, item):
|
|
"""Allow access to dictionary keys via dot notation."""
|
|
if item in self:
|
|
return self[item]
|
|
raise AttributeError(f"'{self.__class__.__name__}' object has no attribute '{item}'")
|
|
|
|
def __setattr__(self, key, value):
|
|
"""Allow setting dictionary keys via dot notation."""
|
|
self[key] = self._convert(value)
|
|
|
|
@classmethod
|
|
def load_config(cls, file_path):
|
|
"""Load a Python config file and return it as a Config instance."""
|
|
spec = importlib.util.spec_from_file_location("config_module", file_path)
|
|
config_module = importlib.util.module_from_spec(spec)
|
|
spec.loader.exec_module(config_module)
|
|
|
|
if hasattr(config_module, "config") and isinstance(config_module.config, dict):
|
|
return cls(config_module.config) # Ensure full conversion of nested dicts
|
|
else:
|
|
raise ValueError("The config file does not define a 'config' dictionary.")
|
|
|
|
def update_config(self, new_data):
|
|
"""Recursively update Config with new dictionary values."""
|
|
for key, value in new_data.items():
|
|
if isinstance(value, dict) and isinstance(self.get(key), Config):
|
|
self[key].update_config(value) # Recursive update for nested dicts
|
|
else:
|
|
self[key] = self._convert(value) # Convert and assign
|
|
|
|
def dump_config(self, file_path=None):
|
|
"""Dump the Config object into a .py file with a Pythonic and readable format."""
|
|
# Ensure the provided path is valid
|
|
if file_path is None:
|
|
file_path = self.log.output_dir + '/config.py'
|
|
else:
|
|
dir_name = os.path.dirname(file_path)
|
|
if dir_name and not os.path.exists(dir_name):
|
|
os.makedirs(dir_name)
|
|
|
|
# Convert the Config instance into a regular dictionary
|
|
def config_to_dict(config):
|
|
"""Recursively convert a Config instance into a regular dictionary."""
|
|
if isinstance(config, Config):
|
|
return {key: config_to_dict(value) if isinstance(value, Config) else value
|
|
for key, value in config.items()}
|
|
return config
|
|
|
|
config_dict = config_to_dict(self)
|
|
|
|
# Write the config dictionary to a .py file in a formatted, readable way
|
|
with open(file_path, 'w') as f:
|
|
# Use json.dumps to pretty-print the dictionary with indentation and spaces
|
|
f.write("config = ")
|
|
f.write(json.dumps(config_dict, indent=4)) # Pretty print with indentation
|
|
f.write("\n")
|
|
|
|
print(f"Config has been saved to {file_path}")
|
|
|
|
def prepare_log(self):
|
|
|
|
def make_folder(folder):
|
|
if not os.path.exists(folder):
|
|
os.makedirs(folder)
|
|
|
|
if self.log.output_dir is not None:
|
|
make_folder(self.log.output_dir)
|
|
if self.log.model_dir is not None:
|
|
make_folder(self.log.model_dir)
|
|
if self.log.log_dir is not None:
|
|
make_folder(self.log.log_dir)
|
|
if self.log.result_dir is not None:
|
|
make_folder(self.log.result_dir)
|
|
|
|
|
|
|