SXXXXXXX_GitUtility/config_manager.py
2025-04-07 10:36:40 +02:00

173 lines
8.2 KiB
Python

# config_manager.py
import configparser
import os
import logging
# Constants
CONFIG_FILE = "git_svn_sync.ini"
DEFAULT_PROFILE = "default"
# This is the single source of truth for the default backup directory path
DEFAULT_BACKUP_DIR = os.path.join(os.path.expanduser("~"), "git_svn_backup")
class ConfigManager:
"""
Manages the configuration file for the Git SVN Sync tool.
Handles loading, saving, and accessing configuration settings.
"""
def __init__(self, logger):
"""
Initializes the ConfigManager with a logger instance.
Args:
logger (logging.Logger): Logger instance for logging messages.
"""
self.logger = logger
self.config = configparser.ConfigParser()
# Ensure logger is available before calling load_config if load_config logs errors
# Or pass logger to load_config if needed there specifically
self.load_config()
def load_config(self):
"""
Loads the configuration from the CONFIG_FILE.
If the file doesn't exist or is missing sections, it creates them with default values.
"""
try:
# Check if file exists, if not, create it with defaults
if not os.path.exists(CONFIG_FILE):
self.logger.info(f"Configuration file '{CONFIG_FILE}' not found. Creating with default profile.")
self._create_default_profile()
self.save_config() # Save the newly created default profile
else:
files_read = self.config.read(CONFIG_FILE)
if not files_read:
self.logger.warning(f"Configuration file '{CONFIG_FILE}' was empty or could not be read properly. Attempting to recreate.")
self._create_default_profile()
self.save_config()
# Ensure the default profile exists even if the file existed but was modified
elif DEFAULT_PROFILE not in self.config.sections():
self.logger.warning(f"Default profile '{DEFAULT_PROFILE}' missing in '{CONFIG_FILE}'. Adding it.")
self._create_default_profile()
self.save_config() # Save changes
except configparser.Error as e:
self.logger.error(f"Error parsing configuration file '{CONFIG_FILE}': {e}")
# Consider creating a default config here as well if parsing fails fundamentally
self.logger.info("Attempting to create a default configuration.")
self.config = configparser.ConfigParser() # Reset parser state
self._create_default_profile()
self.save_config()
except Exception as e:
self.logger.error(f"Unexpected error loading configuration: {e}")
# Optionally re-raise or handle more gracefully depending on desired behavior
def _create_default_profile(self):
"""Creates the default profile section with default values."""
if DEFAULT_PROFILE not in self.config.sections():
self.config.add_section(DEFAULT_PROFILE)
# Set default values (will overwrite if section existed but key was missing)
self.config.set(DEFAULT_PROFILE, "svn_working_copy_path", "/path/to/svn/working/copy")
self.config.set(DEFAULT_PROFILE, "usb_drive_path", "/media/usb")
self.config.set(DEFAULT_PROFILE, "bundle_name", "my_bundle.bundle")
self.config.set(DEFAULT_PROFILE, "bundle_name_updated", "my_updated_bundle.bundle")
self.config.set(DEFAULT_PROFILE, "autobackup", "False")
self.config.set(DEFAULT_PROFILE, "backup_dir", DEFAULT_BACKUP_DIR) # Use constant
self.config.set(DEFAULT_PROFILE, "autocommit", "False")
self.logger.info(f"Ensured default profile '{DEFAULT_PROFILE}' exists with default values.")
def save_config(self):
"""
Saves the current configuration to the CONFIG_FILE.
"""
try:
with open(CONFIG_FILE, "w", encoding='utf-8') as configfile: # Specify encoding
self.config.write(configfile)
self.logger.debug(f"Configuration saved to '{CONFIG_FILE}'.")
except IOError as e:
self.logger.error(f"Error writing configuration file '{CONFIG_FILE}': {e}")
except Exception as e:
self.logger.error(f"Unexpected error saving configuration: {e}")
def get_profile_sections(self):
"""
Returns a list of all profile sections in the configuration file.
Returns:
list: List of profile section names.
"""
return self.config.sections()
def get_profile_option(self, profile, option, fallback=None):
"""
Retrieves a specific option from a profile section.
Args:
profile (str): Name of the profile section.
option (str): Name of the option to retrieve.
fallback (any, optional): Default value to return if the option or profile
is not found. Defaults to None.
Returns:
str: Value of the option or the fallback value if not found.
Returns fallback also if the profile itself doesn't exist.
"""
# Use configparser's built-in fallback mechanism
# The 'fallback' parameter in config.get() handles NoSectionError and NoOptionError
try:
# Ensure fallback is a string if not None, as configparser expects strings
str_fallback = str(fallback) if fallback is not None else None
value = self.config.get(profile, option, fallback=str_fallback)
# Log if fallback was used (optional, can be noisy)
# Check explicitly if the profile or option was missing if needed for logging
if value == str_fallback and not self.config.has_option(profile, option):
if not self.config.has_section(profile):
self.logger.warning(f"Profile '{profile}' not found. Cannot get option '{option}'. Using fallback.")
else:
self.logger.warning(f"Option '{option}' not found in profile '{profile}'. Using fallback.")
return value
except Exception as e:
# Catch unexpected errors during retrieval
self.logger.error(f"Error getting option '{option}' for profile '{profile}': {e}")
return fallback # Return the original fallback type
def set_profile_option(self, profile, option, value):
"""
Sets a specific option in a profile section. Creates the section if it doesn't exist.
Args:
profile (str): Name of the profile section.
option (str): Name of the option to set.
value (any): Value to set for the option (will be converted to string).
"""
try:
if not self.config.has_section(profile):
self.config.add_section(profile)
self.logger.info(f"Created new profile section: '{profile}'")
self.config.set(profile, option, str(value)) # Ensure value is string
self.logger.debug(f"Set option '{option}' in profile '{profile}' to '{value}'.")
except Exception as e:
self.logger.error(f"Error setting option '{option}' in profile '{profile}': {e}")
def remove_profile_section(self, profile):
"""
Removes a profile section from the configuration.
Args:
profile (str): Name of the profile section to remove.
Returns:
bool: True if the section was removed successfully, False otherwise.
"""
if profile == DEFAULT_PROFILE:
self.logger.warning(f"Attempted to remove the default profile '{DEFAULT_PROFILE}', which is not allowed.")
return False
if self.config.has_section(profile):
try:
self.config.remove_section(profile)
self.logger.info(f"Removed profile section: '{profile}'")
return True
except Exception as e:
self.logger.error(f"Error removing profile section '{profile}': {e}")
return False
else:
self.logger.warning(f"Profile section '{profile}' not found, cannot remove.")
return False