'How do I make async HTTP requests using httpx (VS requests) in Python?

I'm new to httpx and async and I'm having trouble converting a function that calls to an external API using requests into an asynchronous function using httpx.

This is the original function:

import requests


def get_api_data(url: str) -> Optional[List[str]]:
    """Get the data from an API url.
    Args:
        url (str):  The API url.
    Returns:
        list: The data.
    """
    try:
        resp = requests.get(url)
        return resp.json()
    except requests.exceptions.HTTPError as errh:
        print("Http Error:", errh)
    except requests.exceptions.ConnectionError as errc:
        print("Error Connecting:", errc)
    except requests.exceptions.Timeout as errt:
        print("Timeout Error:", errt)
    except requests.exceptions.RequestException as err:
        print("OOps: Something Else", err)

and this is what I've tried:

import httpx


async def get_api_data(url: str) -> Optional[List[Any]]:
    """Get the data from an API url.
    Args:
        url (str):  The API url.
    Returns:
        list: The data.
    """
    try:
        async with httpx.AsyncClient() as client:
            resp = await client.get(url)
            return resp.json()
    except httpx.HTTPError as errh:
        print("Http Error:", errh)

But I'm not sure it's a valid code and how to test it...

An example of a test before changing the function (the requests version); It uses patch to mock:

import pytest
import requests

from unittest.mock import patch


def test_api_http_error():
    with patch('app.internal.world_clock.requests.get', side_effect=requests.exceptions.HTTPError):
        assert not get_api_data(TIMEZONES_BASE_URL)

I spent a lot of hours trying to figure this out, so if you have any suggestions for how to make this conversion properly and how to test it - I would greatly appreciate it.



Solution 1:[1]

Your function looks fine. Are you running your async function? I had that issue when I first tried using it to replace requests in my project. Instead of just calling it like: results = async_funtion(parameter) It needed to be: results = asyncio.run(async_funtion(parameter)) In the first example you are just creating a coroutine object, the second actually runs it.

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 RedArmyBushMan