import os import json class Config: """ Manages application configuration stored in a JSON file. Provides attribute-style access to configuration values. """ def __init__(self, file_path='/data/config.json'): """ Initializes the Config manager. Loads configuration from the JSON file. Creates the file with an empty JSON object if it doesn't exist. Args: file_path (str): The path to the JSON configuration file. """ self.file_path = file_path self._config_data = {} # Internal dictionary to hold config data self._load_config() def _load_config(self): """Loads configuration key-value pairs from the JSON file.""" # Check if the file exists. If not, create it with an empty JSON object. if not os.path.exists(self.file_path): self._save_config() # Create the file with empty data try: with open(self.file_path, 'r') as f: # Load data from the file. Handle empty file case. content = f.read() if content: self._config_data = json.loads(content) else: self._config_data = {} # Initialize as empty if file is empty except (FileNotFoundError, json.JSONDecodeError) as e: print(f"Error loading config file {self.file_path}: {e}") # If there's an error loading, initialize with empty data self._config_data = {} # Optionally, you might want to back up the problematic file # or log the error more severely in a real application. def _save_config(self): """Saves the current configuration data to the JSON file.""" try: with open(self.file_path, 'w') as f: json.dump(self._config_data, f, indent=4) # Use indent for readability except IOError as e: print(f"Error saving config file {self.file_path}: {e}") except TypeError as e: print(f"Error serializing config data to JSON: {e}. Ensure all values are JSON serializable.") def get(self, key, default=None): """ Retrieves a configuration value by key. Args: key (str): The configuration key. default: The value to return if the key is not found. Returns: The configuration value or the default value if not found. """ return self._config_data.get(key, default) def set(self, key, value): """ Sets or updates a configuration value in memory and saves to the file. Args: key (str): The configuration key. value: The configuration value. Must be JSON serializable. """ self._config_data[key] = value self._save_config() def delete(self, key): """ Deletes a configuration key-value pair from memory and saves to the file. Args: key (str): The configuration key to delete. """ if key in self._config_data: del self._config_data[key] self._save_config() else: print(f"Warning: Key '{key}' not found in config.") def __str__(self): """ Returns a neat, formatted string representation of all configuration key-value pairs. This method is automatically called when the object is converted to a string (e.g., by print()). """ if not self._config_data: return "\n--- Configuration is Empty ---\n" output = ["\n--- Current Configuration ---"] max_key_len = max(len(key) for key in self._config_data) for key, value in self._config_data.items(): output.append(f"{key.ljust(max_key_len)} : {json.dumps(value, indent=None)}") output.append("-----------------------------\n") return "\n".join(output) def __getattr__(self, name): """ Allows accessing configuration values using attribute notation (e.g., config.my_key). Args: name (str): The attribute name (configuration key). Returns: The configuration value associated with the key. Raises: AttributeError: If the key is not found in the configuration. """ if name in self._config_data: return self._config_data[name] # Fallback for standard attributes if not found in config data try: return self.__getattribute__(name) except AttributeError: raise AttributeError(f"'Config' object has no attribute '{name}' and key '{name}' not found in config.") def __setattr__(self, name, value): """ Allows setting configuration values using attribute notation (e.g., config.my_key = value). Args: name (str): The attribute name (configuration key). value: The value to set. Must be JSON serializable. """ # Handle setting internal attributes like file_path or _config_data if name in ('file_path', '_config_data'): super().__setattr__(name, value) else: # Treat other attribute assignments as setting config values self.set(name, value) def __delattr__(self, name): """ Allows deleting configuration values using attribute notation (e.g., del config.my_key). Args: name (str): The attribute name (configuration key) to delete. """ if name in self._config_data: self.delete(name) else: # Fallback for standard attributes super().__delattr__(name)