Complete guide to Python's os.getenv function covering environment variable access, configuration management, and practical examples.
Last modified April 11, 2025
This comprehensive guide explores Python’s os.getenv function, which retrieves environment variables. We’ll cover basic usage, default values, type conversion, and practical configuration examples.
The os.getenv function retrieves the value of an environment variable. It returns None if the variable doesn’t exist, unless a default is specified.
Key parameters: key (variable name), default (optional return if not found). Returns string value or default. Works across Unix and Windows systems.
The simplest use of os.getenv retrieves a variable’s value. This example shows accessing common system environment variables.
basic_access.py
import os
home_dir = os.getenv(“HOME”) username = os.getenv(“USER”) path_var = os.getenv(“PATH”)
print(f"Home directory: {home_dir}") print(f"Username: {username}") print(f"PATH contains: {path_var.split(’:’)[:3]}…")
missing_var = os.getenv(“NON_EXISTENT_VAR”) print(f"Missing variable returns: {missing_var}")
This example retrieves common Unix environment variables. The PATH variable is split to show partial contents. Non-existent variables return None.
Environment variables are typically strings. The exact variables available depend on the operating system and user configuration.
The default parameter allows specifying a fallback value when the variable isn’t set. This prevents None returns and simplifies configuration handling.
default_values.py
import os
db_host = os.getenv(“DB_HOST”, “localhost”) db_port = os.getenv(“DB_PORT”, “5432”) debug_mode = os.getenv(“DEBUG”, “False”)
print(f"Database connection: {db_host}:{db_port}") print(f"Debug mode enabled: {debug_mode}")
max_retries = os.getenv(“MAX_RETRIES”, os.getenv(“DEFAULT_RETRIES”, “3”)) print(f"Maximum retries: {max_retries}")
This shows setting default values for configuration parameters. The nested example checks multiple fallback options before using a hardcoded default.
Defaults help make applications more robust when environment configuration is incomplete or missing.
Environment variables are always strings. This example demonstrates converting them to appropriate Python types for application use.
type_conversion.py
import os
port = os.getenv(“PORT”, “8080”) try: port = int(port) except ValueError: port = 8080
debug = os.getenv(“DEBUG”, “false”).lower() debug = debug in (“true”, “1”, “yes”, “y”)
allowed_hosts = os.getenv(“ALLOWED_HOSTS”, “localhost,127.0.0.1”) allowed_hosts = [h.strip() for h in allowed_hosts.split(",")]
print(f"Port: {port} (type: {type(port)})") print(f"Debug: {debug} (type: {type(debug)})") print(f"Allowed hosts: {allowed_hosts} (type: {type(allowed_hosts)})")
This shows common type conversion patterns for environment variables. Integer, boolean, and list conversions are demonstrated with error handling.
Proper type conversion ensures configuration values work correctly in the application logic.
A common pattern is wrapping environment variables in a configuration class. This provides type safety and centralizes configuration management.
config_class.py
import os from dataclasses import dataclass
@dataclass class AppConfig: db_host: str db_port: int debug: bool timeout: float
@classmethod
def from_env(cls):
return cls(
db_host=os.getenv("DB_HOST", "localhost"),
db_port=int(os.getenv("DB_PORT", "5432")),
debug=os.getenv("DEBUG", "false").lower() in ("true", "1"),
timeout=float(os.getenv("TIMEOUT", "5.0"))
)
config = AppConfig.from_env() print(f"Database: {config.db_host}:{config.db_port}") print(f"Debug mode: {config.debug}") print(f"Timeout: {config.timeout} seconds")
This example uses a dataclass to store typed configuration. The from_env classmethod handles environment variable loading and conversion.
The configuration class pattern makes the application more maintainable by centralizing environment variable handling.
This example demonstrates a Django-inspired settings pattern where environment variables override default settings in a module.
django_style.py
import os from typing import Any
def get_setting(name: str, default: Any = None, cast: type = str) -> Any: value = os.getenv(name) if value is None: return default try: return cast(value) except (ValueError, TypeError): return default
DEBUG = get_setting(“DEBUG”, False, bool) DATABASE_URL = get_setting(“DATABASE_URL”, “sqlite:///db.sqlite3”) ALLOWED_HOSTS = get_setting(“ALLOWED_HOSTS”, [“localhost”], lambda x: [h.strip() for h in x.split(",")]) MAX_UPLOAD_SIZE = get_setting(“MAX_UPLOAD_MB”, 10, int) * 1024 * 1024
print(f"Debug mode: {DEBUG}") print(f"Database URL: {DATABASE_URL}") print(f"Allowed hosts: {ALLOWED_HOSTS}") print(f"Max upload size: {MAX_UPLOAD_SIZE} bytes")
The get_setting helper function provides flexible configuration loading with type conversion. Complex conversions use lambda functions.
This pattern is extensible and works well for applications with many configuration options.
This example shows how to test code that uses os.getenv by temporarily modifying the environment during tests.
testing_env.py
import os from unittest import TestCase, mock
def get_config(): return { “api_key”: os.getenv(“API_KEY”, “default_key”), “endpoint”: os.getenv(“API_ENDPOINT”, “https://api.example.com”) }
class TestEnvironmentConfig(TestCase): @mock.patch.dict(os.environ, { “API_KEY”: “test_key”, “API_ENDPOINT”: “http://localhost:8000” }) def test_with_mocked_env(self): config = get_config() self.assertEqual(config[“api_key”], “test_key”) self.assertEqual(config[“endpoint”], “http://localhost:8000”)
def test_with_defaults(self):
with mock.patch.dict(os.environ, clear=True):
config = get_config()
self.assertEqual(config["api_key"], "default_key")
self.assertEqual(config["endpoint"], "https://api.example.com")
if name == “main”: import unittest unittest.main()
The test uses unittest.mock.patch.dict to temporarily modify os.environ. This allows testing both custom values and default fallbacks.
Proper testing ensures environment variable handling works correctly in different deployment scenarios.
Sensitive data: Avoid storing secrets in environment variables in production
Validation: Always validate and sanitize environment values
Default values: Use secure defaults for security-related settings
Error handling: Handle type conversion failures gracefully
Documentation: Document all supported environment variables
Centralize config: Use a single configuration module
Type safety: Convert values to proper types early
Default values: Provide sensible defaults
Validation: Validate critical configuration values
Documentation: Document all environment variables
My name is Jan Bodnar, and I am a passionate programmer with extensive programming experience. I have been writing programming articles since 2007. To date, I have authored over 1,400 articles and 8 e-books. I possess more than ten years of experience in teaching programming.
List all Python tutorials.