Las variables de entorno son la forma de configurar un programa que corre en un contenedor. Python las lee en una línea; lo que hay que montar alrededor es la conversión y la validación.
También te puede interesar
Las tres formas de leer
salidaos.environ.get('NO_EXISTE') None
os.environ.get('VACIA') ''
'VACIA' in os.environ True
os.getenv('NO_EXISTE', 'valor') 'valor'
os.environ['NO_EXISTE'] KeyError: 'NO_EXISTE'
Python sí distingue una variable vacía de una que no existe: la primera devuelve la cadena vacía y la segunda None. Aun así casi siempre conviene tratarlas igual, porque VAR= suele significar «no la he puesto».
Los corchetes revientan con KeyError, lo que está bien para lo que es verdaderamente obligatorio; get para lo demás.
Una función por tipo
def entero(k, omision):
v = os.environ.get(k)
if not v: return omision
try: return int(v.strip())
except ValueError: raise SystemExit(f"error: {k}={v!r} no es un entero")
VERDADEROS = {"1","true","t","yes","y","si","sí","on"}
def booleano(k, omision):
v = os.environ.get(k)
return omision if not v else v.strip().lower() in VERDADEROS
def obligatorio(k):
v = os.environ.get(k)
if not v: raise SystemExit(f"error: {k} es obligatoria y no está definida")
return v
salidaConfig(host='localhost', puerto=9000, depurar=True, espera=datetime.timedelta(seconds=1, microseconds=500000), clave='********')
Y cuando algo está mal, el programa no arranca a medias:
y sale con código 1error: APP_CLAVE es obligatoria y no está definida error: APP_PUERTO='ocho' no es un entero
raise SystemExit("mensaje") hace tres cosas de una vez: escribe el mensaje en la salida de errores, termina el programa y devuelve el código 1. Es más corto que print(..., file=sys.stderr) seguido de sys.exit(1), y al ser una excepción se puede capturar en las pruebas.Ojo con el booleano: bool("False") es True, porque cualquier cadena no vacía lo es. Por eso hace falta la lista de valores aceptados.
Que la contraseña no se escape
class Secreto(str):
__slots__ = ()
def __repr__(self): return "'********'" if self else "'(vacío)'"
def __str__(self): return "********" if self else "(vacío)"
Mira la salida de arriba: el Config entero se imprimió y la clave salió como ********. Al heredar de str, el objeto sigue siendo una cadena y se puede usar donde haga falta, pero no aparece en ningún registro, volcado ni mensaje de error, porque todo eso pasa por repr o str.
Cambiarlo para un hijo, sin tocar el propio
import subprocess, sys
ent = os.environ | {"APP_PUERTO": "9999"} # copia con un cambio
subprocess.run([sys.executable, "-c", "..."], env=ent)
salidael hijo ve 9999 y aquí sigue siendo 9000
Ese | entre diccionarios es de Python 3.9 y crea uno nuevo sin modificar el original, que es justo lo que quieres: os.environ es global al proceso y cambiarlo afecta a todo lo que venga después.
| Para… | Usa |
|---|---|
| Leer con valor por omisión | os.getenv(k, omision) |
| Exigir que esté | os.environ[k], o una función propia |
| Distinguir vacía de ausente | k in os.environ |
| Convertir | int(), y una lista para el booleano |
| Que no se filtre un secreto | un tipo propio con __repr__ |
| Pasárselo a un hijo | env=os.environ | {...} |
Leer un archivo .env |
python-dotenv, que no viene de serie |
Todo el código se ejecutó con Python 3.14 en un contenedor limpio antes de publicar esta página; las salidas están copiadas de esa ejecución.