1.15 · Librerías externas¶
Objetivos. Al terminar este capítulo podrás: distinguir biblioteca estándar
y paquete externo; justificar la elección de una dependencia; usar httpx2
como cliente HTTP y configuración desde el entorno; y reutilizar pandas y NumPy
sin repetir su teoría.
Evidencia de logro. Compararás alternativas, documentarás la elección de una dependencia, consumirás una respuesta HTTP servida localmente y procesarás su resultado con las herramientas de datos del proyecto, dejando documentadas las decisiones y los paquetes opcionales.
Contexto y motivación¶
La biblioteca estándar cubre muchas tareas, pero una aplicación de IA suele necesitar clientes HTTP, validación, datos numéricos o un framework web. Una librería externa puede ahorrar trabajo, pero también añade versiones que deben declararse, comprobarse y mantenerse.
Una dependencia entra cuando resuelve una necesidad concreta y su coste está
justificado. La declaración del proyecto se conserva en pyproject.toml; aquí
nos centraremos en elegir una API adecuada y en comprobar su comportamiento.
Vocabulario¶
| Término | Significado |
|---|---|
| Paquete | Distribución instalable desde un índice como PyPI |
| Dependencia | Paquete que otro proyecto necesita |
pyproject.toml |
Manifiesto con metadatos y dependencias directas |
| Dependencia directa | Paquete que el proyecto declara y utiliza explícitamente |
| Dependencia transitiva | Paquete instalado porque otro lo necesita |
| API | Interfaz pública que una librería ofrece |
| Cliente HTTP | Código que realiza peticiones a un servidor |
| Credencial | Dato secreto que identifica o autoriza una petición |
Prerrequisitos¶
1.1 · Introducción y entorno, 1.10 · Archivos y 1.14 · Biblioteca estándar. Para la parte de datos, consulta 1.4 · NumPy y 1.5 · Pandas del módulo de Machine Learning; aquí se usan pandas y NumPy, no se repite su fundamento.
1. Biblioteca estándar o paquete externo¶
Antes de instalar algo, formula la necesidad y compara:
| Necesidad | Primera opción | Paquete externo cuando... |
|---|---|---|
| JSON, rutas, CSV | Biblioteca estándar | necesitas una integración específica |
| Petición HTTP | urllib o un cliente como httpx2 |
quieres una API más cómoda y segura |
| Tabla de datos | pandas | el problema necesita sus operaciones tabulares |
| Cálculo vectorizado | NumPy | necesitas arrays y operaciones numéricas |
| Leer Excel | csv para CSV |
el formato requiere openpyxl |
| HTML | re solo para casos triviales |
necesitas analizar un documento: beautifulsoup4 |
| Imagen | Biblioteca estándar no basta | usa Pillow u otra librería especializada |
No se deben instalar todas las alternativas «por si acaso». Una dependencia entra cuando resuelve una necesidad concreta y su coste está justificado.
2. Elegir y declarar una dependencia¶
Antes de utilizar una librería externa, revisa su nombre de distribución, API
pública, compatibilidad con la versión de Python del proyecto, licencia,
actividad y avisos de seguridad. Una dependencia directa debe quedar declarada
en pyproject.toml junto con una restricción de versión razonable:
En tu proyecto no editas esa lista a mano: la escribe uv add, que además
resuelve las versiones, actualiza uv.lock e instala los paquetes en .venv.
Para seguir este capítulo en tu proyecto de la unidad:
Efecto observable: las cuatro dependencias aparecen en pyproject.toml y
uv run python -c "import httpx2, dotenv, pandas, numpy" termina sin errores.
El detalle de uv add, uv.lock y uv sync está en la guía de
uv. Aquí interesa sobre todo justificar
qué paquete resuelve el problema y qué coste de mantenimiento añade.
Por qué httpx2 y no otro cliente
Durante años, el cliente HTTP de referencia en Python fue requests, y
más tarde httpx (que añadía async y HTTP/2). En 2026, httpx entró en
modo de mantenimiento con actividad limitada, y Pydantic recogió el
testigo creando httpx2: la misma API, mantenida activamente, con
actualizaciones de seguridad garantizadas para una librería que está en el
camino crítico de muchísimos sistemas en producción.
La lección de dependencias que este caso enseña es doble: una librería popular puede agotarse (su autor se mueve a otra cosa), y lo que salva un ecosistema es que otro equipo con credenciales la continúe. Al elegir una dependencia, la actividad del proyecto pesa tanto como su API: un cliente perfecto sin mantenimiento es una deuda futura.
3. Cliente HTTP sin depender de una red real¶
Un cliente HTTP debe definir timeout, comprobar el estado y tratar la respuesta
como datos no confiables. Para aprenderlo sin Internet levantamos un servidor
local que responde con JSON y hacemos la petición con httpx2.
Qué parte hay que entender
La función servidor_json es infraestructura para poder practicar sin
Internet: arranca en segundo plano un servidor web mínimo que siempre
responde con el JSON que le indiques. Cópiala tal cual; no necesitas
entender cómo está hecha (usa hilos y el módulo http.server, que no son
materia de esta unidad). Lo importante del ejemplo son las cuatro líneas
finales, las del bloque with httpx2.Client().
import json
from contextlib import contextmanager
from http.server import BaseHTTPRequestHandler, ThreadingHTTPServer
from threading import Thread
import httpx2
@contextmanager
def servidor_json(cuerpo, estado=200):
class Manejador(BaseHTTPRequestHandler):
def do_GET(self):
contenido = json.dumps(cuerpo).encode("utf-8")
self.send_response(estado)
self.send_header("Content-Type", "application/json")
self.send_header("Content-Length", str(len(contenido)))
self.end_headers()
self.wfile.write(contenido)
def log_message(self, formato, *argumentos):
pass
servidor = ThreadingHTTPServer(("127.0.0.1", 0), Manejador)
hilo = Thread(target=servidor.serve_forever, daemon=True)
hilo.start()
try:
yield f"http://127.0.0.1:{servidor.server_port}"
finally:
servidor.shutdown()
servidor.server_close()
with servidor_json({"modelo": "demo", "estado": "disponible"}) as base_url:
with httpx2.Client() as cliente:
respuesta = cliente.get(f"{base_url}/modelo", timeout=5.0)
respuesta.raise_for_status()
datos = respuesta.json()
print(datos["modelo"], datos["estado"])
Salida esperada:
Lee esas cuatro líneas en orden:
httpx2.Client()crea un cliente HTTP; conwith, sus conexiones se cierran al terminar, como un fichero.cliente.get(url, timeout=5.0)pide la URL y espera como mucho 5 segundos; sin timeout, un servidor que no responde bloquearía el programa.respuesta.raise_for_status()lanza una excepción si el servidor respondió con un código de error (4xx o 5xx), en lugar de seguir con datos vacíos.respuesta.json()convierte el cuerpo JSON en diccionarios y listas de Python.
El servidor solo escucha en 127.0.0.1 (tu propio equipo), por lo que la
salida es determinista y no depende de Internet.
En una integración real añadirías reintentos limitados para errores transitorios, registrarías el identificador de la petición y nunca imprimirías una clave secreta. Los reintentos y la autenticación se amplían en la UD2.
4. Configuración sin secretos en el código¶
Un fichero .env local puede contener configuración que no debe versionarse:
Con python-dotenv se puede leer sin convertirlo en una constante del código:
from pathlib import Path
from dotenv import dotenv_values
ruta_env = Path(".env_ejemplo")
ruta_env.write_text("ASISTENTE_MODELO=demo\nASISTENTE_TIMEOUT=5\n", encoding="utf-8")
configuracion = dotenv_values(ruta_env)
modelo = configuracion.get("ASISTENTE_MODELO", "local")
timeout = float(configuracion.get("ASISTENTE_TIMEOUT", "5"))
print(modelo, timeout)
ruta_env.unlink(missing_ok=True)
Salida esperada:
El bloque crea un .env_ejemplo temporal para que la salida no dependa de un
fichero previo, y lo elimina al terminar. Añade .env a .gitignore y entrega
un .env.example sin valores secretos.
En producción, el proveedor de despliegue debe inyectar las variables; no se suben claves a GitHub ni se escriben en el cuaderno.
Dependencia opcional
python-dotenv facilita el desarrollo local, pero el principio no depende
de ella: Python también ofrece os.environ. No confundas «se puede leer un
.env» con «es seguro publicar su contenido».
5. Usar pandas y NumPy sin repetir el módulo de ML¶
En este módulo los usamos como piezas de una aplicación. El análisis profundo de tablas, arrays, dtypes y rendimiento pertenece a Machine Learning.
import numpy as np
import pandas as pd
respuestas = [
{"usuario": "ana", "puntuacion": 0.8},
{"usuario": "luis", "puntuacion": 0.6},
]
tabla = pd.DataFrame(respuestas)
tabla["puntuacion_porcentaje"] = np.round(tabla["puntuacion"] * 100, 1)
print(tabla[["usuario", "puntuacion_porcentaje"]].to_dict("records"))
Salida esperada:
[{'usuario': 'ana', 'puntuacion_porcentaje': 80.0}, {'usuario': 'luis', 'puntuacion_porcentaje': 60.0}]
La representación es explícita: el servicio entrega puntuaciones entre 0 y 1 y la interfaz necesita porcentajes. En pandas 3.x conviene comprobar dtypes y no basarse en conversiones implícitas accidentales; el módulo de ML desarrolla esas decisiones con más profundidad.
6. Elegir un paquete desde PyPI¶
Antes de añadir una distribución revisa:
- nombre exacto y API pública;
- versión de Python y dependencias compatibles;
- licencia y actividad del proyecto;
- documentación, historial de versiones y avisos de seguridad;
- si la funcionalidad puede resolverse con una dependencia ya instalada.
El nombre de la distribución y el nombre que se importa pueden ser distintos.
Por ejemplo, se instala python-dotenv pero se importa dotenv. No copies el
nombre del comando de instalación sin comprobar la documentación.
6.1 Catálogo de ampliaciones¶
Los cuatro primeros paquetes de la tabla son útiles, pero no hacen falta para esta unidad. Te los señalamos como mapa de elección: cuando en el curso o en tu trabajo aparezcan, sabrás para qué son y si te conviene instalarlos.
| Paquete | Caso de uso | Requisito para esta unidad |
|---|---|---|
requests |
HTTP sencillo | No; se prioriza httpx2 |
beautifulsoup4 |
Parsear HTML | No; ampliación |
Pillow |
Transformar imágenes | No; ampliación |
openpyxl |
Libros Excel | No; ampliación |
httpx2 |
Cliente HTTP | Sí para esta práctica |
python-dotenv |
Configuración local | Sí para esta práctica |
La tabla evita que una persona principiante tenga que instalar cinco paquetes para resolver una sola tarea.
Aplicación práctica: pipeline local de una respuesta¶
Este flujo simula una petición, transforma el resultado y genera un registro listo para guardar:
# Se reutiliza `servidor_json`, definido en el ejemplo anterior.
import httpx2
import numpy as np
import pandas as pd
with servidor_json({"resultados": [0.8, 0.6, 0.9]}) as base_url:
with httpx2.Client() as cliente:
resultado = cliente.get(f"{base_url}/puntuaciones").raise_for_status()
print("tipo de resultado:", type(resultado).__name__)
Salida esperada:
En httpx2, raise_for_status() devuelve la propia respuesta cuando no hay
error, así que se puede encadenar en la misma línea. Funciona, pero mezcla dos
pasos (pedir y comprobar) y no todas las librerías se comportan igual. Para que
cada etapa sea visible y el patrón sea fácil de trasladar a otras librerías,
suele ser más claro conservar la respuesta en una variable:
with servidor_json({"resultados": [0.8, 0.6, 0.9]}) as base_url:
with httpx2.Client() as cliente:
respuesta = cliente.get(f"{base_url}/puntuaciones", timeout=5.0)
respuesta.raise_for_status()
tabla = pd.DataFrame({"puntuacion": respuesta.json()["resultados"]})
tabla["porcentaje"] = np.round(tabla["puntuacion"] * 100, 1)
print(tabla.to_dict("records"))
Salida esperada:
[{'puntuacion': 0.8, 'porcentaje': 80.0}, {'puntuacion': 0.6, 'porcentaje': 60.0}, {'puntuacion': 0.9, 'porcentaje': 90.0}]
Las dos versiones funcionan. La segunda es la recomendada: conserva cada etapa (pedir, comprobar, convertir, transformar) y hace visible la transformación. Si un paso falla, el traceback señala exactamente cuál.
Errores frecuentes¶
- Preparar un entorno distinto del proyecto y luego ejecutar con otro intérprete.
- Usar una dependencia que no está declarada en
pyproject.toml. - Usar una llamada HTTP real en una práctica que debería ser reproducible.
- No definir timeout ni comprobar
raise_for_status(). - Subir
.env, tokens o claves a GitHub. - Confundir paquete distribuido e importación (
python-dotenvfrente adotenv). - Repetir la teoría de pandas o NumPy en lugar de explicar su papel en la aplicación.
- Añadir una librería grande cuando la biblioteca estándar ya resuelve el caso.
Práctica de transferencia¶
- Compara
httpx2con otra alternativa y dejahttpx2ypython-dotenvdeclarados enpyproject.toml; comprueba que el entorno del proyecto puede importarlos. - Sustituye una petición real por un servidor local y diseña respuestas 200 y
404; explica cómo las distinguirías con
raise_for_status(). - Crea
.env.example, añade.enva.gitignorey lee una configuración sin imprimir ningún secreto. - Convierte tres respuestas simuladas en una tabla pandas y calcula una columna derivada con NumPy.
- Escribe una nota de decisión: qué paquete elegiste, qué alternativa descartaste y qué coste de mantenimiento introduces.
Producto evaluable¶
Crea 11_dependencias.py como script o cuaderno marimo. Debe:
- Mostrar la declaración de dependencias en
pyproject.tomly justificar la elección dehttpx2frente a una alternativa. - Consumir una respuesta HTTP simulada con timeout y comprobación de estado.
- Leer una configuración de ejemplo sin incluir credenciales reales.
- Transformar la respuesta con pandas y NumPy, indicando qué conocimiento se reutiliza del módulo de ML.
- Incluir una tabla de dependencias directas, opcionales y de desarrollo.
Criterio de aceptación: otra persona puede preparar el entorno del proyecto y repetir la práctica sin Internet ni secretos; la entrega explica qué sucede cuando el servicio responde con error.
Formato de entrega: script o cuaderno marimo ejecutado, con salida esperada, interpretación y conclusión.
Resumen y referencia rápida¶
| Tarea | Comando o patrón |
|---|---|
| Declarar dependencia | Entrada en pyproject.toml |
| Cliente con timeout | httpx2.Client() y cliente.get(..., timeout=5.0) |
| Comprobar HTTP | respuesta.raise_for_status() |
Leer .env |
dotenv_values(".env") |
Siguiente: 1.16 · Asincronía con asyncio.