Robust Environment Management with Python
This challenge focuses on building a simple, yet effective, environment management system in Python. The goal is to create a tool that can load environment variables from a file (e.g., .env) and make them accessible in your Python code, while gracefully handling missing variables and providing a way to override them with system environment variables. This is crucial for managing configuration settings, API keys, and other sensitive information separately from your codebase.
Problem Description
You are tasked with creating a Python class called EnvironmentManager that handles loading and accessing environment variables. The class should:
- Load from File: Load environment variables from a specified
.envfile. Each line in the file should be in the formatKEY=VALUE. - Override with System Variables: If an environment variable with the same name already exists in the system environment, it should take precedence over the value loaded from the
.envfile. - Handle Missing Variables: If a variable is requested that doesn't exist in either the
.envfile or the system environment, the class should returnNone. - Provide a Getter Method: Offer a method
get(key)to retrieve the value of a specific environment variable. - Error Handling: Gracefully handle cases where the
.envfile does not exist or is malformed (e.g., a line doesn't contain an equals sign). In these cases, log an error message to the console and continue loading any valid variables.
Expected Behavior:
The EnvironmentManager should initialize by attempting to load variables from a .env file (defaulting to .env in the current directory if no file path is provided). It should then allow you to retrieve environment variables using the get(key) method. The system environment should always take precedence.
Examples
Example 1:
.env file:
API_KEY=your_api_key
DATABASE_URL=localhost:5432
System Environment:
DATABASE_URL=remote_db:8000
Code:
env_manager = EnvironmentManager()
api_key = env_manager.get("API_KEY")
database_url = env_manager.get("DATABASE_URL")
print(api_key)
print(database_url)
Output:
your_api_key
remote_db:8000
Explanation: The API_KEY is loaded from the .env file, while DATABASE_URL is overridden by the system environment variable.
Example 2:
.env file:
DEBUG=True
System Environment:
(No relevant variables set)
Code:
env_manager = EnvironmentManager()
debug_mode = env_manager.get("DEBUG")
missing_var = env_manager.get("NON_EXISTENT_VAR")
print(debug_mode)
print(missing_var)
Output:
True
None
Explanation: DEBUG is loaded from the .env file. NON_EXISTENT_VAR is not found and returns None.
Example 3: (Malformed .env file)
.env file:
API_KEY=your_api_key
INVALID_LINE
DATABASE_URL=localhost:5432
Code:
env_manager = EnvironmentManager()
api_key = env_manager.get("API_KEY")
database_url = env_manager.get("DATABASE_URL")
Output (to console):
Error: Invalid line in .env file: INVALID_LINE
Output (printed to standard output):
your_api_key
localhost:5432
Explanation: The malformed line is logged as an error, but the valid variables are still loaded.
Constraints
- The
.envfile will be a plain text file. - Keys and values in the
.envfile will be strings. - The file path to the
.envfile can be a relative or absolute path. - The
get()method should returnNoneif the variable is not found. - Error messages should be printed to standard error (console).
- The code should be reasonably efficient for typical
.envfile sizes (less than 1KB).
Notes
- Consider using Python's built-in
osmodule to access system environment variables. - You can use string manipulation techniques to parse the
.envfile. - Think about how to handle potential errors during file reading and parsing.
- The class should be designed to be reusable and easy to integrate into other Python projects.
- No external libraries are allowed for this challenge (e.g.,
python-dotenv). The goal is to implement the environment loading logic yourself.