Package: async-pychasing
An asynchronous version ofpychasing(a wrapper for thehttps://ballchasing.comAPI).Installpip install async-pychasingUsageThis asynchronous wrapper functions similarly topychasing; it is recommended that youread the documentationas it explains the core functionality. Additionally, all helper enumerations and types are used from thepychasingmodule, and are not included withasync-pychasing. There are two main changes in the way this version should be used. Creating aClientis the same (althoughasync_pychasingis used instead ofpychasing):importasync_pychasingimportpychasingpychasing_client=async_pychasing.Client(token="your_token",auto_rate_limit=True,patreon_tier=pychasing.PatreonTier.none)but the way you interact with the client is slightly different; instead of calling methods directly from theClientinstance, anaiohttp.ClientSessioninstance must be passed into theClient, which will then return aasync_pychasing.Sessioninstance. ThisSessioninstance is then how you interact with the API, and contains all the usual methods (although asynchronous). For example:importaiohttpimportasyncio# keep in mind that a new aiohttp.ClientSession is being created simply for illustration. In reality, you should NOT create a new aiohttp.ClientSession for each individual request.asyncdefmain()->None:asyncwithaiohttp.ClientSession()ascs:asyncwithpychasing_client(cs)asbs:# or pychasing_client.session(cs)awaitbs.get_replay(...)if__name__=="__main__":asyncio.run(main())Because we're usingaiohttpinstead ofrequests, the way we download replays through thedownload_replaysmethod is slightly different (we use<response>.content.iter_chunked(...)instead of<response>.iter_content(...)):asyncdefdownload_replay(bs:async_pychasing.Session,replay_id:str)->None:asyncwithbs.download_replay(replay_id)asreplay:withopen("my_replay.replay","wb")asoutfile:# or using an async file managerasyncforchunkinreplay.content.iter_chunks(4096):outfile.write(chunk)Notes on rate limitingBy default (if rate limiting is enabled) the methods are rate limited similarly topychasing, in which one function call waits until the previous call has completed. For faster call speeds, this can be avoided by using theconcurrentandca_deviationoptions uponClientinstantiation, although there are some drawbacks. The main drawback is that there is a posibility for getting rate-limited by the API, whereas withconcurrent=Falsethat is not the case. Ifconcurrent=True, the methods will be rate limited without consideration for execution time of the HTTP[s] request. To help account for this time, you can setca_deviationto a float; this will cause each function to take <ca_deviation> seconds longer before allowing the next function call to go through, which can allow for some extra leeway with request speed variation. To optimize speed, you should try to separate your API calls from your processing. For example (listing 10 replays, then getting the GUID of each replay):importasync_pychasingimportpychasingimportaiohttpimportasyncioclient=async_pychasing.Client(...)asyncdefget_guids(guids:list[str],responses:asyncio.Queue[aiohttp.ClientResponse],break_after:int)->None:foriinrange(break_after):r=awaitresponses.get()j=awaitr.json()guids.append(j["match_guid"])# ONLY getting the replay and putting it into the queueasyncdeffetch_replay(bs:async_pychasing.Session,id:str,responses:asyncio.Queue[aiohttp.ClientResponse])->None:res=awaitbs.get_replay(id)awaitresponses.put(res)asyncdefmain(guids:list[str],responses:asyncio.Queue[aiohttp.ClientResponse])->None:asyncwithaiohttp.ClientSession()asclient_session:asyncwithclient(client_session)asballchasing_session:replay_list=awaitbs.list_replays(count=10)replay_list_json=awaitreplay_list.json()replay_ids=[r["id"]forrinreplay_list_json["list"]]loop=asyncio.get_event_loop()tasks:list[asyncio.Task]=[]foridinreplay_ids:tasks.append(loop.create_task(fetch_replay(ballchasing_session,id,responses)))tasks.append(loop.create_task(get_guids(guids,responses,len(replay_ids))))awaitasyncio.gather(*tasks)if__name__=="__main__":asyncio.run(main([],asyncio.Queue()))