Concurrent Requests
Asyncio provides multiple ways to run tasks concurrently:
asyncio.gatherasyncio.TaskGroup(Python 3.11+)
These are convenient ways to get the job done for general use-cases, however, they may not be the best fit for Pulsefire use-cases, as such, a modified TaskGroup is provided out of the box for this purpose.
Example Usage
Request first 20 matches of a LoL summoner.
from pulsefire.clients import RiotAPIClient
from pulsefire.schemas import RiotAPISchema
from pulsefire.taskgroups import TaskGroup
async with RiotAPIClient(default_headers={"X-Riot-Token": <API_KEY>}) as client:
account = await client.get_account_v1_by_riot_id(region="americas", game_name="200", tag_line="16384")
summoner = await client.get_lol_summoner_v4_by_puuid(region="na1", puuid=account["puuid"])
match_ids = await client.get_lol_match_v5_match_ids_by_puuid(region="americas", puuid=summoner["puuid"])
async with TaskGroup(asyncio.Semaphore(100)) as tg: #(1)!
for match_id in match_ids[:20]:
await tg.create_task(client.get_lol_match_v5_match(region="americas", id=match_id)) #(2)!
matches: list[RiotAPISchema.LolMatchV5Match] = tg.results() #(3)!
for match in matches:
assert match["metadata"]["matchId"] in match_ids
- Semaphore is optional, provide a semaphore to limit the amount of concurrency.
- Unlike
asyncio.TaskGroup, thecreate_taskmethod is async. - Internal collection of task results and exceptions.
Key differences from asyncio.TaskGroup
- Accepts a semaphore to restrict the amount of concurrent running coroutines.
- Due to semaphore support, the
create_taskmethod is now async. - Allows internal collection of results and exceptions, similar to
asyncio.Task. - If exception collection is on (default), the task group will not abort on task exceptions.
async with RiotAPIClient(default_headers={"X-Riot-Token": <API_KEY>}) as client:
account = await client.get_account_v1_by_riot_id(region="americas", game_name="200", tag_line="16384")
summoner = await client.get_lol_summoner_v4_by_puuid(region="na1", puuid=account["puuid"])
match_ids = await client.get_lol_match_v5_match_ids_by_puuid(region="americas", puuid=summoner["puuid"])
tasks: list[asyncio.Task] = []
async with asyncio.TaskGroup() as tg:
for match_id in match_ids[:20]:
tasks.append(tg.create_task(client.get_lol_match_v5_match(region="americas", id=match_id)))
matches: list[RiotAPISchema.LolMatchV5Match] = [task.result() for task in tasks]
for match in matches:
assert match["metadata"]["matchId"] in match_ids
About asyncio.TaskGroup
- The first time any of the tasks belonging to the group fails with an exception other than
asyncio.CancelledError, the remaining tasks in the group are cancelled, and exceptions are raised to the scope. - Tasks may start to crowd up on memory if task creation is faster than task execution.
async with RiotAPIClient(default_headers={"X-Riot-Token": <API_KEY>}) as client:
account = await client.get_account_v1_by_riot_id(region="americas", game_name="200", tag_line="16384")
summoner = await client.get_lol_summoner_v4_by_puuid(region="na1", puuid=account["puuid"])
match_ids = await client.get_lol_match_v5_match_ids_by_puuid(region="americas", puuid=summoner["puuid"])
matches: list[RiotAPISchema.LolMatchV5Match] = await asyncio.gather(*[
client.get_lol_match_v5_match(region="americas", id=match_id)
for match_id in match_ids[:20]
])
for match in matches:
assert match["metadata"]["matchId"] in match_ids
About asyncio.gather
- Requires scheduling all coroutines at once, may cause memory issues if not properly controlled or semaphored.