Conceptos básicos de scripting
10 ejemplos para empezar con System Scripting: 7 básicos y 3 intermedios.
Prerrequisitos
uv venv && source .venv/bin/activate
# solo stdlib para la mayoría de los ejemplos; typer opcional:
uv pip install "typer>=0.15"- Python 3.14.0 - los ejemplos de scripting usan primero la biblioteca estándar.
Ejemplos básicos
1. Punto de entrada del script
Protege los efectos secundarios para que las importaciones permanezcan seguras.
def main() -> int:
print("ejecutando")
return 0
if __name__ == "__main__":
raise SystemExit(main())main()devuelve un código de salida entero para cron y CI.- Los módulos importables no deben realizar llamadas de red en el momento de la importación.
raise SystemExitevitasys.exiten código adyacente a la biblioteca.
Relacionado: Scripts robustos - códigos de salida y registro
2. CLI de argparse
Analiza indicadores y argumentos posicionales sin dependencias adicionales.
import argparse
def main() -> int:
parser = argparse.ArgumentParser(description="Archivar logs")
parser.add_argument("--dry-run", action="store_true")
parser.add_argument("path")
args = parser.parse_args()
print(args.path, args.dry_run)
return 0
if __name__ == "__main__":
raise SystemExit(main())action="store_true"para indicadores booleanos.- El texto de ayuda se genera automáticamente a partir de
ArgumentParser. - Valida las rutas después del análisis, no dentro de
add_argumentpara reglas complejas.
Relacionado: Automatización de archivos y directorios - manejo de rutas
3. Rutas de pathlib
Las rutas orientadas a objetos superan a la concatenación de cadenas.
from pathlib import Path
root = Path("data")
for csv in root.glob("**/*.csv"):
print(csv.resolve())- El operador
/une segmentos de ruta de forma portable. glob("**/*.csv")recorre recursivamente con**(sigue los enlaces simbólicos con cuidado).- Usa
.read_text(encoding="utf-8")en lugar deopen()cuando el archivo completo cabe en memoria.
Relacionado: Automatización de archivos y directorios - escrituras seguras
4. Registro en lugar de print
Los registros estructurados sobreviven al correo electrónico de cron y a los agregadores de registros.
import logging
logging.basicConfig(level=logging.INFO, format="%(levelname)s %(message)s")
log = logging.getLogger("backup")
def main() -> int:
log.info("inicio de la copia de seguridad")
return 0- Nivel de registro predeterminado INFO en scripts de producción; DEBUG mediante el indicador
--verbose. - Registra en stderr; mantén stdout limpio para consumidores de JSON/tuberías.
- Incluye el contexto de la operación en los mensajes de registro, no volcados de datos enormes.
Relacionado: Scripts robustos - registro operacional
5. Códigos de salida explícitos
Los llamadores distinguen los modos de fallo a través del estado de salida.
EXIT_OK = 0
EXIT_USAGE = 2
EXIT_FAIL = 1
def main() -> int:
return EXIT_OK- Documenta los códigos en
--helpo README para los operadores. 2señala comúnmente errores de uso (argparse usa esto).- Nunca ignores excepciones sin registrar y sin una salida distinta de cero.
Relacionado: Scripts robustos - manejo de fallos
6. Variables de entorno
Lee la configuración del entorno para scripts de 12 factores.
import os
API_URL = os.environ.get("API_URL", "https://api.example.com")
TOKEN = os.environ["API_TOKEN"] # requerido - lanza KeyError si falta- Secretos requeridos: falla rápidamente con un mensaje claro si falta la variable de entorno.
- Valores predeterminados solo para conveniencia de desarrollo no secreto.
- Nunca imprimas variables de entorno que contengan tokens.
Relacionado: Trabajar con APIs y Webhooks - llamadas autenticadas
7. Ejecución segura de subcomandos
Delega a herramientas externas con listas de argumentos explícitas.
import subprocess
def run_git_status() -> int:
proc = subprocess.run(["git", "status", "--short"], check=False)
return proc.returncode- La forma de lista evita la inyección de shell - no
shell=Truea menos que sea inevitable. check=Falsete permite mapear los códigos de retorno tú mismo.- Captura la salida con
capture_output=Trueal analizar resultados.
Relacionado: subprocess e Interoperabilidad de Shell - captura y tiempos de espera
Ejemplos intermedios
8. CLI de Typer con tipos
CLI tipada con menos código repetitivo que argparse puro.
import typer
app = typer.Typer()
@app.command()
def greet(name: str, loud: bool = False) -> None:
msg = f"hola {name}"
typer.echo(msg.upper() if loud else msg)
if __name__ == "__main__":
app()- Typer se basa en Click - bueno para herramientas de múltiples comandos.
- Las sugerencias de tipo controlan el análisis y la validación de opciones.
- Empareja con
uv run script.pyen la tabla de scripts depyproject.toml.
Relacionado: Programación y Cron - CLIs de Typer programadas
9. Archivo de configuración + anulación de CLI
Fusiona valores predeterminados, configuración de archivo y banderas de CLI (lo último prevalece).
from dataclasses import dataclass
@dataclass
class Config:
retries: int = 3
dry_run: bool = False
def resolve_config(cli_retries: int | None, base: Config) -> Config:
if cli_retries is not None:
return Config(retries=cli_retries, dry_run=base.dry_run)
return base- Precedencia: predeterminados < archivo de configuración < entorno < CLI.
- Las clases de datos inmutables
Configsimplifican las pruebas. - Valida la configuración fusionada antes de cualquier efecto secundario.
Relacionado: Scripts robustos - modo dry-run
10. Guardia principal idempotente
Omite el trabajo cuando la salida ya existe a menos que se use --force.
from pathlib import Path
def build_report(out: Path, force: bool) -> int:
if out.exists() and not force:
print(f"omitir existente {out}")
return 0
out.write_text("datos del informe\n", encoding="utf-8")
return 0- La idempotencia hace que las reejecuciones de cron sean seguras.
--forcedocumenta la sobrescritura intencional.- Las escrituras atómicas (archivo temporal + renombrar) evitan salidas parcialmente escritas - ver la página de automatización de archivos.
Relacionado: Automatización de archivos y directorios - escrituras atómicas
Versiones de la pila: Esta página se escribió para Python 3.14.0 (estable 3.14, mantenimiento 3.13), FastAPI 0.115+, Django 5.2, Flask 3.1, Pydantic 2, PyTorch 2.6+, pandas 2.2+, Polars 1.x, ruff 0.9+ y uv 0.6+.