A library for paced execution — run a fixed number of calls over a fixed time, for load and penetration testing
Project description
Paced Runner
A simple and flexible Python library for running paced execution flows. Supports various execution patterns like distributed execution, burst execution, and progressive flows.
Installation
pip install paced-runner
Basic Usage
1. Distributed Execution - distribute()
Execute a specified number of tasks distributed over a specified time period.
import paced_runner
import requests
import time
def api_test():
"""Target API call for execution"""
response = requests.get("https://httpbin.org/delay/1")
return response.status_code == 200
# Execute 50 times distributed over 1 hour
summary = paced_runner.distribute("1h", 50, api_test)
# Execute 100 times distributed over 30 minutes
summary = paced_runner.distribute("30m", 100, api_test)
# Execute 20 times distributed over 45 seconds
summary = paced_runner.distribute("45s", 20, api_test)
print(f"Success rate: {summary.success_rate:.1f}%")
print(f"Average response time: {summary.average_response_time:.3f}s")
2. Burst Execution - burst()
Execute a specified number of tasks all at once in burst mode.
import paced_runner
def quick_task():
time.sleep(0.1) # Simulate processing time
return "success"
# Execute 100 tasks at once (10 workers)
summary = paced_runner.burst(100, quick_task, max_workers=10)
print(f"Total execution time: {summary.total_duration:.2f}s")
print(f"Success rate: {summary.success_rate:.1f}%")
3. Class-based Detailed Control
import paced_runner
def custom_function(user_id, api_key):
"""Example function with arguments"""
# API call or other processing
time.sleep(0.1) # Simulate processing time
return {"user_id": user_id, "status": "success"}
# Detailed control with PacedRunner class
runner = paced_runner.PacedRunner(verbose=True)
# Distributed execution
summary = runner.distribute(
duration="30m",
count=100,
target_function=custom_function,
max_workers=20,
function_args=("user123",), # Positional arguments
function_kwargs={"api_key": "key456"} # Keyword arguments
)
# Burst execution
summary = runner.burst(
count=50,
target_function=custom_function,
max_workers=15,
function_args=("user456",),
function_kwargs={"api_key": "key789"}
)
# Detailed result analysis
for result in summary.results:
if not result.success:
print(f"Error: {result.error_message}")
4. Progressive Execution - progressive()
Execute multiple stages progressively.
import paced_runner
def simple_task():
time.sleep(0.05) # 50ms processing simulation
return "success"
# Stage definition: (execution_count, duration)
stages = [
(20, "30s"), # Warm-up: 20 executions/30 seconds
(50, "30s"), # Standard load: 50 executions/30 seconds
(100, "30s"), # Peak load: 100 executions/30 seconds
]
summaries = paced_runner.progressive(
stages=stages,
target_function=simple_task,
stage_interval=3.0 # 3 seconds wait between stages
)
# Check results for each stage
for i, summary in enumerate(summaries, 1):
print(f"Stage {i}: Success rate {summary.success_rate:.1f}%, RPS {summary.requests_per_second:.2f}")
5. Web API Testing Example
import paced_runner
import requests
import json
def test_api_endpoint(base_url, endpoint, method="GET", payload=None):
"""Generic API test function"""
url = f"{base_url}/{endpoint}"
try:
if method.upper() == "GET":
response = requests.get(url, timeout=10)
elif method.upper() == "POST":
response = requests.post(url, json=payload, timeout=10)
else:
raise ValueError(f"Unsupported method: {method}")
return {
"status_code": response.status_code,
"response_time": response.elapsed.total_seconds(),
"success": 200 <= response.status_code < 300
}
except Exception as e:
return {
"status_code": None,
"response_time": None,
"success": False,
"error": str(e)
}
# API load testing - 200 times in 1 hour
summary = paced_runner.distribute(
duration="1h",
count=200,
target_function=test_api_endpoint,
max_workers=25,
function_args=("https://jsonplaceholder.typicode.com", "posts/1"),
function_kwargs={"method": "GET"}
)
print(f"API test completed:")
print(f"- Total executions: {summary.total_requests}")
print(f"- Success: {summary.successful_requests} ({summary.success_rate:.1f}%)")
print(f"- Average response time: {summary.average_response_time:.3f}s")
print(f"- RPS: {summary.requests_per_second:.2f}")
6. Database Access Testing
import paced_runner
import sqlite3
import threading
# Thread local storage for DB connections
thread_local = threading.local()
def get_db_connection():
"""Get DB connection per thread"""
if not hasattr(thread_local, 'connection'):
thread_local.connection = sqlite3.connect('test.db')
return thread_local.connection
def database_operation(query, params=None):
"""Database operation test"""
try:
conn = get_db_connection()
cursor = conn.cursor()
if params:
cursor.execute(query, params)
else:
cursor.execute(query)
result = cursor.fetchall()
conn.commit()
return len(result)
except Exception as e:
return False
# Database access testing - 500 times in 5 minutes
summary = paced_runner.distribute(
duration="5m",
count=500,
target_function=database_operation,
max_workers=10,
function_args=("SELECT * FROM users WHERE id = ?",),
function_kwargs={"params": (1,)}
)
Time Format
Time can be specified in the following formats:
"1h"- 1 hour"30m"- 30 minutes"45s"- 45 seconds"2.5h"- 2 hours 30 minutes"90m"- 90 minutes (1 hour 30 minutes)
API Reference
Function Level API
# Distributed execution
paced_runner.distribute(duration, count, target_function, **options)
# Burst execution
paced_runner.burst(count, target_function, **options)
# Progressive execution
paced_runner.progressive(stages, target_function, **options)
Class-based API
runner = paced_runner.PacedRunner(verbose=True)
# Distributed execution
runner.distribute(duration, count, target_function, **options)
# Burst execution
runner.burst(count, target_function, **options)
ExecutionConfig
Defines execution configuration.
total_requests: Total number of executionsduration_hours: Execution time (in hours)max_workers: Maximum parallel workers
ExecutionResult
Stores individual execution results.
task_id: Task IDsuccess: Success/failure statusduration: Execution timeresponse_data: Response dataerror_message: Error messagetimestamp: Execution timestamp
ExecutionSummary
Summary of overall execution results.
total_requests: Total number of executionssuccessful_requests: Number of successful executionsfailed_requests: Number of failed executionstotal_duration: Total execution timeaverage_response_time: Average response timerequests_per_second: Executions per secondsuccess_rate: Success rate (%)results: List of individual results
Package Structure
paced-runner/
├── paced_runner/
│ └── __init__.py
├── setup.py
├── README.md
├── LICENSE
└── examples/
├── api_test.py
├── database_test.py
└── progressive_test.py
Development
Development Environment Setup
git clone https://github.com/sogatat/paced-runner.git
cd paced-runner
pip install -e ".[dev]"
Running Tests
pytest tests/
Code Formatting
black paced_runner/
flake8 paced_runner/
License
MIT License
Authors
Tasuya-SOGA
📧 sogatat@gmail.com
🐙 GitHub
Masaki Yamamoto
📧 masaki.yamamoto373@gmail.com
Contributing
Pull requests and issues are welcome.
Support
If you find this library helpful, please consider:
- ⭐ Starring the repository
- 🐛 Reporting bugs via GitHub Issues
- 💡 Suggesting new features
- 📖 Improving documentation
Languages
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file paced_runner-0.2.0.tar.gz.
File metadata
- Download URL: paced_runner-0.2.0.tar.gz
- Upload date:
- Size: 20.6 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.14.6
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
3fa76f5580251b18d3bb717b670b239a5136120527b90a540c56e0d50735929c
|
|
| MD5 |
95f4cd616b36318db03669ed96c92866
|
|
| BLAKE2b-256 |
b9933ecafdd51a09ad6917da1a2645b59cbf77bd40006bb016a6fa44080730d9
|
File details
Details for the file paced_runner-0.2.0-py3-none-any.whl.
File metadata
- Download URL: paced_runner-0.2.0-py3-none-any.whl
- Upload date:
- Size: 10.9 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.14.6
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
9809c4782986603f85141b57864297967121774b32c1ca5a1eaa4087a0d6aa21
|
|
| MD5 |
4a1940a528039df3b30c4296af78a032
|
|
| BLAKE2b-256 |
9526c73138ea0e6744e4c3512b55aab1a3436b6318b51e4e08c788430659eabf
|