Python os.getenv Function
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.
Basic Definitions
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.
Basic Environment Variable Access
The simplest use of os.getenv
retrieves a variable's value.
This example shows accessing common system environment variables.
import os # Get common environment variables 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]}...") # Non-existent variable 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.
Providing Default Values
The default parameter allows specifying a fallback value when the variable isn't set. This prevents None returns and simplifies configuration handling.
import os # Configuration with defaults 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}") # Nested defaults example 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.
Type Conversion of Values
Environment variables are always strings. This example demonstrates converting them to appropriate Python types for application use.
import os # String to integer conversion port = os.getenv("PORT", "8080") try: port = int(port) except ValueError: port = 8080 # String to boolean conversion debug = os.getenv("DEBUG", "false").lower() debug = debug in ("true", "1", "yes", "y") # String to list conversion 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.
Configuration Class Example
A common pattern is wrapping environment variables in a configuration class. This provides type safety and centralizes configuration management.
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")) ) # Load configuration 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.
Django-style Settings Management
This example demonstrates a Django-inspired settings pattern where environment variables override default settings in a module.
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 # Application settings 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.
Testing Environment Variables
This example shows how to test code that uses os.getenv by temporarily modifying the environment during tests.
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.
Security Considerations
- 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
Best Practices
- 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
Source References
Author
List all Python tutorials.