Primer script y validación de parámetros en Python
Metadatos de la lección
- Autoría:
- Ignacio Slater-Muñoz
- Última actualización:
- 27 de marzo de 2026
Cambios recientes:
-
8880728· 27 de marzo de 2026 · ✨ feat(notes): add abstract slots and Python structured-output lesson ( GitLab / GitHub ) -
c2972b0· 26 de marzo de 2026 · 🐛✅ fix(types): unblock astro check in CI ( GitLab / GitHub ) -
29b0ae0· 11 de marzo de 2026 · 📝✨ docs(scripting): add Nushell first-script lesson and inline code guidance ( GitLab / GitHub )
Encuentra el código de la lección:
Abstract
Validar argumentos con argparseargparse
argparseargparse
En PowerShell, buena parte del «contrato» del script (parámetros
obligatorios, validaciones, errores) está integrado en el lenguaje mediante atributos
como
o [Parameter(Mandatory)][Parameter(Mandatory)]. Python, si bien es un lenguaje conocido por proveer una amplia gama de
funcionalidades out-of-the-box, incorpora estas funcionalidades mediante su
biblioteca estándar y no como características del lenguaje. Esto significa que escribir
el contrato que deseamos requerirá más «pegamento» manual; aquí es
donde cobra protagonismo [ValidateScript(...)][ValidateScript(...)].
argparseargparse
A través de podemos declarar argumentos posicionales
y opcionales, marcar algunos como obligatorios, asignarles valores por defecto,
documentarlos automáticamente en la ayuda (argparseargparse--help) y validar su forma
básica (por ejemplo, si son enteros, strings o rutas).
Para empezar, construiremos un bloque reutilizable: un validador que recibe un string y
lo convierte a un , fallando con un error entendible si la
ruta no es un directorio. La idea es que luego podamos usarlo en el parser como PathPathtype=validated_directory.
import argparse
from pathlib import Path
def validated_directory(value: str) -> Path:
p = Path(value)
if not p.is_dir():
raise argparse.ArgumentTypeError(f"'{value}' is not a valid directory")
return p Detalles clave
- Type hints:
indica que esperamos un string, yvalue: strvalue: strque devolvemos un-> Path-> Path. En Python esto es una anotación para herramientas (IDE, linters, mypy), no una restricción obligatoria en runtime.PathPath -
: convierte el texto recibido (por ejemploPath(value)Path(value)"./scripts") en un objeto de ruta multiplataforma, evitando manipular strings manualmente. -
: verifica si la ruta existe y corresponde a un directorio (no a un archivo). Si quieres permitir rutas inexistentes que se crearán después, este chequeo sería distinto.p.is_dir()p.is_dir() -
: es la excepción «oficial» para validadores de tipo enraise argparse.ArgumentTypeErrorraise argparse.ArgumentTypeError. Hace que el parser muestre el mensaje de error y termine con un output de uso consistente.argparseargparse
Construyendo un parser con argparseargparse
argparseargparse
Ahora que ya contamos con un bloque reutilizable para validar directorios (nuestro
), el siguiente paso es construir el parser de la línea de comandos. En PowerShell, este tipo de contrato suele quedar
«pegado» al propio script vía atributos. En Python lo expresamos
configurando un objeto que describe: qué banderas existen, qué tipo de datos aceptan,
qué valores por defecto usan y qué errores deben mostrarse si la invocación es inválida.
validated_directory()validated_directory()
En nuestro caso, el programa generará un README. Para eso necesitamos:
- Un nombre obligatorio (
--name) para personalizar el contenido. - Un modo verboso (
-v/--verbose) para imprimir información extra. - Un directorio de salida opcional (
-o/--out_dir), validado como directorio.
Todo esto se declara en una función , que
devuelve un build_parser()build_parser() ya configurado.
ArgumentParserArgumentParser
import argparse
from readme_gen.validated_directory import validated_directory
def build_parser() -> argparse.ArgumentParser:
p = argparse.ArgumentParser(
prog="readme-gen",
description="Generate a README.md from simple parameters.",
)
p.add_argument(
"--name",
required=True,
help="Project name (used in the README title and sections).",
)
p.add_argument(
"-v",
"--verbose",
action="store_true",
help="Enable verbose output (extra messages during execution).",
)
p.add_argument(
"-o",
"--out_dir",
type=validated_directory,
default=".",
help="Directory where README.md will be written (default: current directory).",
)
return p Detalles clave
crea el objeto que concentra toda la
definición de nuestra interfaz de línea de comandos. Al inicializarlo, podemos
ajustar detalles como:
ArgumentParser()ArgumentParser()
-
: el nombre que aparecerá en el texto de ayuda (útil si el script se ejecuta como comando).progprog -
: un resumen que se muestra al usardescriptiondescription, para dar contexto rápido.--help--help
La función clave es : cada llamada registra
un argumento. Lo interesante es que varios parámetros de add_argument()add_argument() equivalen a partes del «contrato» :
add_argument()add_argument()
-
: el argumento es obligatorio. Si falta,required=Truerequired=Trueimprime un error, muestra la ayuda y termina el programa con un código de salida de error.argparseargparse -
: convierte una bandera en un booleano. Si la bandera aparece, el valor seráaction="store_true"action="store_true"; si no aparece, seráTrueTrue. Esto es ideal para flags comoFalseFalse.--verbose--verbose -
define cómo convertir el texto recibido a un tipo útil. Aquí usamostype=...type=..., que transforma un string envalidated_directoryvalidated_directoryy falla con un error claro si no es un directorio válido. Es importante notar que pasamos la función sin paréntesis: no la ejecutamos aquí, sino que entregamos una referencia para quePathPathla invoque al procesar los argumentos.argparseargparse -
: valor por defecto cuando el usuario no entrega el argumento. En este ejemplo, el directorio de salida será el directorio actual.default="."default="."
Logging en Python con el módulo logginglogging
logginglogging
En PowerShell es común escribir mensajes para depurar o diagnosticar usando
, Write-VerboseWrite-Verbose, o Write-WarningWrite-Warning. Python ofrece algo similar (aunque con
diferencias sutiles) 1 a través del módulo estándar Write-ErrorWrite-Error: una forma centralizada de emitir mensajes con distintos niveles (loggingloggingDEBUG,
INFO, WARNING, etc.) y decidir cuáles se muestran según el
modo de ejecución.
La idea es simple: si se activa , mostramos
detalles de depuración; si no, dejamos solo advertencias (o mensajes más importantes).
--verbose--verbose
import logging
def setup_logging(verbose: bool) -> logging.Logger:
level = logging.DEBUG if verbose else logging.WARNING
logging.basicConfig(level=level, format="%(message)s")
return logging.getLogger(__name__) Detalles clave
Esta función define una política mínima de logging para toda la app:
-
selecciona el nivel global. Cuandolevel = ...level = ...usamosverbose == Trueverbose == True(mensajes detallados); si no, usamoslogging.DEBUGlogging.DEBUG(solo advertencias y más graves).logging.WARNINGlogging.WARNING -
aplica una configuración base. Aquí fijamos:logging.basicConfig(...)logging.basicConfig(...)para filtrar mensajes ylevellevelpara imprimir solo el contenido del mensaje (sin prefijos como fecha, nombre del logger, etc.). La sintaxis deformat="%(message)s"format="%(message)s"corresponde al sistema de atributos deformatformatLogRecord. -
entrega un logger nombrado.logging.getLogger(__name__)logging.getLogger(__name__)es una variable especial de Python que contiene el nombre del módulo actual (por ejemplo,__name____name__readme_gen.app_logging). Esto permite que los mensajes indiquen de qué parte del programa vienen si luego cambiamos el formato, o si configuramos distintos niveles por módulo.
Punto de entrada y escritura del README
Ya tenemos las piezas principales: un parser () y una
configuración de logging (argparseargparse). Ahora toca unir todo en un
script ejecutable: parseamos argumentos, generamos el contenido y lo escribimos en
loggingloggingREADME.md.
En PowerShell, normalmente devolveríamos un string y dejaríamos que quien consume el script decida si lo redirige a un archivo, a la salida estándar o a otro destino mediante el pipeline. En Python, lograr exactamente ese mismo estilo de composición es menos directo, así que aquí optaremos por un enfoque simple: escribir directamente a un archivo en el directorio de salida.
from datetime import datetime
from pathlib import Path
import sys
from readme_gen.app_logging import setup_logging
from readme_gen.parser import build_parser
def write_readme(content: str, out_dir: Path) -> None:
out_dir.mkdir(parents=True, exist_ok=True)
target = out_dir / "README.md"
with target.open("w", encoding="utf-8") as f:
f.write(content)
def main(argv: list[str]) -> int:
args = build_parser().parse_args(argv)
logger = setup_logging(args.verbose)
logger.info("Creating README.md for project '%s'", args.name)
content = (
f"# {args.name}\n\n"
f"Project initialized on {datetime.now():%Y-%m-%d %H:%M:%S}.\n\n"
"Learn more about READMEs at https://www.makeareadme.com/.\n"
)
write_readme(content, args.out_dir)
return 0
if __name__ == "__main__":
sys.exit(main(sys.argv[1:]))README.md.
Detalles clave
Aquí están los puntos clave del script:
-
crea el directorio de salida si no existe. Conmkdir(parents=True, exist_ok=True)mkdir(parents=True, exist_ok=True)se crean también directorios intermedios, y conparents=Trueparents=Trueno falla si ya existía.exist_ok=Trueexist_ok=True -
abre el archivo de forma segura: al salir del bloque se cierra automáticamente, incluso si ocurre un error.with target.open(...) as f:with target.open(...) as f: -
escribe el string completo al archivo.f.write(content)f.write(content) -
toma la lista de strings (sin el nombre del programa) y la convierte en un objeto con atributos (parse_args(argv)parse_args(argv),args.nameargs.name, etc.).args.out_dirargs.out_dir -
emite un mensaje de diagnóstico. Silogger.info(...)logger.info(...)no está activo, este mensaje no se muestra (porque el nivel queda en--verbose--verbose).WARNINGWARNING - El bloque
concatena strings de manera legible: cada línea es un literal separado, pero Python los une en un solo string final.(f"...")(f"...") -
yreturn 0return 0establecen el código de salida del proceso. Esto es importante para automatización (CI, scripts, tareas):sys.exit(...)sys.exit(...)suele indicar éxito, y otros valores indican fallas.00 -
hace que el archivo se pueda ejecutar como script sin queif __name__ == "__main__":if __name__ == "__main__":corra al importarlo como módulo. Además,mainmainomite el primer argumento (el nombre del programa).sys.argv[1:]sys.argv[1:]
Uso
-m -m le indica a Python que ejecute un módulo (o submódulo) usando el sistema de importaciones, en lugar de un archivo
.py directo. Esto asegura que los imports funcionen correctamente dentro
del paquete (readme_gen) y que el punto de entrada sea el bloque
if __name__ == "__main__"if __name__ == "__main__".
-m # Muestra la ayuda generada por argparse
python -m readme_gen.new_readme --help
# Genera un README en el directorio actual
python -m readme_gen.new_readme --name 'Utility Scripts - DIBS' --verboseConclusiones
En esta lección construimos un script completo en Python desde cero, recorriendo el camino habitual de una herramienta de línea de comandos: definición del contrato, validación de entradas, diagnóstico controlado y ejecución con un punto de entrada explícito.
A diferencia de PowerShell, donde muchas de estas decisiones están integradas en el propio lenguaje y su modelo de pipeline, Python delega estas responsabilidades en módulos bien definidos de su biblioteca estándar, lo que lleva a más «código pegamento» explícito.
El resultado no es solo un script funcional, sino una estructura que puede crecer: validadores reutilizables, parsers testeables, logging configurable y un flujo de ejecución claro.
Puntos clave
-
permite declarar y validar el contrato de un script de forma explícita.argparseargparse - Los validadores personalizados convierten texto en tipos útiles antes de que la lógica principal se ejecute.
-
separa los mensajes de diagnóstico del flujo principal del programa.logginglogging - El patrón
define un punto de entrada claro y reusable.if __name__ == "__main__"if __name__ == "__main__"
¿Qué nos llevamos?
Más allá de la sintaxis, esta lección busca dejar una idea central: escribir scripts no es solo hacer que algo funcione, sino definir contratos claros entre quien escribe la herramienta y quien la usa.
Python no ofrece un pipeline de objetos como PowerShell, pero compensa esa ausencia con una biblioteca estándar rica y coherente. Aprender a combinar estas piezas —parsers, validadores, logging y puntos de entrada— es clave para escribir herramientas pequeñas, pero bien diseñadas.
¿Con ganas de más?
Referencias recomendadas
- Cubre los fundamentos de entrada y salida en Python, incluyendo manipulación de archivos, flujos estándar (stdin/stdout/stderr) y la construcción de interfaces de línea de comandos. Útil para entender cómo Python gestiona los datos de entrada/salida y la interacción con el sistema, conceptos clave al trabajar con
yargparseargparseen scripts.logginglogging
Notas
-
A diferencia de PowerShell, donde los cmdlets
envían objetos a distintos canales del pipeline (verbose, warning, error, etc.), el móduloWrite-_Write-_se limita a emitir mensajes de texto con distintos niveles de severidad. El control fino no ocurre en el flujo de datos, sino en la configuración del logger (niveles, handlers y formato). Volverlogginglogging