os.environ
os.environ is a dict-like snapshot of the environment taken when os is imported. Changing it also changes the real process environment, so subprocesses see the update. Values are always str: convert numbers and flags yourself.
Demo
import os from unittest import mock with mock.patch.dict(os.environ, {'HOME': '/home/ada', 'LANG': 'C.UTF-8'}, clear=True): try: item = os.environ['HOME'] except KeyError as e: item = f'KeyError: {e}' result = (item, os.getenv('HOME'), os.getenv('HOME', 'default')) result
Each tab works on a temporary environment (mock.patch.dict(..., clear=True) restores the real one afterwards), so the result does not depend on your machine. A missing name: os.environ[name] raises KeyError, getenv returns None or your default. In the second tab text is stored as-is, while 10 and 1.5 arrive as an int and a float, and os.environ refuses them with TypeError: str expected, not int (or float). On Windows os.environ upper-cases names, so os.environ['home'] finds HOME there but raises KeyError on Linux and macOS.
Parameters
| Name | Type | Required | Description |
|---|---|---|---|
| key | str | yes | The variable name. On Linux and macOS names are case-sensitive; on Windows os.environ upper-cases them, so lookups ignore case. |
| default | any | no (None) | getenv only: returned when the variable is not set. Not converted to str. |
| value | str | yes | putenv / os.environ[key] = value: must be a str — anything else raises TypeError. |
Return value
str | None — os.environ[key] returns the str value (KeyError when unset); getenv returns the value, or default (None) when unset.
Common patterns
import os PORT = int(os.environ.get('PORT', '8000')) DEBUG = os.environ.get('DEBUG', '0') in ('1', 'true', 'yes') DATABASE_URL = os.environ['DATABASE_URL'] # required: KeyError if missing
import os, subprocess subprocess.run(['make', 'test'], env={**os.environ, 'CI': '1'}, check=True)
import os from unittest import mock with mock.patch.dict(os.environ, {'APP_ENV': 'test'}): run_app()
import os if os.supports_bytes_environ: raw = os.environb.get(b'LANG')
Examples
Pitfalls
import os from unittest import mock with mock.patch.dict(os.environ, clear=True): os.environ['API_TOKEN']
import os from unittest import mock with mock.patch.dict(os.environ, clear=True): token = os.getenv('API_TOKEN', 'none') token
import os from unittest import mock with mock.patch.dict(os.environ, {'DEBUG': '0'}): debug = bool(os.getenv('DEBUG')) debug
import os from unittest import mock with mock.patch.dict(os.environ, {'DEBUG': '0'}): debug = os.getenv('DEBUG', '0') == '1' debug
import os from unittest import mock with mock.patch.dict(os.environ): os.environ['WORKERS'] = 4
import os from unittest import mock with mock.patch.dict(os.environ): os.environ['WORKERS'] = str(4) workers = int(os.environ['WORKERS']) workers
When to use
- Configuration and secrets supplied by the deployment (12-factor style)
- Passing settings to child processes
- Structured or large configuration → a config file (tomllib, json)
- Values shared between running processes — each process has its own copy
- Changing the environment for one subprocess → pass env= to subprocess.run instead
Notes
FAQ
None in practice: os.getenv(key, default) is os.environ.get(key, default). Both return None (or the default) for a missing variable, while os.environ[key] raises KeyError.