ZetCode

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.

basic_access.py
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.

default_values.py
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.

type_conversion.py
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.

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"))
        )

# 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.

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

# 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.

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.

Security Considerations

Best Practices

Source References

Author

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.