2

I've been grokking some of my sample async code and I'm struggling to understand the flow of the data and how often I find myself awaiting.

Am I using await correctly? Also - are my output types correct? I'd imagine some these should be outputting coroutines (but my Pyright/Pylance type hinting keeps telling me that's wrong...)

The functions that require inspection are governed requests, send, governed_send, and governed_sendall. Maybe I'm not understanding what await is doing completely - and how this interacts with function scopes.

The code works correctly; I'd like to understand why and whether I can optimize out some of these awaits or if that's unnecessary.

import time import asyncio import httpx from typing import Any, Callable, Coroutine, Dict, List from rich import print class GovernedClient: def __init__(self): self.client = httpx.AsyncClient() self.loop = asyncio.get_event_loop() self.semaphore = asyncio.BoundedSemaphore(4) def __del__(self) -> None: # Define a destructor that closes the client and the loop. async def close(self: GovernedClient) -> None: # An async function is required to await aclose await self.client.aclose() self.loop.run_until_complete(close(self)) self.loop.close() def govern_requests(fn: Callable) -> Callable: # govern_requests applies semaphore locking to a given callable. async def call(self, *args, **kwargs): async with self.semaphore: return await fn(self, *args, **kwargs) return call async def send(self, method: str, url: str) -> httpx.Response: # A single send. request = httpx.Request(method, url, headers={'accept': 'application/json'}) return await self.client.send(request) @govern_requests async def governed_send(self, method: str, url: str) -> httpx.Response: # Applies semaphore locking via decorator. return await self.send(method, url) def governed_sendall(self, urls: List[str]) -> List[httpx.Response]: async def goverened_sendall(urls: List[str]) -> List[httpx.Response]: start = time.time() awaitables = [self.governed_send('GET', url) for url in urls] responses = [] for response in asyncio.as_completed(awaitables): responses.append(await response) print('Total time: ', int(1000 * (time.time() - start))) return responses return self.loop.run_until_complete(goverened_sendall(urls)) if __name__ == '__main__': URL = 'https://www.icanhazdadjoke.com' bc = GovernedClient() urls = [URL for url in range(20)] responses = bc.governed_sendall(urls) for response in responses: print(response.json()) 
1
  • Yeah, what confuses me I guess are the types and tracing them. For instance, governed send retirns an awaited response but is this a response or a coroutine or both? How does await affecr types? When that awaited function is invoked before syncronization, what type is it? Isnt its return type a future? In writing this I had to experiment with await placement there were one or two cases where I accidentally synced my web calls Commented May 31, 2021 at 9:04

1 Answer 1

1

You know that async is a function that will not block the code. Right? You know that await is an async function caller that when called it will not block the code. Right? So There's no using too much async/await because if you using it it will not blocking your code unlike def and functioname(). I guess you may understand. Happy day!

Sign up to request clarification or add additional context in comments.

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.