Migración de Request síncrono a HTTPX asíncrono en Python

Clase 15 de 17Curso de Python Profesional: Arquitectura de Proyectos, Entornos y PyPI

Resumen

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.