Skip to main content

Asyncio Throttler

asyncio and async/await are 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_throttler is 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.

Usage

Well, 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 out pyenv (not to be confused with pyvenv), a Python version manager similar to rbenv.

If you really don’t want pyenv, brew install python3 will 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?

"""
import logging
import asyncio
from asyncio_throttler import Throttler, 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)

    async def dummy_consumer(item):
        print("Item received:", item)
        await asyncio.sleep(2)

    import random
    async def dummy_task():
        logger.info("Executed")
        return await asyncio.sleep(1, random.randrange(1, 1000))

    async def bad_dummy_task():
        logger.info("Executed and gonna throw a throttle")
        raise ThrottleException(bad_dummy_task())

    loop = asyncio.get_event_loop()

    # roflcoptr
    todo_list = [dummy_task() for _ in range(1, 31)]
    todo_list.append(bad_dummy_task())
    todo_list = todo_list + [dummy_task() for _ in range(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 Works

A Throttler is instantiated with a list of awaitables, an async function, and numerous keyword arg knobs you adjust to suit your purposes.

Inside are two asyncio.Queue objects, and one asyncio.LifoQueue.

  • exceptions is a Queue for non-throttle exceptions we catch.

  • processed is a Queue for processed output. This is what your consumer will consume from.

  • todo is a LifoQueue that holds your unprocessed task list. It’s initially fed from a reverse of the list you pass to Throttler, which is fast and an iterator. It’s LIFO just 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 at Throttler instantiation. It’ll backoff time_window when throttled, only execute concurrency of your tasks at a time, and will wait time_window after triggering the processing of per_time_window items.

That oughta cover a few cases…

Anyways, the async consumer_fn you pass in will be executed as results become available, immediately, for writing to disk or somethin’.

Notes

This was painful.

Metadata

Release files for asyncio-throttler 0.1.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for asyncio-throttler 0.1.0
File Size Uploaded Provenance Actions
asyncio_throttler-0.1.0.tar.gz 5.9 kB Without provenance Details for asyncio_throttler-0.1.0.tar.gz

Built distribution (wheel)

Table of built distributions (wheels) for asyncio-throttler 0.1.0
File Interpreter ABI Platform Actions
asyncio_throttler-0.1.0-py3-none-any.whl Python 3 none any Details for asyncio_throttler-0.1.0-py3-none-any.whl

Total release size: 14.8 kB

Release files / asyncio_throttler-0.1.0.tar.gz

Download URL asyncio_throttler-0.1.0.tar.gz
Size 5.9 kB
Tags Source
SHA-256 checksum
How to use checksums
64cb52715fdd91eab32ad5db7cb32dae62b1a20ad4af8d4abdf402cba42a4325
BLAKE2b-256 checksum
How to use checksums
4fa1be96073700512b4eda0cd9faf2047a36d9f0e859c97bc55209ac60db5d57
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No

Release files / asyncio_throttler-0.1.0-py3-none-any.whl

Download URL asyncio_throttler-0.1.0-py3-none-any.whl
Size 8.9 kB
Tags Python 3
SHA-256 checksum
How to use checksums
76431861e2fcb17a14007009cfaa4f26db3fbe6b53874b5802dfc16f3a943ca7
BLAKE2b-256 checksum
How to use checksums
aaf34a82944b6bb7c83af4b700a2c33cbf5dbf0c09b96f56d3e939f3e69ffb84
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No

Security for asyncio-throttler 0.1.0

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

No provenance found

None of the files in this release were uploaded with provenance metadata.

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

Release history Release notifications | RSS feed

This release

0.1.0 This release

2 release files No provenance

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page