environs: simplified environment variable parsing
environs is a Python library (current version 15.0.1) designed for simplified parsing of environment variables, aligning with the Twelve-Factor App methodology for separating configuration from code. It provides robust type-casting, validation, and flexible parsing of various data types including lists, dictionaries, dates, and URLs. It also integrates seamlessly with `.env` files. environs is actively maintained and has a steady release cadence.
Warnings
- breaking The `env.get()` method was removed in `environs` version 1.0.0. Attempting to use it will result in an `AttributeError`.
- gotcha Reading `.env` files is not automatic. You must explicitly call `env.read_env()` early in your application's lifecycle to load variables from `.env` files into the `Env` instance. This functionality also requires `python-dotenv` to be installed.
- gotcha `environs` operates on its own internal state and does not directly mutate `os.environ` when `env.read_env()` is called or variables are accessed. If you later try to access `os.environ` directly, it will not reflect the values loaded or overridden by `environs` from `.env` files.
- gotcha The `env.url()` method returns a `urllib.parse.ParseResult` object, not a plain string. If you require the URL as a string, you should explicitly use `env.str(..., validate=validate.URL())`.
- gotcha When using validators with `environs` (especially when `Env` is initialized with `eager=False`), you must explicitly call `env.seal()` after all environment variables have been parsed to trigger deferred validation. Failing to do so can lead to validation errors not being raised until much later or incorrect behavior.
Install
-
pip install environs
Imports
- Env
from environs import Env
Quickstart
import os
from environs import Env
from urllib.parse import urlparse
import logging
# For demonstration, set some environment variables
os.environ['GITHUB_USER'] = 'test_user'
os.environ['MAX_CONNECTIONS'] = '10'
os.environ['SHIP_DATE'] = '1984-06-25'
os.environ['ENABLE_LOGIN'] = 'true'
os.environ['API_KEY'] = 'some_secret_key'
os.environ['LOG_LEVEL'] = 'INFO'
os.environ['MY_API_URL'] = 'https://api.example.com/v1'
# Initialize Env object
env = Env()
# For local development, uncomment and ensure python-dotenv is installed:
# with open('.env', 'w') as f:
# f.write('ANOTHER_VAR=hello\n')
# env.read_env() # Reads from .env file if it exists
# Required variables
gh_user = env('GITHUB_USER')
print(f"GitHub User: {gh_user}")
# Casting to specific types
max_connections = env.int('MAX_CONNECTIONS', default=5) # default if not set
print(f"Max Connections: {max_connections} (type: {type(max_connections)})")
ship_date = env.date('SHIP_DATE')
print(f"Ship Date: {ship_date} (type: {type(ship_date)})")
enable_login = env.bool('ENABLE_LOGIN', default=False)
print(f"Enable Login: {enable_login} (type: {type(enable_login)})")
api_key = env('API_KEY') # No type cast, defaults to str
print(f"API Key: {api_key}")
log_level = env.log_level('LOG_LEVEL', default=logging.DEBUG)
print(f"Log Level: {log_level} (type: {type(log_level)})")
# URL parsing
my_api_url = env.url('MY_API_URL')
print(f"API URL: {my_api_url.scheme}://{my_api_url.netloc} (type: {type(my_api_url)})")
# Example of a missing variable with default
feature_flag = env.bool('FEATURE_X', default=False)
print(f"Feature X enabled: {feature_flag}")
# Example of a required variable not set (will raise EnvValidationError)
# try:
# missing_var = env('MISSING_REQUIRED_VAR')
# except Exception as e:
# print(f"Error: {e}")