Robust Configuration Management System in Python
Configuration management is crucial for any application, allowing you to easily adjust settings without modifying code. This challenge asks you to build a simple configuration management system in Python that loads settings from a file (INI-style) and provides a clean interface for accessing and modifying them. This is useful for managing application behavior, database connections, API keys, and other environment-specific parameters.
Problem Description
You are tasked with creating a ConfigManager class that handles loading and accessing configuration settings from a file. The configuration file will be in a simple INI-like format, where each section is denoted by a header enclosed in square brackets [], and each setting within a section is a key=value pair.
The ConfigManager class should have the following methods:
__init__(self, filepath): Initializes theConfigManagerwith the path to the configuration file. The file should be read and parsed during initialization. If the file doesn't exist, an exception should be raised.get(self, section, key): Retrieves the value associated with a givenkeywithin a specifiedsection. If the section or key doesn't exist, returnNone.set(self, section, key, value): Sets the value of a givenkeywithin a specifiedsection. If the section doesn't exist, it should be created.save(self): Saves the current configuration back to the file specified in the constructor. The file should be overwritten with the updated configuration.
Key Requirements:
- The configuration file format is simple INI-like:
- Sections are enclosed in
[]. - Settings are in the format
key=value. - Whitespace around the
=is allowed. - Empty lines and lines starting with
#(comments) should be ignored.
- Sections are enclosed in
- The
ConfigManagershould handle cases where sections or keys are missing gracefully. - The
savemethod should write the configuration back to the file in the same format. - Error handling: Raise a
FileNotFoundErrorif the configuration file does not exist.
Expected Behavior:
The ConfigManager should provide a simple and reliable way to load, access, modify, and save configuration settings. The code should be well-structured and easy to understand.
Examples
Example 1:
Input:
config_file.ini:
[Database]
host=localhost
port=5432
user=admin
password=secret
[API]
key=abcdef123456
url=https://api.example.com
Code:
config = ConfigManager("config_file.ini")
print(config.get("Database", "host"))
print(config.get("API", "url"))
print(config.get("Unknown", "key"))
Output:
localhost
https://api.example.com
None
Explanation: The code loads the configuration from config_file.ini, retrieves the values for "host" and "url" from their respective sections, and attempts to retrieve a value from a non-existent section, resulting in None.
Example 2:
Input:
config_file.ini:
[Application]
name=My App
version=1.0
Code:
config = ConfigManager("config_file.ini")
config.set("Application", "version", "1.1")
config.set("Logging", "level", "DEBUG")
config.save()
config_file.ini (after save):
[Application]
name=My App
version=1.1
[Logging]
level=DEBUG
Explanation: The code loads the configuration, updates the "version" setting in the "Application" section, creates a new "Logging" section and sets its "level" setting, and then saves the updated configuration back to the file.
Example 3: (Edge Case - File Not Found)
Input:
config_file.ini (does not exist)
Code:
config = ConfigManager("config_file.ini")
Output:
FileNotFoundError: config_file.ini not found
Explanation: The code attempts to load a configuration file that does not exist, resulting in a FileNotFoundError.
Constraints
- The configuration file path will be a string.
- Section and key names will be strings.
- Values will be strings.
- The configuration file will not exceed 1MB in size.
- The code should be reasonably efficient for typical configuration file sizes. Avoid unnecessary file reads or writes.
Notes
- Consider using a dictionary to store the configuration data internally.
- Pay close attention to error handling, especially when dealing with file operations.
- The INI-like format is simplified; you don't need to handle complex features like nested sections or different data types.
- Focus on clarity and readability of your code.
- The
savemethod should preserve the original order of sections and settings as much as possible. - Whitespace around the
=sign in the configuration file should be ignored. - Comments (lines starting with
#) should be ignored.