Skip to main content

True parallelism for Python - Bypass the GIL with Rust-powered decorators for CPU-bound tasks

Project description

makeParallel 🚀

The easiest way to speed up your Python code using all your CPU cores.

PyPI version Tests Python Version License

Got a slow, CPU-heavy task in Python? makeParallel lets you run it on a separate core with a single line of code, so you can get results up to 4x, 8x, or even 16x faster without blocking your main program.

It's powered by Rust to safely bypass Python's Global Interpreter Lock (GIL), giving you true parallelism without the complexity of multiprocessing.


📋 Table of Contents


🤔 What's the "GIL"?

Python has a rule called the Global Interpreter Lock (GIL) that only lets one thread run at a time, even on a multi-core CPU. For tasks that just wait for networks (I/O-bound), this is fine. But for heavy calculations (CPU-bound), it means Python can't use all the power your computer has. makeParallel fixes this.


✨ Why You'll Love makeParallel

  • So Simple: Just add the @parallel decorator to any function. That's it!
  • True Speed-Up: Uses Rust threads to run your code on all available CPU cores.
  • Doesn't Block: Your main application stays responsive while the work happens in the background.
  • Smart Callbacks: Monitor progress, handle completion, catch errors - all with simple callbacks.
  • Task Dependencies: Build complex pipelines where tasks automatically wait for their dependencies.
  • Auto Progress Tracking: Report progress from within tasks without managing task IDs.
  • No multiprocessing Headaches: Avoids the complexity, memory overhead, and data-sharing issues of multiprocessing.
  • Production Ready: Built-in error handling, timeouts, cancellation, and graceful shutdown.
  • Works with Your Code: Decorate any function, even class methods.

📦 Installation

Installing is as simple as:

pip install makeparallel

Or, to build it from the source:

# Clone the repository
git clone https://github.com/amiyamandal-dev/makeParallel.git
cd makeParallel

# Build and install locally
pip install .

🚀 Quick Start

Let's say you have a function that does a lot of math and slows down your program.

Before: Your code waits...

import time

def cpu_intensive_task(n):
    # A slow calculation
    return sum(i * i for i in range(n))

start = time.time()
result = cpu_intensive_task(20_000_000) # This blocks everything!
print(f"Got result: {result} in {time.time() - start:.2f}s")

After: Instant and non-blocking!

import time
from makeparallel import parallel

@parallel # Just add this decorator!
def cpu_intensive_task(n):
    # The same slow calculation
    return sum(i * i for i in range(n))

start = time.time()
# The function returns instantly with a "handle"
handle = cpu_intensive_task(20_000_000)

print("The task is running in the background, my app is still responsive!")
# You can do other work here...

# Now, get the result (this will wait until it's ready)
result = handle.get()
print(f"Got result: {result} in {time.time() - start:.2f}s")

In the example above, handle.get() blocks until the result is ready. You can also check if it's done without waiting:

if handle.is_ready():
    print("It's done!")
else:
    print("Still working...")

🤔 When Should I Use This?

makeParallel is for CPU-bound tasks. These are operations that require a lot of computation, like:

  • heavy data processing, or scientific computing.
  • Image or video processing.
  • Complex simulations.

For I/O-bound tasks (like waiting for a web request or reading a file), Python's built-in threading or asyncio are usually a better fit.

📚 Complete Feature Guide

makeParallel comes with many powerful decorators and utilities.

🔥 Parallel Execution Decorators

@parallel - Full-featured parallel execution with callbacks and advanced control

from makeparallel import parallel, report_progress

