Name | ripplex JSON |
Version |
1.0.1
JSON |
| download |
home_page | None |
Summary | A Python framework for parallel execution with automatic dependency resolution and variable capture. |
upload_time | 2025-07-22 12:25:17 |
maintainer | None |
docs_url | None |
author | None |
requires_python | >=3.8 |
license | MIT License
Copyright (c) 2024 Seth Burkart
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE. |
keywords |
async
concurrency
flow
parallel
threading
|
VCS |
 |
bugtrack_url |
|
requirements |
No requirements were recorded.
|
Travis-CI |
No Travis.
|
coveralls test coverage |
No coveralls.
|
# Ripplex
A pythonic parallel computing library that just works
## Installation
```bash
pip install ripplex
```
## Two Simple Features
### 1. `@flow` - Automatic Parallelization
Add `@flow` to any function and Ripplex automatically runs independent operations in parallel.
```python
from ripplex import flow
@flow
def get_user_profile(user_id):
# These three calls run in PARALLEL automatically
user = fetch_user_data(user_id) # 1.0s
posts = fetch_user_posts(user_id) # 1.5s
friends = fetch_user_friends(user_id) # 0.8s
# This waits for all three to finish, then runs
return {
"user": user,
"posts": posts,
"friends": friends
}
# Sequential: 3.3 seconds
# With @flow: 1.5 seconds
result = get_user_profile(123)
```
**Zero refactoring required.** Just add the decorator.
### 2. `@loop` - Parallel Processing
Process lists in parallel with automatic variable capture.
```python
from ripplex import loop
# Variables from outer scope are automatically available
API_KEY = "secret-key"
BASE_URL = "https://api.example.com"
user_ids = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
@loop(user_ids)
def fetch_user(user_id):
# API_KEY and BASE_URL automatically available!
url = f"{BASE_URL}/users/{user_id}?key={API_KEY}"
return requests.get(url).json()
# The decorated function becomes a list of results!
print(f"Fetched {len(fetch_user)} users in parallel")
print(fetch_user[0]) # First user's data
```
**No setup needed.** Outer variables just work.
## Working with @loop Results
The `@loop` decorator returns a special `LoopResult` object that acts like a list but includes extra metadata:
```python
@loop([1, 2, 0, 4, 0, 6], on_error="collect")
def divide(n):
return 100 / n
# It's a list!
print(divide) # [100, 50, None, 25, None, 16.67]
print(divide[0]) # 100
print(len(divide)) # 6
# But with extras!
print(divide.success_count) # 4 successful
print(divide.total_count) # 6 total attempts
print(divide.all_successful) # False
print(divide.errors) # {2: ZeroDivisionError(...), 4: ZeroDivisionError(...)}
```
## Error Handling Options
The `@loop` decorator provides three strategies for handling errors:
```python
data = [1, 2, 0, 4, 0, 6] # Zeros will cause division errors
# Option 1: "continue" (default) - Skip failures, only return successes
@loop(data, on_error="continue")
def divide_continue(n):
return 100 / n
# Returns: [100, 50, 25, 16.67] - failed items removed
# Option 2: "raise" - Stop on first error
@loop(data, on_error="raise")
def divide_raise(n):
return 100 / n
# Raises: ZeroDivisionError (and cancels remaining)
# Option 3: "collect" - Keep None for failures, preserve positions
@loop(data, on_error="collect")
def divide_collect(n):
return 100 / n
# Returns: [100, 50, None, 25, None, 16.67] - preserves list structure
```
## More Ways to Parallelize
### Functional API - No Decorators Needed
```python
from ripplex import pmap, execute, quick_map
# One-liner parallel map
squared = pmap(lambda x: x**2, range(100))
# Execute with options
results = execute(process_item, items, workers=8, on_error="collect")
# Super quick for prototyping
doubled = quick_map(lambda x: x * 2, [1,2,3,4,5])
```
### Convenience Aliases
```python
import ripplex as rx
# Use short aliases for less typing
@rx.f # Instead of @flow
def complex_pipeline():
data = fetch_data()
processed = transform(data)
return processed
@rx.l(items) # Instead of @loop
def process(item):
return item * 2
# Ultra-short parallel map
results = rx.p(lambda x: x**2, range(10))
```
## Complete Example
Here's a real-world data pipeline using both features:
```python
from ripplex import flow, loop
@flow
def process_sales_data():
# Step 1: Fetch data in parallel
sales = fetch_sales_data() # 2s
customers = fetch_customers() # 1.5s
products = fetch_products() # 1s
# Step 2: Process each sale in parallel
@loop(sales, workers=8)
def enrich_sale(sale):
# customers and products automatically available!
customer = customers[sale['customer_id']]
product = products[sale['product_id']]
return {
**sale,
'customer_name': customer['name'],
'product_name': product['name'],
'profit': sale['price'] - product['cost']
}
# Step 3: Generate reports in parallel
summary = generate_summary(enrich_sale)
insights = analyze_trends(enrich_sale)
return {'sales': enrich_sale, 'summary': summary, 'insights': insights}
# Sequential: ~15 seconds
# With Ripplex: ~6 seconds
result = process_sales_data()
```
## Debugging
Add `debug=True` to see what's happening:
```python
@flow(debug=True)
def my_function():
# Shows execution timeline
pass
@loop(items, debug=True)
def process_item(item):
# Shows progress bar
pass
```
## API Reference
### Decorators
#### `@flow(debug=False)`
Automatically parallelizes independent operations in a function by analyzing dependencies.
#### `@loop(iterable, workers=None, on_error="continue", debug=False)`
Processes items in parallel with automatic variable capture.
**Parameters:**
- `iterable`: Items to process (or an integer for `range(n)`)
- `workers`: Number of threads (default: smart auto-detection)
- `on_error`: Error handling strategy:
- `"continue"`: Skip errors, return only successes
- `"raise"`: Stop on first error
- `"collect"`: Include `None` for failures, preserve list positions
- `debug`: Show progress bar and timing
**Returns:** `LoopResult` - an enhanced list with error tracking
### Functions
#### `pmap(function, iterable, **kwargs)`
Functional parallel map for one-liners:
```python
results = pmap(lambda x: x**2, [1,2,3,4]) # [1,4,9,16]
```
#### `execute(function, items, **kwargs)`
Execute a function in parallel without decorators:
```python
results = execute(process_item, items, workers=10, on_error="collect")
```
#### `quick_map(function, items)`
Simplest parallel map with defaults:
```python
results = quick_map(lambda x: x * 2, items)
```
#### `summary(loop_result)`
Get a human-readable summary of loop execution:
```python
from ripplex import summary
@loop(items, on_error="collect")
def process(item):
return risky_operation(item)
print(summary(process))
# 📊 Execution Summary:
# Total items: 100
# Successful: 95
# Failed: 5
# Success rate: 95.0%
```
## Performance Tips
- **I/O heavy**: Use more workers (`workers=20`)
- **CPU heavy**: Use fewer workers (`workers=4`)
- **Start with**: `debug=True` to see what's happening
- **Error handling**: Use `on_error="collect"` to see which items failed
## Gotchas and Tips
1. **@flow analyzes code**: Each parallelizable operation must be an assignment that creates a new variable
2. **Thread-based**: Best for I/O-bound tasks (API calls, file operations, database queries)
3. **Auto-captures variables**: Inner functions automatically see outer scope - no need to pass everything
4. **Smart defaults**: Worker count auto-scales based on workload
5. **Not for CPU-bound**: Use `multiprocessing` for heavy computation
**Ready to supercharge your Python code?** Start with `@flow` on your existing functions and watch them run in parallel!
Raw data
{
"_id": null,
"home_page": null,
"name": "ripplex",
"maintainer": null,
"docs_url": null,
"requires_python": ">=3.8",
"maintainer_email": null,
"keywords": "async, concurrency, flow, parallel, threading",
"author": null,
"author_email": "Seth Burkart <sethburkart7@gmail.com>",
"download_url": "https://files.pythonhosted.org/packages/92/42/6b1636c58d1f1f5584988223c73d248ae0a9809e86a5b44523b614f2fdcb/ripplex-1.0.1.tar.gz",
"platform": null,
"description": "# Ripplex\nA pythonic parallel computing library that just works\n\n## Installation\n\n```bash\npip install ripplex\n```\n\n## Two Simple Features\n\n### 1. `@flow` - Automatic Parallelization\n\nAdd `@flow` to any function and Ripplex automatically runs independent operations in parallel.\n\n```python\nfrom ripplex import flow\n\n@flow\ndef get_user_profile(user_id):\n # These three calls run in PARALLEL automatically\n user = fetch_user_data(user_id) # 1.0s\n posts = fetch_user_posts(user_id) # 1.5s \n friends = fetch_user_friends(user_id) # 0.8s\n \n # This waits for all three to finish, then runs\n return {\n \"user\": user,\n \"posts\": posts,\n \"friends\": friends\n }\n\n# Sequential: 3.3 seconds\n# With @flow: 1.5 seconds\nresult = get_user_profile(123)\n```\n\n**Zero refactoring required.** Just add the decorator.\n\n### 2. `@loop` - Parallel Processing\n\nProcess lists in parallel with automatic variable capture.\n\n```python\nfrom ripplex import loop\n\n# Variables from outer scope are automatically available\nAPI_KEY = \"secret-key\"\nBASE_URL = \"https://api.example.com\"\n\nuser_ids = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]\n\n@loop(user_ids)\ndef fetch_user(user_id):\n # API_KEY and BASE_URL automatically available!\n url = f\"{BASE_URL}/users/{user_id}?key={API_KEY}\"\n return requests.get(url).json()\n\n# The decorated function becomes a list of results!\nprint(f\"Fetched {len(fetch_user)} users in parallel\")\nprint(fetch_user[0]) # First user's data\n```\n\n**No setup needed.** Outer variables just work.\n\n## Working with @loop Results\n\nThe `@loop` decorator returns a special `LoopResult` object that acts like a list but includes extra metadata:\n\n```python\n@loop([1, 2, 0, 4, 0, 6], on_error=\"collect\")\ndef divide(n):\n return 100 / n\n\n# It's a list!\nprint(divide) # [100, 50, None, 25, None, 16.67]\nprint(divide[0]) # 100\nprint(len(divide)) # 6\n\n# But with extras!\nprint(divide.success_count) # 4 successful\nprint(divide.total_count) # 6 total attempts\nprint(divide.all_successful) # False\nprint(divide.errors) # {2: ZeroDivisionError(...), 4: ZeroDivisionError(...)}\n```\n\n## Error Handling Options\n\nThe `@loop` decorator provides three strategies for handling errors:\n\n```python\ndata = [1, 2, 0, 4, 0, 6] # Zeros will cause division errors\n\n# Option 1: \"continue\" (default) - Skip failures, only return successes\n@loop(data, on_error=\"continue\")\ndef divide_continue(n):\n return 100 / n\n# Returns: [100, 50, 25, 16.67] - failed items removed\n\n# Option 2: \"raise\" - Stop on first error\n@loop(data, on_error=\"raise\")\ndef divide_raise(n):\n return 100 / n\n# Raises: ZeroDivisionError (and cancels remaining)\n\n# Option 3: \"collect\" - Keep None for failures, preserve positions\n@loop(data, on_error=\"collect\")\ndef divide_collect(n):\n return 100 / n\n# Returns: [100, 50, None, 25, None, 16.67] - preserves list structure\n```\n\n## More Ways to Parallelize\n\n### Functional API - No Decorators Needed\n\n```python\nfrom ripplex import pmap, execute, quick_map\n\n# One-liner parallel map\nsquared = pmap(lambda x: x**2, range(100))\n\n# Execute with options\nresults = execute(process_item, items, workers=8, on_error=\"collect\")\n\n# Super quick for prototyping\ndoubled = quick_map(lambda x: x * 2, [1,2,3,4,5])\n```\n\n### Convenience Aliases\n\n```python\nimport ripplex as rx\n\n# Use short aliases for less typing\n@rx.f # Instead of @flow\ndef complex_pipeline():\n data = fetch_data()\n processed = transform(data)\n return processed\n\n@rx.l(items) # Instead of @loop \ndef process(item):\n return item * 2\n\n# Ultra-short parallel map\nresults = rx.p(lambda x: x**2, range(10))\n```\n\n## Complete Example\n\nHere's a real-world data pipeline using both features:\n\n```python\nfrom ripplex import flow, loop\n\n@flow \ndef process_sales_data():\n # Step 1: Fetch data in parallel\n sales = fetch_sales_data() # 2s\n customers = fetch_customers() # 1.5s\n products = fetch_products() # 1s\n \n # Step 2: Process each sale in parallel \n @loop(sales, workers=8)\n def enrich_sale(sale):\n # customers and products automatically available!\n customer = customers[sale['customer_id']]\n product = products[sale['product_id']]\n \n return {\n **sale,\n 'customer_name': customer['name'],\n 'product_name': product['name'],\n 'profit': sale['price'] - product['cost']\n }\n \n # Step 3: Generate reports in parallel\n summary = generate_summary(enrich_sale)\n insights = analyze_trends(enrich_sale)\n \n return {'sales': enrich_sale, 'summary': summary, 'insights': insights}\n\n# Sequential: ~15 seconds\n# With Ripplex: ~6 seconds \nresult = process_sales_data()\n```\n\n## Debugging\n\nAdd `debug=True` to see what's happening:\n\n```python\n@flow(debug=True)\ndef my_function():\n # Shows execution timeline\n pass\n\n@loop(items, debug=True) \ndef process_item(item):\n # Shows progress bar\n pass\n```\n\n## API Reference\n\n### Decorators\n\n#### `@flow(debug=False)`\nAutomatically parallelizes independent operations in a function by analyzing dependencies.\n\n#### `@loop(iterable, workers=None, on_error=\"continue\", debug=False)`\nProcesses items in parallel with automatic variable capture.\n\n**Parameters:**\n- `iterable`: Items to process (or an integer for `range(n)`)\n- `workers`: Number of threads (default: smart auto-detection)\n- `on_error`: Error handling strategy:\n - `\"continue\"`: Skip errors, return only successes\n - `\"raise\"`: Stop on first error\n - `\"collect\"`: Include `None` for failures, preserve list positions\n- `debug`: Show progress bar and timing\n\n**Returns:** `LoopResult` - an enhanced list with error tracking\n\n### Functions\n\n#### `pmap(function, iterable, **kwargs)`\nFunctional parallel map for one-liners:\n```python\nresults = pmap(lambda x: x**2, [1,2,3,4]) # [1,4,9,16]\n```\n\n#### `execute(function, items, **kwargs)`\nExecute a function in parallel without decorators:\n```python\nresults = execute(process_item, items, workers=10, on_error=\"collect\")\n```\n\n#### `quick_map(function, items)`\nSimplest parallel map with defaults:\n```python\nresults = quick_map(lambda x: x * 2, items)\n```\n\n#### `summary(loop_result)`\nGet a human-readable summary of loop execution:\n```python\nfrom ripplex import summary\n\n@loop(items, on_error=\"collect\")\ndef process(item):\n return risky_operation(item)\n\nprint(summary(process))\n# \ud83d\udcca Execution Summary:\n# Total items: 100\n# Successful: 95\n# Failed: 5\n# Success rate: 95.0%\n```\n\n## Performance Tips\n\n- **I/O heavy**: Use more workers (`workers=20`)\n- **CPU heavy**: Use fewer workers (`workers=4`) \n- **Start with**: `debug=True` to see what's happening\n- **Error handling**: Use `on_error=\"collect\"` to see which items failed\n\n## Gotchas and Tips\n\n1. **@flow analyzes code**: Each parallelizable operation must be an assignment that creates a new variable\n2. **Thread-based**: Best for I/O-bound tasks (API calls, file operations, database queries)\n3. **Auto-captures variables**: Inner functions automatically see outer scope - no need to pass everything\n4. **Smart defaults**: Worker count auto-scales based on workload\n5. **Not for CPU-bound**: Use `multiprocessing` for heavy computation\n\n**Ready to supercharge your Python code?** Start with `@flow` on your existing functions and watch them run in parallel!\n",
"bugtrack_url": null,
"license": "MIT License\n \n Copyright (c) 2024 Seth Burkart\n \n Permission is hereby granted, free of charge, to any person obtaining a copy\n of this software and associated documentation files (the \"Software\"), to deal\n in the Software without restriction, including without limitation the rights\n to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n copies of the Software, and to permit persons to whom the Software is\n furnished to do so, subject to the following conditions:\n \n The above copyright notice and this permission notice shall be included in all\n copies or substantial portions of the Software.\n \n THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n SOFTWARE. ",
"summary": "A Python framework for parallel execution with automatic dependency resolution and variable capture.",
"version": "1.0.1",
"project_urls": {
"Documentation": "https://github.com/sethburkart/ripplex#readme",
"Homepage": "https://github.com/sethburkart/ripplex",
"Issues": "https://github.com/sethburkart/ripplex/issues",
"Repository": "https://github.com/sethburkart/ripplex"
},
"split_keywords": [
"async",
" concurrency",
" flow",
" parallel",
" threading"
],
"urls": [
{
"comment_text": null,
"digests": {
"blake2b_256": "5dbdc45297da6d11692edfd700216209fa0347ffd7eb4c7309d58600fa7f6a1e",
"md5": "692024f1668b7fdb96ec36542652ed4e",
"sha256": "c06be1c82009fabb68afb32c333e69cb56aef8e9c19a5259239ed47ddb8da28b"
},
"downloads": -1,
"filename": "ripplex-1.0.1-py3-none-any.whl",
"has_sig": false,
"md5_digest": "692024f1668b7fdb96ec36542652ed4e",
"packagetype": "bdist_wheel",
"python_version": "py3",
"requires_python": ">=3.8",
"size": 12777,
"upload_time": "2025-07-22T12:25:16",
"upload_time_iso_8601": "2025-07-22T12:25:16.791486Z",
"url": "https://files.pythonhosted.org/packages/5d/bd/c45297da6d11692edfd700216209fa0347ffd7eb4c7309d58600fa7f6a1e/ripplex-1.0.1-py3-none-any.whl",
"yanked": false,
"yanked_reason": null
},
{
"comment_text": null,
"digests": {
"blake2b_256": "92426b1636c58d1f1f5584988223c73d248ae0a9809e86a5b44523b614f2fdcb",
"md5": "4a5f603ab7503b98ba86b2ba5ede6c63",
"sha256": "47caa642306f9904e7dbeec1e504d55afb199f036bf26bbd7f02fa76eee6c52a"
},
"downloads": -1,
"filename": "ripplex-1.0.1.tar.gz",
"has_sig": false,
"md5_digest": "4a5f603ab7503b98ba86b2ba5ede6c63",
"packagetype": "sdist",
"python_version": "source",
"requires_python": ">=3.8",
"size": 10610,
"upload_time": "2025-07-22T12:25:17",
"upload_time_iso_8601": "2025-07-22T12:25:17.829472Z",
"url": "https://files.pythonhosted.org/packages/92/42/6b1636c58d1f1f5584988223c73d248ae0a9809e86a5b44523b614f2fdcb/ripplex-1.0.1.tar.gz",
"yanked": false,
"yanked_reason": null
}
],
"upload_time": "2025-07-22 12:25:17",
"github": true,
"gitlab": false,
"bitbucket": false,
"codeberg": false,
"github_user": "sethburkart",
"github_project": "ripplex#readme",
"github_not_found": true,
"lcname": "ripplex"
}