Create custom async context managers

Recently I found myself trying to set up some code to mimic some functionality of Depends from FaspAPI in other async code. After a few false starts, I found the asynccontextmanager helper from contextlib, which made the process a breeze.

It’s super helpful for quickly creating your own context manager wrappers, all you need is an (async) function that yield’s once

import os
from contextlib import asynccontextmanager

import httpx

headers = httpx.Headers(
    {
        'Content-Type': 'application/json',
        'Authorization': f'Bearer {os.environ["MY_TOKEN"]}',
    }
)
base_url = 'https://example.com/api/'


@asynccontextmanager
async def custom_client() -> httpx.AsyncClient:
    async with httpx.AsyncClient(
        base_url=base_url,
        headers=headers,
    ) as client:
        yield client


async def function_using_using_client(thing_id: int) -> dict:
    async with custom_client() as client:
        r = await client.get(f'/thing/{thing_id}')
        r.raise_for_status()
    return r.json()

For synchronous versions it’s basically the same, just remove all instances of async and await.

This example could be extended to enable reuse of a single global client at scale via a singleton pattern