Concurrent Requests
Asyncio provides multiple ways to run tasks concurrently:
asyncio.gather
asyncio.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_task
method 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_task
method 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.