Package: asyncio-throttler
asyncioandasync/awaitare pretty awesome tools!
Asynchronous stuff! In Python! Without insanity!Well, unfortunately given the nature of this style of programming it’s
not bloody easy sometimes.Well, this project provides at least one tool to help ya out in a
reasonably simple way.asyncio_throttleris a throttling system for Python 3.5+, designed
aiohttp throttling in mind but designed to be flexible enough to handle
most throttling and rate limiting needs.UsageWell, get it:$ pyvenv env/
$ . env/bin/activate
$ pip install asyncio_throttler # Pin the damned version in setup.py
                                # you bloody savage.If you don’t have Python 3.5, check outpyenv(not to be confused with
pyvenv), a Python version manager similar to rbenv.If you really don’t want pyenv,brew install python3will
non-destructively install Python 3.5+.Anyways, here’s the terrible usage example I wrote while developing the
thing. The code is well documented, concise, and hopefully easily
understandable by humans, but this should getcha started. I’ll make
better docs, I promise. I gotta sleep at some point.WARNING: THIS WILL NEVER COMPLETE, THE THROTTLE ERROR WILL BOUNCE
AROUND FOREVER. ON PURPOSE. TO DEMONSTRATE THINGS DON’T GET LOST.
SERIOUSLY."""Dump test module I built while writing this thing. Need to make real tests,
but whatcha gonna do ya got schedules and stuff amirite?

"""importloggingimportasynciofromasyncio_throttlerimportThrottler,ThrottleException# Demonstrates that windowing, throttling, and every other known feature# works, I think.if__name__=='__main__':logger=logging.getLogger('testthrottler')logger.setLevel(logging.DEBUG)handler=logging.StreamHandler()format_template='%(asctime)s:%(name)s:%(levelname)s–%(message)s'handler.setFormatter(logging.Formatter(fmt=format_template,datefmt='%Y-%m-%d%H:%M:%S'))logger.addHandler(handler)asyncdefdummy_consumer(item):print("Item received:",item)awaitasyncio.sleep(2)importrandomasyncdefdummy_task():logger.info("Executed")returnawaitasyncio.sleep(1,random.randrange(1,1000))asyncdefbad_dummy_task():logger.info("Executed and gonna throw a throttle")raiseThrottleException(bad_dummy_task())loop=asyncio.get_event_loop()# roflcoptrtodo_list=[dummy_task()for_inrange(1,31)]todo_list.append(bad_dummy_task())todo_list=todo_list+[dummy_task()for_inrange(1,31)]throttler=Throttler(todo_list,dummy_consumer,time_window=10,per_time_window=20,concurrency=5,log_handler=logging.StreamHandler(),log_level=logging.DEBUG,loop=loop)loop.run_until_complete(throttler.run())loop.close()How’s It WorksA Throttler is instantiated with a list of awaitables, an async
function, and numerous keyword arg knobs you adjust to suit your
purposes.Inside are twoasyncio.Queueobjects, and oneasyncio.LifoQueue.exceptionsis aQueuefor non-throttle exceptions we catch.processedis aQueuefor processed output. This is what your
consumer will consume from.todois aLifoQueuethat holds your unprocessed task list.
It’s initially fed from areverseof the list you pass toThrottler, which is fast and an iterator. It’sLIFOjust so
we can pop throttled items back into it at the front.Several internal functions are composed to create an async producer and
consumer loop where items are processed as fast as possible given the
restrictions imposed atThrottlerinstantiation. It’ll backofftime_windowwhen throttled, only executeconcurrencyof your
tasks at a time, and will waittime_windowafter triggering the
processing ofper_time_windowitems.That oughta cover a few cases…Anyways, the asyncconsumer_fnyou pass in will be executed as
results become available, immediately, for writing to disk or somethin’.NotesThis was painful.