@parallel
def cpu_intensive_task(n):
    for i in range(0, n, n//10):
        # Report progress automatically (no task_id needed!)
        report_progress(i / n)
        # Do work...
    return sum(i * i for i in range(n))

# Returns immediately with an AsyncHandle
handle = cpu_intensive_task(20_000_000, timeout=5.0)

# Set up callbacks (execute automatically when task completes)
handle.on_progress(lambda p: print(f"Progress: {p*100:.0f}%"))
handle.on_complete(lambda result: print(f"Success! Result: {result}"))
handle.on_error(lambda error: print(f"Error occurred: {error}"))

# Check status
if handle.is_ready():
    result = handle.get()  # Callbacks fire here

# Try to get result without blocking
result = handle.try_get()  # Returns None if not ready

# Cancel a running task
handle.cancel()
if handle.is_cancelled():
    print("Task was cancelled")

# Get task info
print(f"Task ID: {handle.get_task_id()}")
print(f"Elapsed: {handle.elapsed_time()}s")
print(f"Progress: {handle.get_progress()}")

# Add metadata
handle.set_metadata("user_id", "user-123")
metadata = handle.get_all_metadata()

@parallel_fast - Optimized with lock-free channels (crossbeam)

from makeparallel import parallel_fast

@parallel_fast
def fast_task(x):
    return x ** 2

handle = fast_task(10)
result = handle.get()  # Faster channel communication

@parallel_pool - Uses Rayon thread pool (best for many small tasks)

from makeparallel import parallel_pool

@parallel_pool
def small_task(x):
    return x * 2

# Efficiently handles many concurrent tasks
handles = [small_task(i) for i in range(1000)]
results = [h.get() for h in handles]

@parallel_priority - Priority-based execution

from makeparallel import parallel_priority, start_priority_worker, stop_priority_worker

# Start the priority worker before using priority tasks
start_priority_worker()

@parallel_priority
def task(data):
    return process(data)

# High priority tasks execute first (higher number = higher priority)
low = task(data1, priority=1)
high = task(data2, priority=10)  # Executes first

# Get results
low_result = low.get()
high_result = high.get()

# Stop the worker when done
stop_priority_worker()

@parallel_with_deps - Task dependencies and pipelines

from makeparallel import parallel_with_deps

@parallel_with_deps
def step1():
    return "data from step 1"

@parallel_with_deps
def step2(deps):
    # deps is a tuple of all dependency results
    data = deps[0]  # Result from step1
    return f"processed {data}"

@parallel_with_deps
def step3(deps):
    result = deps[0]  # Result from step2
    return f"final: {result}"

# Build dependency chain
h1 = step1()
h2 = step2(depends_on=[h1])  # Automatically waits for h1
h3 = step3(depends_on=[h2])  # Automatically waits for h2

# Execute entire pipeline
final = h3.get()  # Returns: "final: processed data from step 1"

🎯 Callbacks and Event Handling

makeParallel provides a powerful callback system for monitoring task execution:

from makeparallel import parallel, report_progress

@parallel
def download_file(url):
    # Simulate download with progress
    for i in range(100):
        download_chunk(url, i)
        # Report progress (task_id is automatic!)
        report_progress(i / 100.0)
    return f"Downloaded {url}"

handle = download_file("https://example.com/large_file.zip")

# Set up callbacks
handle.on_progress(lambda p: print(f"Downloaded: {p*100:.1f}%"))
handle.on_complete(lambda result: notify_user(result))
handle.on_error(lambda error: log_error(error))

# Callbacks fire automatically when you get the result
result = handle.get()

Callback Types:

  • on_progress(callback) - Called when report_progress() is called inside task
  • on_complete(callback) - Called when task succeeds (receives result)
  • on_error(callback) - Called when task fails (receives error string)

Key Features:

  • ✅ Automatic task_id tracking (no need to pass task_id!)
  • ✅ Thread-safe callback execution
  • ✅ Error isolation (callback failures don't crash tasks)
  • ✅ Progress validation (NaN/Infinity rejected)

🗺️ Batch Processing

parallel_map - Process lists in parallel

from makeparallel import parallel_map

def process_data(item):
    return item * 2

my_large_list = list(range(10000))
results = parallel_map(process_data, my_large_list)

gather - Collect results from multiple handles

from makeparallel import parallel, gather

@parallel
def task(x):
    return x ** 2

handles = [task(i) for i in range(10)]

# Wait for all and collect results
results = gather(handles, on_error="raise")  # or "skip" or "none"

ParallelContext - Context manager for parallel tasks

from makeparallel import ParallelContext, parallel

@parallel
def task(x):
    return x * 2

with ParallelContext(timeout=10.0) as ctx:
    handle1 = ctx.submit(task, (5,))
    handle2 = ctx.submit(task, (10,))
    # All tasks complete when exiting context

💾 Caching Decorators

@memoize - Cache function results

from makeparallel import memoize

@memoize
def fibonacci(n):
    if n <= 1:
        return n
    return fibonacci(n - 1) + fibonacci(n - 2)

fibonacci(35)  # Slow first time
fibonacci(35)  # Instant second time

@memoize_fast - Lock-free concurrent cache (DashMap)

from makeparallel import memoize_fast

@memoize_fast
def expensive_computation(x, y):
    return x ** y

# Safe for concurrent access from multiple threads

🔁 Retry Logic

@retry - Simple retry with fixed delays

from makeparallel import retry

@retry(max_retries=3)
def flaky_api_call():
    # Will retry up to 3 times on failure
    return make_request()

@retry_backoff - Retry with exponential backoff

from makeparallel import retry_backoff

@retry_backoff(
    max_attempts=5,
    backoff="exponential",  # or "linear"
    initial_delay=1.0,
    max_delay=60.0
)
def unreliable_task():
    return do_something()

📊 Performance Monitoring

@profiled - Automatic performance tracking

from makeparallel import profiled, get_metrics, get_all_metrics

@profiled
def tracked_function(x):
    return x ** 2

for i in range(100):
    tracked_function(i)

# Get metrics for specific function
metrics = get_metrics("tracked_function")
print(f"Total tasks: {metrics.total_tasks}")
print(f"Completed: {metrics.completed_tasks}")
print(f"Failed: {metrics.failed_tasks}")
print(f"Avg time: {metrics.average_execution_time_ms}ms")

# Get all metrics
all_metrics = get_all_metrics()

@timer - Simple execution timing

from makeparallel import timer

@timer
def my_function():
    # Do work...
    pass

my_function()  # Prints execution time

@CallCounter - Count function invocations

from makeparallel import CallCounter

@CallCounter
def counted_function():
    return "result"

counted_function()
counted_function()
print(counted_function.call_count)  # 2
counted_function.reset()

⚙️ Advanced Configuration

Thread Pool Configuration

from makeparallel import configure_thread_pool, get_thread_pool_info

# Configure global thread pool
configure_thread_pool(num_threads=8, stack_size=2*1024*1024)

# Get current info
info = get_thread_pool_info()
print(info["current_num_threads"])

Backpressure and Resource Management

from makeparallel import set_max_concurrent_tasks, configure_memory_limit

# Limit concurrent tasks to prevent overload
set_max_concurrent_tasks(100)

# Set memory limit (percentage)
configure_memory_limit(max_memory_percent=80.0)

Progress Reporting and Callbacks

from makeparallel import parallel, report_progress

@parallel
def long_task():
    for i in range(100):
        # Report progress from within task (task_id is automatic!)
        report_progress(i / 100.0)
        # Do work...
    return "done"

handle = long_task()

# Set up callbacks
handle.on_progress(lambda p: print(f"Progress: {p*100:.1f}%"))
handle.on_complete(lambda result: print(f"Finished: {result}"))
handle.on_error(lambda error: print(f"Error: {error}"))

# Get result (callbacks fire automatically)
result = handle.get()

Task Dependencies

from makeparallel import parallel_with_deps

@parallel_with_deps
def fetch_data():
    return {"users": 100, "orders": 500}

@parallel_with_deps
def process_data(deps):
    # deps[0] contains result from fetch_data
    data = deps[0]
    return f"Processed {data['users']} users"

@parallel_with_deps
def save_results(deps):
    # deps[0] contains result from process_data
    processed = deps[0]
    return f"Saved: {processed}"

# Build a dependency pipeline
h1 = fetch_data()
h2 = process_data(depends_on=[h1])  # Waits for h1
h3 = save_results(depends_on=[h2])  # Waits for h2

# Execute the entire pipeline
final_result = h3.get()  # Returns: "Saved: Processed 100 users"

Graceful Shutdown

from makeparallel import shutdown, get_active_task_count, reset_shutdown

# Get active task count
print(f"Active tasks: {get_active_task_count()}")

# Graceful shutdown with timeout
success = shutdown(timeout_secs=30.0, cancel_pending=True)

# Reset after shutdown (for testing)
reset_shutdown()

🎯 Choosing the Right Decorator

Decorator Best For Performance Features
@parallel Most use cases Good Full control: timeout, cancel, metadata, progress
@parallel_fast High-throughput tasks Better Lock-free channels (crossbeam)
@parallel_pool Many small tasks Best Rayon thread pool, efficient resource usage
@parallel_priority Priority-based scheduling Good Priority queue execution
parallel_map Batch processing lists Best Automatic parallelization across items

Quick Decision Guide:

  • Single long task with monitoring?@parallel
  • Thousands of small tasks?@parallel_pool
  • Processing a large list?parallel_map
  • Need priority scheduling?@parallel_priority
  • Maximum throughput?@parallel_fast

🏗️ How It Works

Here's a simple breakdown of what happens when you call a @parallel function:

  1. Python Side: Your main program calls the function but doesn't run it directly. Instead, it sends the function and its arguments to the Rust backend.
  2. Rust Backend:
    • It immediately returns the AsyncHandle object to your Python code so it doesn't have to wait.
    • It releases Python's Global Interpreter Lock (GIL).
    • It spawns a new Rust OS thread (a real parallel thread).
    • Inside the new thread, it re-acquires the GIL to safely execute your Python function.
  3. Result: The result is sent back to the AsyncHandle, which your main program can access with .get().

This GIL-release-and-reacquire step is the key to unlocking true parallelism for CPU-bound Python code.

🔧 Best Practices

✅ Do's

  • Use for CPU-bound tasks: Heavy computation, data processing, mathematical operations
  • Combine with caching: Use @memoize or @memoize_fast to avoid redundant calculations
  • Monitor with profiling: Use @profiled to track performance and identify bottlenecks
  • Set resource limits: Use set_max_concurrent_tasks() to prevent overload
  • Handle errors gracefully: Use gather() with appropriate on_error strategy
  • Use timeouts for long tasks: Add timeout parameter to prevent hanging

❌ Don'ts

  • Don't use for I/O-bound tasks: Use asyncio or threading instead
  • Don't pass large objects: Minimize data transfer between threads
  • Don't ignore error handling: Always check results or use try/except
  • Don't spawn unlimited tasks: Use set_max_concurrent_tasks() for backpressure
  • Don't forget cleanup: Use shutdown() for graceful termination

💡 Performance Tips

# ✅ Good: Process large batches efficiently
results = parallel_map(heavy_computation, large_list)

# ❌ Bad: Creating too many individual parallel tasks
handles = [parallel_task(x) for x in range(10000)]  # Overhead!

# ✅ Good: Use memoization for repeated calls
@memoize_fast
@parallel_pool
def cached_parallel_task(x):
    return expensive_operation(x)

# ✅ Good: Configure thread pool for your workload
configure_thread_pool(num_threads=8)  # Match your CPU cores

🆚 Comparison with Alternatives

Feature makeParallel multiprocessing threading asyncio
True Parallelism ✅ Yes ✅ Yes ❌ No (GIL) ❌ No (GIL)
CPU-bound Tasks ✅ Excellent ✅ Good ❌ Poor ❌ Poor
I/O-bound Tasks ⚠️ Okay ⚠️ Okay ✅ Good ✅ Excellent
Memory Overhead ✅ Low ❌ High ✅ Low ✅ Low
Easy to Use ✅ Very Easy ⚠️ Complex ✅ Easy ⚠️ Moderate
Data Sharing ✅ Simple ❌ Complex ✅ Simple ✅ Simple
Performance ✅✅ Fast (Rust) ✅ Good ⚠️ Limited ✅ Good
Cancellation ✅ Built-in ⚠️ Manual ⚠️ Manual ✅ Built-in
Progress Tracking ✅ Built-in ❌ Manual ❌ Manual ⚠️ Manual

📖 Real-World Examples

Example 1: Image Processing Pipeline

from makeparallel import parallel_map, profiled
from PIL import Image
import os

@profiled
def process_image(image_path):
    img = Image.open(image_path)
    # Resize, apply filters, etc.
    img_resized = img.resize((800, 600))
    # Save processed image
    output_path = f"processed_{os.path.basename(image_path)}"
    img_resized.save(output_path)
    return output_path

# Process 1000 images in parallel
image_files = [f"image_{i}.jpg" for i in range(1000)]
processed = parallel_map(process_image, image_files)

print(f"Processed {len(processed)} images")

Example 2: Web Scraping with Retry Logic

from makeparallel import parallel_pool, retry_backoff
import requests

@retry_backoff(max_attempts=3, backoff="exponential")
@parallel_pool
def fetch_url(url):
    response = requests.get(url, timeout=10)
    return response.text

urls = ["https://example.com/page1", "https://example.com/page2", ...]
handles = [fetch_url(url) for url in urls]
results = [h.get() for h in handles]

Example 3: Data Analysis with Progress Tracking and Callbacks

from makeparallel import parallel, report_progress
import pandas as pd

@parallel
def analyze_dataset(file_path):
    df = pd.read_csv(file_path)
    total_rows = len(df)

    results = []
    for i, row in df.iterrows():
        # Report progress (task_id is automatic!)
        report_progress(i / total_rows)

        # Perform analysis
        result = complex_analysis(row)
        results.append(result)

    return results

handle = analyze_dataset("large_dataset.csv")

# Set up callbacks for monitoring
handle.on_progress(lambda p: print(f"Analyzed: {p*100:.1f}%"))
handle.on_complete(lambda results: print(f"Analysis complete! {len(results)} rows"))
handle.on_error(lambda e: print(f"Analysis failed: {e}"))

# Get results (callbacks fire automatically)
final_results = handle.get()

Example 4: ETL Pipeline with Task Dependencies

from makeparallel import parallel_with_deps

@parallel_with_deps
def extract_data(source):
    # Fetch data from database/API
    print(f"Extracting from {source}...")
    return fetch_raw_data(source)

@parallel_with_deps
def transform_data(deps):
    # deps[0] contains result from extract_data
    raw_data = deps[0]
    print("Transforming data...")
    return clean_and_transform(raw_data)

@parallel_with_deps
def validate_data(deps):
    # deps[0] contains result from transform_data
    transformed = deps[0]
    print("Validating data...")
    return run_validation_checks(transformed)

@parallel_with_deps
def load_data(deps):
    # deps[0] contains result from validate_data
    validated = deps[0]
    print("Loading to warehouse...")
    return insert_into_warehouse(validated)

# Build ETL pipeline with dependencies
h1 = extract_data("production_db")
h2 = transform_data(depends_on=[h1])   # Waits for extract
h3 = validate_data(depends_on=[h2])    # Waits for transform
h4 = load_data(depends_on=[h3])        # Waits for validate

# Execute entire pipeline
result = h4.get()  # Blocks until all dependencies complete
print(f"Pipeline complete: {result}")

Example 5: Machine Learning Model Training

from makeparallel import parallel, gather, configure_thread_pool
from sklearn.model_selection import train_test_split
from sklearn.ensemble import RandomForestClassifier

# Configure thread pool for ML workload
configure_thread_pool(num_threads=4)

@parallel
def train_model(params):
    model = RandomForestClassifier(**params)
    model.fit(X_train, y_train)
    score = model.score(X_test, y_test)
    return {"params": params, "score": score}

# Train multiple models with different hyperparameters
param_grid = [
    {"n_estimators": 100, "max_depth": 10},
    {"n_estimators": 200, "max_depth": 15},
    {"n_estimators": 300, "max_depth": 20},
]

handles = [train_model(params) for params in param_grid]
results = gather(handles)

# Find best model
best = max(results, key=lambda x: x["score"])
print(f"Best params: {best['params']}, Score: {best['score']}")

🐛 Troubleshooting

My tasks are running slowly

  • Check if your task is CPU-bound (use @profiled to measure)
  • For I/O-bound tasks, use asyncio instead
  • Ensure you're not creating too many tasks (use parallel_map for batch processing)
  • Configure thread pool: configure_thread_pool(num_threads=<cpu_cores>)

Tasks are hanging

  • Add timeouts: @parallel def task(): ... then task(timeout=10.0)
  • Use cancel() to stop stuck tasks
  • Check for deadlocks in your Python code

Memory usage is too high

  • Limit concurrent tasks: set_max_concurrent_tasks(100)
  • Set memory limit: configure_memory_limit(max_memory_percent=80.0)
  • Use @parallel_pool instead of spawning individual threads
  • Process data in smaller batches

Errors are being swallowed

  • Always check handle.get() in a try/except block
  • Use gather() with on_error="raise" to see all errors
  • Enable profiling to see failed task counts: @profiled
  • Use on_error callbacks to capture errors: handle.on_error(lambda e: print(e))

Callbacks not firing

  • Make sure you call handle.get() or handle.wait() to trigger callbacks
  • Callbacks execute during result retrieval
  • Check callback syntax: handle.on_progress(lambda p: print(p))

Dependencies hanging

  • Check for circular dependencies (task A depends on B, B depends on A)
  • Verify all dependencies complete successfully
  • Use timeouts: task(depends_on=[h1], timeout=60.0)
  • Enable logging to see dependency errors: RUST_LOG=makeparallel=debug

🤝 Contributing

Contributions are welcome! If you want to help improve makeParallel, please feel free to open an issue or submit a pull request.

Development Setup

# Clone the repository
git clone https://github.com/amiyamandal-dev/makeParallel.git
cd makeParallel

# Create virtual environment
python -m venv .venv
source .venv/bin/activate  # On Windows: .venv\Scripts\activate

# Install development dependencies
pip install maturin

# Build and install in development mode
maturin develop

# Run tests
python tests/test_all.py

Running Tests

# Run all tests
python tests/test_all.py

# The test suite includes:
# - 37 core tests covering all decorators and features
# - 3 callback tests (on_progress, on_complete, on_error)
# - 5 progress tracking tests
# - Performance benchmarks
# - Edge case validation
# - Error handling verification

# Run specific test suites
python test_simple_callbacks.py      # Callback functionality
python test_progress_fix.py          # Progress tracking

Code Quality

# Format Rust code
cargo fmt

# Lint Rust code
cargo clippy

# Format Python code (if you have ruff)
ruff format .

# Check Python code
ruff check .

📝 License

This project is licensed under the MIT License - see the LICENSE file for details.

🙏 Acknowledgments

  • Built with PyO3 for Python-Rust interop
  • Uses Rayon for efficient thread pool management
  • Uses Crossbeam for lock-free channels
  • Uses DashMap for concurrent caching

📬 Contact & Support


Made with ❤️ and Rust 🦀

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

makeparallel-0.2.0.tar.gz (119.0 kB view details)

Uploaded Source

Built Distributions

If you're not sure about the file name format, learn more about wheel file names.

makeparallel-0.2.0-pp311-pypy311_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (1.3 MB view details)

Uploaded PyPymanylinux: glibc 2.17+ x86-64

makeparallel-0.2.0-pp311-pypy311_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl (1.2 MB view details)

Uploaded PyPymanylinux: glibc 2.17+ ARM64

makeparallel-0.2.0-cp314-cp314t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl (1.2 MB view details)

Uploaded CPython 3.14tmanylinux: glibc 2.17+ ARM64

makeparallel-0.2.0-cp314-cp314-win_amd64.whl (935.9 kB view details)

Uploaded CPython 3.14Windows x86-64

makeparallel-0.2.0-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (1.3 MB view details)

Uploaded CPython 3.14manylinux: glibc 2.17+ x86-64

makeparallel-0.2.0-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl (1.2 MB view details)

Uploaded CPython 3.14manylinux: glibc 2.17+ ARM64

makeparallel-0.2.0-cp314-cp314-macosx_11_0_arm64.whl (1.0 MB view details)

Uploaded CPython 3.14macOS 11.0+ ARM64

makeparallel-0.2.0-cp314-cp314-macosx_10_12_x86_64.whl (1.1 MB view details)

Uploaded CPython 3.14macOS 10.12+ x86-64

makeparallel-0.2.0-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl (1.2 MB view details)

Uploaded CPython 3.13tmanylinux: glibc 2.17+ ARM64

makeparallel-0.2.0-cp313-cp313-win_amd64.whl (937.0 kB view details)

Uploaded CPython 3.13Windows x86-64

makeparallel-0.2.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (1.3 MB view details)

Uploaded CPython 3.13manylinux: glibc 2.17+ x86-64

makeparallel-0.2.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl (1.2 MB view details)

Uploaded CPython 3.13manylinux: glibc 2.17+ ARM64

makeparallel-0.2.0-cp313-cp313-macosx_11_0_arm64.whl (1.0 MB view details)

Uploaded CPython 3.13macOS 11.0+ ARM64

makeparallel-0.2.0-cp313-cp313-macosx_10_12_x86_64.whl (1.1 MB view details)

Uploaded CPython 3.13macOS 10.12+ x86-64

makeparallel-0.2.0-cp312-cp312-win_amd64.whl (937.2 kB view details)

Uploaded CPython 3.12Windows x86-64

makeparallel-0.2.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (1.3 MB view details)

Uploaded CPython 3.12manylinux: glibc 2.17+ x86-64

makeparallel-0.2.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl (1.2 MB view details)

Uploaded CPython 3.12manylinux: glibc 2.17+ ARM64

makeparallel-0.2.0-cp312-cp312-macosx_11_0_arm64.whl (1.0 MB view details)

Uploaded CPython 3.12macOS 11.0+ ARM64

makeparallel-0.2.0-cp312-cp312-macosx_10_12_x86_64.whl (1.1 MB view details)

Uploaded CPython 3.12macOS 10.12+ x86-64

makeparallel-0.2.0-cp311-cp311-win_amd64.whl (937.2 kB view details)

Uploaded CPython 3.11Windows x86-64

makeparallel-0.2.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (1.3 MB view details)

Uploaded CPython 3.11manylinux: glibc 2.17+ x86-64

makeparallel-0.2.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl (1.2 MB view details)

Uploaded CPython 3.11manylinux: glibc 2.17+ ARM64

makeparallel-0.2.0-cp311-cp311-macosx_11_0_arm64.whl (1.1 MB view details)

Uploaded CPython 3.11macOS 11.0+ ARM64

makeparallel-0.2.0-cp311-cp311-macosx_10_12_x86_64.whl (1.1 MB view details)

Uploaded CPython 3.11macOS 10.12+ x86-64

makeparallel-0.2.0-cp310-cp310-win_amd64.whl (937.6 kB view details)

Uploaded CPython 3.10Windows x86-64

makeparallel-0.2.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (1.3 MB view details)

Uploaded CPython 3.10manylinux: glibc 2.17+ x86-64

makeparallel-0.2.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl (1.2 MB view details)

Uploaded CPython 3.10manylinux: glibc 2.17+ ARM64

makeparallel-0.2.0-cp39-cp39-win_amd64.whl (939.2 kB view details)

Uploaded CPython 3.9Windows x86-64

makeparallel-0.2.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (1.3 MB view details)

Uploaded CPython 3.9manylinux: glibc 2.17+ x86-64

makeparallel-0.2.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl (1.2 MB view details)

Uploaded CPython 3.9manylinux: glibc 2.17+ ARM64

makeparallel-0.2.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (1.3 MB view details)

Uploaded CPython 3.8manylinux: glibc 2.17+ x86-64

makeparallel-0.2.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl (1.2 MB view details)

Uploaded CPython 3.8manylinux: glibc 2.17+ ARM64

File details

Details for the file makeparallel-0.2.0.tar.gz.

File metadata

  • Download URL: makeparallel-0.2.0.tar.gz
  • Upload date:
  • Size: 119.0 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: maturin/1.10.2

File hashes

Hashes for makeparallel-0.2.0.tar.gz
Algorithm Hash digest
SHA256 6e32654152bceeb433ea70efbdc724fcf6fa901c658c82179867328fd0b67812
MD5 c5559562e05982e7fecdd8ee20f16e17
BLAKE2b-256 05f991ee5c990e5eab45e7ab2e9d57dcae270732c063da6d29c52d4c129b4bbb

See more details on using hashes here.

File details

Details for the file makeparallel-0.2.0-pp311-pypy311_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.

File metadata

File hashes

Hashes for makeparallel-0.2.0-pp311-pypy311_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
Algorithm Hash digest
SHA256 cbe2d8ec0bbe4de338d17b39ecc8a675e9d169699b5fab357dbd1a4b435e1de7
MD5 86f62991bc4b51d55c6b32520ba0344c
BLAKE2b-256 dfdadcaf49ceb382b13b46c317baa28e3d52a65b1dd412e93f4e9b926712eb1c

See more details on using hashes here.

File details

Details for the file makeparallel-0.2.0-pp311-pypy311_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl.

File metadata

File hashes

Hashes for makeparallel-0.2.0-pp311-pypy311_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl
Algorithm Hash digest
SHA256 0d08a54a0030e8824e7d86b432c56259bdbbf99763419d0f4abc6b337df832ba
MD5 b33855c197f8c7344430dbaa2a4c6f2a
BLAKE2b-256 902798d1acea8bf7b9913fee095ef953cf102f795e51539e9271b89fe3d44ff8

See more details on using hashes here.

File details

Details for the file makeparallel-0.2.0-cp314-cp314t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl.

File metadata

File hashes

Hashes for makeparallel-0.2.0-cp314-cp314t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl
Algorithm Hash digest
SHA256 79e768d068ca1b7aca209c9c88264dc9b4365ac65d279d142c434c563828e8e1
MD5 2e6fe6da7d641168eee903d8845c1b56
BLAKE2b-256 2ab38f9ff241097fea96b7f8cf2ba359905bd5dbbe4a885390330674a646a5c2

See more details on using hashes here.

File details

Details for the file makeparallel-0.2.0-cp314-cp314-win_amd64.whl.

File metadata

File hashes

Hashes for makeparallel-0.2.0-cp314-cp314-win_amd64.whl
Algorithm Hash digest
SHA256 7d514816ad89886e31548a788703675e41bcfee03bea83a9e18bd2fa5e5c500f
MD5 46a023a528f755519b1985444561dbd3
BLAKE2b-256 f26b97a6017f32f399aaef42371233dbcdc40d6d038bcd602a4f6edae777cbfa

See more details on using hashes here.

File details

Details for the file makeparallel-0.2.0-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.

File metadata

File hashes

Hashes for makeparallel-0.2.0-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
Algorithm Hash digest
SHA256 0ad937b1e094a803d51b12d045db5c84e016d86c309844cbb45b3f6014b6e492
MD5 914c3ee9f587e9ba0c0183ff2bad690f
BLAKE2b-256 84de33a1bf2d7d20e319954bacf4413635a9e4e40dc828e11c9d8a740c101fbb

See more details on using hashes here.

File details

Details for the file makeparallel-0.2.0-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl.

File metadata

File hashes

Hashes for makeparallel-0.2.0-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl
Algorithm Hash digest
SHA256 40eb7e78e0af807ec353de3133732f6473692cb7b207c058c439ad154c4991c3
MD5 554f1b28701a05dbea047eca3eefb831
BLAKE2b-256 ed37744c30bf5b2a92f259832534d528a884381256d98fdaef367fa0b5c1c9a6

See more details on using hashes here.

File details

Details for the file makeparallel-0.2.0-cp314-cp314-macosx_11_0_arm64.whl.

File metadata

File hashes

Hashes for makeparallel-0.2.0-cp314-cp314-macosx_11_0_arm64.whl
Algorithm Hash digest
SHA256 7e3f430cb43401802dffc52cea90e8485403f55a6da37cbd6c4fd5b02d10da95
MD5 5c39430908748753d252cbb1ccd82585
BLAKE2b-256 3f07f5076bf81e9a366c6e12fa22395692e9a00d74a861db7efca9511cdc11c9

See more details on using hashes here.

File details

Details for the file makeparallel-0.2.0-cp314-cp314-macosx_10_12_x86_64.whl.

File metadata

File hashes

Hashes for makeparallel-0.2.0-cp314-cp314-macosx_10_12_x86_64.whl
Algorithm Hash digest
SHA256 2a975efd996db8f1079ed7ce59f3a588212bd1730c6b94584b99827a0486fc27
MD5 149ac3e4c3d3f3f802e05f5fdfab6d17
BLAKE2b-256 44495fa51a225aa7afcdc2c1f3d8bc6e7b7edd79afecd4d5da3873130aa78323

See more details on using hashes here.

File details

Details for the file makeparallel-0.2.0-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl.

File metadata

File hashes

Hashes for makeparallel-0.2.0-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl
Algorithm Hash digest
SHA256 2c5e297ebf863bf1939fbd0b87f35ae6dbcca50c980fbd048fa7afbebd54489b
MD5 21cf55487273bcca53a85f05bd61daaa
BLAKE2b-256 8614dc396c2bbe99e41023360fced6fa3a784cec435ff0bd22a2a4f8242d8266

See more details on using hashes here.

File details

Details for the file makeparallel-0.2.0-cp313-cp313-win_amd64.whl.

File metadata

File hashes

Hashes for makeparallel-0.2.0-cp313-cp313-win_amd64.whl
Algorithm Hash digest
SHA256 bbaa0bbc830621f426778fb4d80208c6ef3c4fd2a79fc41fa383550a34e6dc0a
MD5 3d31343ca606c47f489d8c4f6adcac49
BLAKE2b-256 c7f8849912cc7dec61624283529189c3d4ed8c050f0035188edf590f32444178

See more details on using hashes here.

File details

Details for the file makeparallel-0.2.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.

File metadata

File hashes

Hashes for makeparallel-0.2.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
Algorithm Hash digest
SHA256 4afc20caba6edcc190310822fc682e67daed1e5a982711ef9771d781f9b51cf6
MD5 ff7d3a8124562254ac8e89aed888efe9
BLAKE2b-256 73ccf353553a3d3c054fd8c436572064fd84d632aef51899ffc5729f9a2e83af

See more details on using hashes here.

File details

Details for the file makeparallel-0.2.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl.

File metadata

File hashes

Hashes for makeparallel-0.2.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl
Algorithm Hash digest
SHA256 9018f7ed7cc34da5600a4b91d36b6fcd53bed1ab61b882a265c7ec7d37b4dcb6
MD5 aef13b934e06efa2c99f01b9511350be
BLAKE2b-256 31ca5b2aa5fb7da9263085484889120770460af354c1a71dfc6d05ef4199e6f2

See more details on using hashes here.

File details

Details for the file makeparallel-0.2.0-cp313-cp313-macosx_11_0_arm64.whl.

File metadata

File hashes

Hashes for makeparallel-0.2.0-cp313-cp313-macosx_11_0_arm64.whl
Algorithm Hash digest
SHA256 1228f5701531c96b1a5a5689ec15712f31a642479242ac94912c1ee931038f93
MD5 1ecc23e40990987732799651c4fee4a1
BLAKE2b-256 ebe58813b5d3a989f4da07fba84582b0173f0f7cdde0d125d8d566f784ea8954

See more details on using hashes here.

File details

Details for the file makeparallel-0.2.0-cp313-cp313-macosx_10_12_x86_64.whl.

File metadata

File hashes

Hashes for makeparallel-0.2.0-cp313-cp313-macosx_10_12_x86_64.whl
Algorithm Hash digest
SHA256 7930bcc8dd9f4442be85d194539ba4d30ce64a2e2861e6a5558e8eaab4af2a68
MD5 2942acb3bc5b7ab52f1e45d3764804be
BLAKE2b-256 08b9e6bd8168ecd52df54951a1f5615bc8c197b2df042305926adcc051daa22c

See more details on using hashes here.

File details

Details for the file makeparallel-0.2.0-cp312-cp312-win_amd64.whl.

File metadata

File hashes

Hashes for makeparallel-0.2.0-cp312-cp312-win_amd64.whl
Algorithm Hash digest
SHA256 13b08ee9613293acf835c2792c5f75c2b952f531af3de9ca37ece02d3a93b601
MD5 142b6fc2973121b8b3245361ae1179e3
BLAKE2b-256 6a7d875201620e175a3874b520c240104adb25f6c987cbb4e49514864665021e

See more details on using hashes here.

File details

Details for the file makeparallel-0.2.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.

File metadata

File hashes

Hashes for makeparallel-0.2.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
Algorithm Hash digest
SHA256 98823770375bd7fa8b788ab9d958a251ce9537444552d320d2e7d4b04656b0ad
MD5 de2ee79da72603f77a14d1a3e1357cdb
BLAKE2b-256 0f35014d6112fb89e669db10c236b38b5709d11d447dcb0cf46dd4fa818b3128

See more details on using hashes here.

File details

Details for the file makeparallel-0.2.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl.

File metadata

File hashes

Hashes for makeparallel-0.2.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl
Algorithm Hash digest
SHA256 0daa24b0c8195bff42609f96d06c0fef8bf43a47a39de8fd324ca94ce1f78a04
MD5 5171eb03c591fb674f0fa8b5daea920e
BLAKE2b-256 d44f251c574d1d53726a3ae9e1eb63e6f2e15b73f27e0b9cb313c65f70960e30

See more details on using hashes here.

File details

Details for the file makeparallel-0.2.0-cp312-cp312-macosx_11_0_arm64.whl.

File metadata

File hashes

Hashes for makeparallel-0.2.0-cp312-cp312-macosx_11_0_arm64.whl
Algorithm Hash digest
SHA256 02791d646e26b783448f99619f7e4c0cc82511ce70887580876a78c815fd8c59
MD5 30bd12c7a755c904e3f0e83c9eeacf8e
BLAKE2b-256 350bd1aafbbefed342afb405ecd8682e0673c056ebe53ba6cac0e01938866401

See more details on using hashes here.

File details

Details for the file makeparallel-0.2.0-cp312-cp312-macosx_10_12_x86_64.whl.

File metadata

File hashes

Hashes for makeparallel-0.2.0-cp312-cp312-macosx_10_12_x86_64.whl
Algorithm Hash digest
SHA256 585818612a3ac6640f4afb7b27e9b1ebc435bf423844bf2c2da51036c7d5ce6e
MD5 185ac9a0628a6a7a3d1f68b3e20a54c2
BLAKE2b-256 5200fc8f9fea32d87534e490cd0caad772bafe527f200890099b42b7b313d1bc

See more details on using hashes here.

File details

Details for the file makeparallel-0.2.0-cp311-cp311-win_amd64.whl.

File metadata

File hashes

Hashes for makeparallel-0.2.0-cp311-cp311-win_amd64.whl
Algorithm Hash digest
SHA256 0c3c4e7247939d911d1539d275c56170fd2221188ee381505f2d29854b074812
MD5 ad7ac70dfb193e81d06cf215c93b2969
BLAKE2b-256 c12728b49de16a9d0334229231eaafe62ea778cfa5fe1e34a7a48de0c724555b

See more details on using hashes here.

File details

Details for the file makeparallel-0.2.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.

File metadata

File hashes

Hashes for makeparallel-0.2.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
Algorithm Hash digest
SHA256 50c2ea3ee9fe70ec4cd387dd954cb44f283093dc1a99d8844dcaf591c08c8584
MD5 edd07bc35e96a66fa0697dc59d82b10b
BLAKE2b-256 1df85924469c14bd452b28836d62e6a378fecae3ed2c904c71f7d3c3b2c0c806

See more details on using hashes here.

File details

Details for the file makeparallel-0.2.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl.

File metadata

File hashes

Hashes for makeparallel-0.2.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl
Algorithm Hash digest
SHA256 11c9f3651c9900c1bf5145d5165976d6031d9da349d1a7de052045e6b0a3af13
MD5 02460d670596e8bd41ddbaf9d85517b5
BLAKE2b-256 9e64f21ba942bc8c4bb685f33f47a17091b9b4c5bf0233358417f598edbb8b21

See more details on using hashes here.

File details

Details for the file makeparallel-0.2.0-cp311-cp311-macosx_11_0_arm64.whl.

File metadata

File hashes

Hashes for makeparallel-0.2.0-cp311-cp311-macosx_11_0_arm64.whl
Algorithm Hash digest
SHA256 0f5bd3b90766bc40727abda8f7b3595b4015fabdc10cfc9e7d48137699996b7b
MD5 3569bf67e1d1d9efe38d3bd29d2944bb
BLAKE2b-256 82cfefb7a8f715468538fca5005fa4180845b6325e015180a884827f82d7e3c3

See more details on using hashes here.

File details

Details for the file makeparallel-0.2.0-cp311-cp311-macosx_10_12_x86_64.whl.

File metadata

File hashes

Hashes for makeparallel-0.2.0-cp311-cp311-macosx_10_12_x86_64.whl
Algorithm Hash digest
SHA256 afb0513b6c24a60c7273b931dabbfb3abcf09162231a9ecbe414f5a9162f4d89
MD5 b0bd164b88ac9ea1766a23e15cfd3740
BLAKE2b-256 a6b107f569884a79c03612cf46249195ac8520dea3249b44798c65946f9c52ff

See more details on using hashes here.

File details

Details for the file makeparallel-0.2.0-cp310-cp310-win_amd64.whl.

File metadata

File hashes

Hashes for makeparallel-0.2.0-cp310-cp310-win_amd64.whl
Algorithm Hash digest
SHA256 8d23aae5f9a01ac499373c4a98e7b81a67948999e05f6c6f338737615dee7d99
MD5 c387f8116a39076f05ab98eaa1dcfd5d
BLAKE2b-256 8ba42e94e795e573836581437812ccf049be97fdc04d5af76d4f4ea047c29d14

See more details on using hashes here.

File details

Details for the file makeparallel-0.2.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.

File metadata

File hashes

Hashes for makeparallel-0.2.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
Algorithm Hash digest
SHA256 5113093bd8abf60678c56b957e24b99b7e2a4ec18eb550130ef96af0e60ccf9e
MD5 ff448bac26105f26928223c43f6bf272
BLAKE2b-256 e9bafaaae37c325a182cc5bb7545c5f6605e961cf6cd8508c994067441de0d8c

See more details on using hashes here.

File details

Details for the file makeparallel-0.2.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl.

File metadata

File hashes

Hashes for makeparallel-0.2.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl
Algorithm Hash digest
SHA256 3512bbf8a90643932969e5b58ab2693f34c40b236b0be06c894a07ae4e777bb0
MD5 4b43c5530bc4ce6913bb98b5661c5762
BLAKE2b-256 57017341d11155fe9f292f9e57379e4bba8360048cffae277f59935e62242495

See more details on using hashes here.

File details

Details for the file makeparallel-0.2.0-cp39-cp39-win_amd64.whl.

File metadata

File hashes

Hashes for makeparallel-0.2.0-cp39-cp39-win_amd64.whl
Algorithm Hash digest
SHA256 910958177969fe74fdec380165f5f31c5c3a41eb327e412fb6118e124ac98dcd
MD5 0f97797d2aadf26986757c05a1065529
BLAKE2b-256 99ee4bb236f8b0cad8cae872545396ae3cf344b6af70cb51de6846da46ebc27c

See more details on using hashes here.

File details

Details for the file makeparallel-0.2.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.

File metadata

File hashes

Hashes for makeparallel-0.2.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
Algorithm Hash digest
SHA256 dee1ef0d6af4d8504b8a2dd060d85df863e351d3a390a7480c1126c9f633103a
MD5 2fb91cf561ba76e933413a14cb79251a
BLAKE2b-256 3bacac363566220558f7521a847b659f569b226e1e1ea5c91698c68e19822b15

See more details on using hashes here.

File details

Details for the file makeparallel-0.2.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl.

File metadata

File hashes

Hashes for makeparallel-0.2.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl
Algorithm Hash digest
SHA256 84cce79af7b1cfbb4604553da1a133047bc35fdf6713f8de73df8aa31a33079c
MD5 f80bb671ec62ee68bfea64126b00e41f
BLAKE2b-256 5d5c2eb722bd155e746cccb5f18237b3e356c5c8b9abd0dd5354dbc2849a2ed9

See more details on using hashes here.

File details

Details for the file makeparallel-0.2.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.

File metadata

File hashes

Hashes for makeparallel-0.2.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
Algorithm Hash digest
SHA256 f3504119ee1702360a5265f0370d98c41872df4a296cfa17889957a4d15ae11b
MD5 550f44c3f4998db09b5d42287a680b8a
BLAKE2b-256 64f796a938eb6d284a5179ecd922f35e5a47eaf17dbeb55a409feef9c2335f0b

See more details on using hashes here.

File details

Details for the file makeparallel-0.2.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl.

File metadata

File hashes

Hashes for makeparallel-0.2.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl
Algorithm Hash digest
SHA256 1d459dc3b6b2309e92d9e3afa7475ef12c5f6359a28dfe9647d6026d00c44ef7
MD5 eb55c3eaee3fa1c34a31d1a3b840a47a
BLAKE2b-256 5a44420dfff968a703c90424650bc54cf74581124de0d91bce926c4dace77011

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page