Migración de Request síncrono a HTTPX asíncrono en Python
Clase 15 de 17 • Curso de Python Profesional: Arquitectura de Proyectos, Entornos y PyPI
Adopta un flujo asíncrono en Python con confianza: en Platzi News se migra de Request síncrono a HTTPX con AsyncIO para ejecutar llamadas concurrentes a The Guardian y News API, logrando reducir el tiempo de respuesta ~50% y mantener la compatibilidad hacia atrás gracias a un enfoque de dual approach.
¿Qué cambia con HTTPX y AsyncIO?
La ejecución deja de ser bloqueante. Antes, cada petición HTTP esperaba a la anterior. Con AsyncIO y HTTPX se disparan varias solicitudes de forma concurrente, aprovechando mejor el I/O de red y acelerando la obtención de artículos. Se preservan los métodos síncronos existentes y se agregan equivalentes asíncronos con prefijo “A” para distinguirlos y facilitar una migración gradual.
- Concurrencia real en I/O: múltiples requests al mismo tiempo.
- Prefijo “A” en métodos asíncronos para claridad y consistencia.
- Dual approach: síncrono y asíncrono conviven sin romper usos previos.
- Menos tiempo total: respuesta más rápida, aprox. 50%.
¿Cómo se instala HTTPX?
- Se elige HTTPX por su compatibilidad con AsyncIO.
- Instalación directa: ejecutar en la terminal: V add HTTPX.
- Verificación: aparece en dependencias de pyproject.toml.
¿Cómo se define el nuevo protocolo asíncrono?
- En el módulo source se tiene un protocolo con el método síncrono existente (ej.: fetch...).
- Se agrega el nuevo método asíncrono con prefijo “A” y misma firma y documentación.
- Se usa async def y se mantiene el tipado para retornar la misma estructura de artículos.
¿Cómo se implementa en Guardian y News API?
- En guardian.py se crea el método asíncrono con async def y prefijo “A”.
- Se mueve el logger a la parte superior del archivo para reutilizarlo.
- Se importa httpx y se usa un cliente asíncrono con contexto.
import httpx
async def afetch_articles(params):
# logger definido arriba
async with httpx.AsyncClient() as client:
response = await client.get("https://api.guardian...", params=params)
data = response.json()
# Adaptar a la estructura de artículos esperada
return [ ... ]
- En News API se repite el refactor: mover logger, importar httpx, crear el método con prefijo “A”, usar await y ajustar el mapeo de campos porque los nombres difieren respecto a Guardian.
¿Cómo se adapta el servicio y la CLI?
El servicio en core/services agrega un método asíncrono gemelo del síncrono (misma responsabilidad, prefijo “A”). Internamente, obtiene la fuente con el método actual (sigue siendo síncrono) y llama al nuevo método asíncrono de la fuente con await. Se toca el mínimo de funciones para no afectar otras features ni tests.
class NewsService:
def search_articles(self, ...):
source = self.get_source(...)
return source.fetch_articles(...)
async def a_search_articles(self, ...):
source = self.get_source(...)
return await source.afetch_articles(...)
- Regla de oro: await solo dentro de funciones async. Por eso, al actualizar el uso desde la CLI, la función que invoca el nuevo método se vuelve async.
- Se renombra el main asíncrono y se crea un wrapper síncrono que ejecuta la corrutina con asyncio.run, manteniendo la compatibilidad del punto de entrada.
async def async_main():
# await service.a_search_articles(...)
...
def main():
import asyncio
asyncio.run(async_main())
¿Cómo mantener compatibilidad hacia atrás?
- Compatibilidad: el código existente sigue funcionando con métodos síncronos.
- Migración gradual: módulo por módulo, sin pausas grandes.
- Flexibilidad: scripts simples usan síncrono; producción usa asíncrono.
- Testing: los tests no se rompen al conservar los métodos previos.
¿Cómo verificar que HTTPX está en uso?
La validación práctica incluye ejecutar la búsqueda y luego activar los logs en modo debug para confirmar que HTTPX realiza los requests.
- Ejecutar búsqueda: Platinus search con término y source News API. Muestra artículos correctamente.
- Confirmar HTTPX: PlatzyNews con log level debug para ver los mensajes info/DEBUG de HTTPX indicando requests exitosos.
En conjunto, se logra un refactor sólido: métodos asíncronos con async def, uso de async with httpx.AsyncClient(), llamados con await, logger centralizado y un wrapper con asyncio.run que garantiza compatibilidad. ¿Te animas al reto final? Agrega nuevas funcionalidades asíncronas en el servicio aplicando el mismo patrón.
¿Tienes dudas sobre la migración o quieres compartir tu experiencia con AsyncIO y HTTPX? Deja tu comentario y conversemos.