Working with Environment Variables
- Environment variables are dynamic, named values provided by the operating system to running processes, enabling configuration of behavior without code modifications.
- They allow applications to adapt across development, staging, and production environments by externalizing configuration data such as API keys, file paths, and feature flags.
- Python’s
os module offers simple interfaces to access and manage these variables, promoting separation of code and configuration.
import os
for key in ["HOME", "SHELL"]:
value = os.getenv(key)
print(f"{key} = {value if value else "Not set"}")
env_keys = list(os.environ.keys())
print(f"We have {len(env_keys)} environment variables available!")
for key in env_keys[:5]:
print(key)
Accessing Environment Variables with os.getenv()
- The
os.getenv function retrieves the value of an environment variable by key, returning None or a provided default if the key is not found.
- It prevents
KeyError exceptions by offering a safe access pattern for optional configuration settings.
- Since environment variables are always strings, any expected non-string types require explicit conversion after retrieval.
import os
os.environ["APP_API_KEY"] = "ab12cd34"
api_key = os.getenv("APP_API_KEY")
debug_mode = os.getenv("DEBUG_MODE", False)
if api_key:
print(f"API key found: {api_key[:4]}... (masked)")
else:
print("APP_API_KEY not set.")
print(f"Debug mode: {debug_mode}")
Accessing Environment Variables with os.environ
os.environ behaves like a dictionary mapping environment variable names to their string values.
- Accessing a missing key via
os.environ['KEY'] raises a KeyError, making it suitable for mandatory variables.
- One should guard against missing keys by checking membership or catching
KeyError to handle critical configuration errors.
import os
try:
java_home = os.environ["JAVA_HOME"]
print(java_home)
except KeyError:
print("JAVA_HOME environment variable not set.")
Setting Environment Variables Within Python
- While environment variables are typically set externally,
os.environ can be modified at runtime to affect the current process and its children.
- Assigning to
os.environ['KEY'] makes the variable available to any subprocesses spawned by the script.
- Deleting an entry from
os.environ removes it for subsequent operations within the process, but changes do not persist after the script exits.
import os
import sys
import subprocess
print(f"Initial MY_CUSTOM_VAR: {os.getenv("MY_CUSTOM_VAR")}")
os.environ["MY_CUSTOM_VAR"] = "SetByOurScript"
print(f"Updated MY_CUSTOM_VAR: {os.getenv("MY_CUSTOM_VAR")}")
result = subprocess.run([
sys.executable,
"-c",
"""import os
print(f"Child sees MY_CUSTOM_VAR: {os.getenv("MY_CUSTOM_VAR")}")"""
])
result.stdout
del os.environ["MY_CUSTOM_VAR"]
print(f"After deletion, MY_CUSTOM_VAR: {os.getenv("MY_CUSTOM_VAR")}")
Using dotenv to Manage Local Environment Files
- The python-dotenv library lets you keep sensitive and environment-specific values in a .env file instead of the shell.
- A .env file lives alongside your script and contains lines like
KEY=value; it's loaded at runtime into os.environ.
- Install with
pip install python-dotenv==1.1.0 (version included here so that we are all using the same, in other installations it may be omitted), then call load_dotenv() before any os.getenv calls.
- This approach keeps your shell clean and makes it easy to commit example
.env.example files without secrets.
- Remember not to commit actual .env files with real secrets! Add them to
.gitignore.
import os
from dotenv import load_dotenv
os.environ["MY_DOTENV_VAR"] = "setFromJupyter"
load_dotenv(override=True)
secret_dotenv_value = os.getenv("MY_DOTENV_VAR")
print(f"Retrieved MY_DOTENV_VAR with value {secret_dotenv_value}")
Common Pitfalls & How to Avoid Them
- Environment variable names are always case-sensitive in Python, regardless of the underlying OS; inconsistent casing leads to unexpected missing values.
- Forgetting that all environment variable values are strings can cause type errors; always convert to the intended type like int or bool after retrieval.
- Accessing a missing mandatory variable via os.environ raises KeyError; avoid unhandled errors by checking membership or catching exceptions.
- Storing highly sensitive secrets in plain environment variables carries security risks; for production use, consider managed secrets solutions like Vault or AWS Secrets Manager.
import os
from dotenv import load_dotenv
load_dotenv(override=True)
number_dotenv_value = os.getenv("MY_NUMBER_VAR")
print(type(number_dotenv_value))
# print(number_dotenv_value + 45) # Uncommenting will raise TypeError because number_dotenv_value is a string!