os.environ and Environment Variables
Overview
Environment variables are key-value pairs managed by the operating system, passed into a program when it starts. In Python, you access them using `os.environ`, which behaves exactly like a dictionary. Think of them as a set of sticky notes given to your program on launch. The program can read the notes (e.g., `DATABASE_URL=postgres://...`) to know how to behave in different environments. - `os.environ[key]`: Fetches the variable. Returns the value as a string. Raises a `KeyError` if the key is missing. - `os.environ.get(key, default)`: Fetches the variable safely. Returns the string value, or `None` (or the provided `default`) if missing. Does not raise an exception. When working locally, you might expect Python to automatically read variables from a `.env` file. This will silently fail because `os.environ` only reads from the active shell's environment. import os # This trap fires when developers assume .env files load automatically # The variable will be None unless explicitly exported in the terminal secret = os.environ.get('MY_SECRET') This `.env` file loading behavior is covered fully in dotenv.
They allow you to configure an application without changing its code. This is essential for securely passing secrets (passwords, API keys) and configuring the app for different servers (local, testing, production) without hardcoding them in version control.
Where used: Docker Containers, GitHub Actions, Web Servers
Why learn this
- Keep API keys and passwords out of your source code
- Follow the Twelve-Factor App methodology for configuration
- Make your app easy to deploy via Docker and cloud platforms
Code walkthrough
import os
os.environ['DB_HOST'] = 'localhost'
host = os.environ.get('DB_HOST', 'default.db')
port = os.environ.get('DB_PORT', '5432')
print(f"Connecting to {host}:{port}")
Focus: port = os.environ.get('DB_PORT', '5432')
Aha moment
import os
os.environ['RETRIES'] = '5'
tries = os.environ.get('RETRIES', 3)
try:
result = tries + 2
except TypeError as e:
print('TypeError')
Prediction: What will print?
Common guess: 7
It prints TypeError. Environment variables are ALWAYS strings, so '5' + 2 fails.
Common mistakes
- Hardcoding secrets: This commits sensitive data to Git forever. Bots can steal keys from public repositories in seconds.
- Crashing on missing variables: Direct access raises a `KeyError` if the variable isn't set. Use `.get()` instead to provide safe defaults.
- Forgetting that all values are strings: The operating system passes all variables as strings (`str`). You must explicitly convert them to `int` or `bool` in your code.
Glossary
- Twelve-Factor App
- A popular methodology or set of best practices for building software-as-a-service apps that are easy to maintain and scale. Example: `store config in environment`
- cast
- To explicitly convert a value from one data type to another, like changing the string `'1'` into the integer `1`. Example: `int('1')`
Recall questions
- Why is it dangerous to hardcode configuration like API keys in Python files?
- How does `os.environ.get()` differ from accessing `os.environ['KEY']` directly?
- What data type are the values inside `os.environ`?
- If you create a .env file in your project directory, will os.environ automatically read the variables inside it?
Understanding checks
What is printed to the console?
`str`
Even though our fallback is the integer `3000`, the variable was successfully found in `os.environ`, and all environment variables are strings (`'8080'`).
Why does this print 'Feature is ON'?
The value in the environment is the string `'False'`, which is a truthy value in Python. Only empty strings (`''`) are falsy.
All environment variables are strings. You must explicitly check == 'True' or cast the string to a boolean correctly.
Practice tasks
Safely read environment configs
Given this code that aggressively crashes if `API_KEY` is missing, modify it so that `API_KEY` is required (keep the bracket notation so it fails fast), but `MAX_RETRIES` is optional and defaults to the integer `3` if not provided. Don't forget to convert `MAX_RETRIES` to an integer!
Continue learning
Previous: Dictionaries: operations and use cases