Skip to main content

werkpool

werkpool is a Python library for managing asynchronous task execution with fine-grained control over concurrency and rate limiting. Built on asyncio, it provides a flexible worker pool that can handle various workload patterns efficiently.

Features

  • Concurrency Control: Limit the number of tasks executing simultaneously
  • Rate Limiting: Control task execution rate (tasks per second)
  • Hybrid Mode: Combine both concurrency and rate limits
  • Task Retries: Built-in retry logic with configurable backoff strategies
  • Timeout Support: Set per-task execution timeouts
  • Context Manager: Clean resource management with async with syntax

Installation

pip install werkpool

Quick Start

import asyncio
from werkpool import WorkerPool

async def fetch_data(url):
    # Your async task here
    return f"Data from {url}"

async def main():
    pool = WorkerPool(size=10, rate=100)
    
    # Schedule tasks
    future = pool.run(lambda: fetch_data("https://example.com"))
    result = await future
    
    await pool.shutdown()

asyncio.run(main())

Usage Examples

1. Rate-Limited (High Throughput)

Making requests with a rate limit of 100 requests per second.

import asyncio
from werkpool import worker_pool

async def call_api(item_id):
    # Simulated API call
    await asyncio.sleep(0.01)
    return f"Processed {item_id}"

async def main():
    async with worker_pool(rate=100) as pool:  # 100 tasks per second
        tasks = [pool.run(lambda i=i: call_api(i)) for i in range(1000)]
        results = await asyncio.gather(*tasks)
        print(f"Completed {len(results)} tasks")

asyncio.run(main())

2. Rate-Limited Pool (Low Throughput)

Calling a rate-restricted API allowing less than 1 request every second.

import asyncio
from werkpool import WorkerPool

async def check_status(resource_id):
    # Expensive or rate-limited operation
    await asyncio.sleep(0.1)
    return f"Status of {resource_id}"

async def main():
    pool = WorkerPool(rate=0.2)  # 0.2 per second = 1 request per 5 seconds
    
    futures = []
    for i in range(10):
        future = pool.run(lambda i=i: check_status(i))
        futures.append(future)
    
    results = await asyncio.gather(*futures)
    print(f"All statuses checked: {len(results)} items")
    
    await pool.shutdown()

asyncio.run(main())

3. Worker-Limited Pool

Preventing resource exhaustion by limiting concurrent workers.

import asyncio
from werkpool import worker_pool

async def process_file(filename):
    # Memory or I/O intensive operation
    await asyncio.sleep(0.5)
    return f"Processed {filename}"

async def main():
    # Maximum 5 concurrent tasks at any time
    async with worker_pool(size=5) as pool:
        files = [f"file_{i}.txt" for i in range(50)]
        
        tasks = [pool.run(lambda f=f: process_file(f)) for f in files]
        results = await asyncio.gather(*tasks)
        
        print(f"Processed {len(results)} files")

asyncio.run(main())

4. Combined Rate and Worker Limiting

In some situations like web scraping, you may need to respect both rate and concurrency limitationsss.

import asyncio
from werkpool import WorkerPool

async def scrape_page(url):
    # Simulated web scraping
    await asyncio.sleep(0.2)
    return f"Content from {url}"

async def main():
    # Max 5 concurrent requests, but only 10 requests per second
    pool = WorkerPool(size=5, rate=10)
    
    urls = [f"https://example.com/page/{i}" for i in range(100)]
    
    futures = [pool.run(lambda u=u: scrape_page(u)) for u in urls]
    results = await asyncio.gather(*futures)
    
    print(f"Scraped {len(results)} pages successfully")
    
    await pool.shutdown()

asyncio.run(main())

5. Advanced: Retries and Timeouts

import asyncio
from werkpool import WorkerPool

async def unreliable_api_call(endpoint):
    # Might fail occasionally
    import random
    if random.random() < 0.3:
        raise ConnectionError("Network error")
    await asyncio.sleep(0.1)
    return f"Data from {endpoint}"

async def main():
    pool = WorkerPool(size=10, rate=50)
    
    future = pool.run(
        lambda: unreliable_api_call("/data"),
        timeout=5,  # Max 5 seconds per attempt
        retries=3,  # Retry up to 3 times
        retryable_exceptions=[ConnectionError, TimeoutError]
    )
    
    try:
        result = await future
        print(f"Success: {result}")
    except Exception as e:
        print(f"Failed after retries: {e}")
    
    await pool.shutdown()

asyncio.run(main())

API Reference

WorkerPool

WorkerPool(size: int | None = None, rate: float | None = None)
  • size: Maximum number of concurrent workers (None = unlimited)
  • rate: Maximum executions per second (None = unlimited)

Methods

run() - Schedule a task for execution

pool.run(
    task: Callable[[], Awaitable[T]],
    timeout: int | None = None,
    retries: int = 0,
    retryable_exceptions: List[type[Exception]] = [Exception],
    backoff: Callable[[int], float] = lambda attempts: 2**attempts + random.uniform(0, 1)
) -> asyncio.Future[T]

shutdown() - Wait for all tasks to complete

await pool.shutdown()

kill() - Cancel all pending tasks immediately

await pool.kill()

worker_pool (Context Manager)

async with worker_pool(size=10, rate=100) as pool:
    # Use pool
    pass
# Automatically calls shutdown

Contributing

Found a bug? Have a feature request? Please open an issue on GitHub!

We welcome:

  • 🐛 Bug reports
  • 💡 Feature requests
  • 📖 Documentation improvements
  • 🔧 Pull requests

License

MIT License - see LICENSE file for details.

Links

Metadata

Release files for werkpool 0.2.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 werkpool 0.2.0
File Size Uploaded
werkpool-0.2.0.tar.gz 5.5 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for werkpool 0.2.0
File Interpreter ABI Platform
werkpool-0.2.0-py3-none-any.whl Python 3 none any Details

Total release size: 11.9 kB

Release files / werkpool-0.2.0.tar.gz

Download URL werkpool-0.2.0.tar.gz
Size 5.5 kB
Tags Source
SHA-256 checksum
How to use checksums
2f5372886a2795f8dd1fc5b0f70d28c74b7db3bf1c502a0f8dd32ef627334932
BLAKE2b-256 checksum
How to use checksums
8fb3f52c18b18aea65b385f146c42b95feb3ea390535eb40ffc68d8b2d497c89
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via poetry/2.2.1 CPython/3.12.11 Linux/6.11.11-linuxkit

Release files / werkpool-0.2.0-py3-none-any.whl

Download URL werkpool-0.2.0-py3-none-any.whl
Size 6.4 kB
Tags Python 3
SHA-256 checksum
How to use checksums
e640188db8b8a7d8705467182ad0b6631c9361de970b2793697a60c2a2ad720f
BLAKE2b-256 checksum
How to use checksums
5272fe13ab9495b6351c356374fed13c6bc5d3b32e9ee54f9905cc1d2fb67107
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via poetry/2.2.1 CPython/3.12.11 Linux/6.11.11-linuxkit

Release history Release notifications | RSS feed

This release

0.2.0 This release

2 release files

0.1.4

2 release files

0.1.3

2 release files

0.1.2

2 release files

0.1.1

2 release files

0.1.0

2 release files

0.0.1

2 release files

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