action-dispatch


Nameaction-dispatch JSON
Version 0.1.0 PyPI version JSON
download
home_pageNone
SummaryA flexible action dispatching system with multi-dimensional routing capabilities
upload_time2025-07-20 18:27:14
maintainerNone
docs_urlNone
authorNone
requires_python>=3.9
licenseNone
keywords action context-based dispatch framework handler middleware multi-dimensional routing
VCS
bugtrack_url
requirements No requirements were recorded.
Travis-CI No Travis.
coveralls test coverage No coveralls.
            # Action Dispatch

[![PyPI version](https://badge.fury.io/py/action-dispatch.svg)](https://badge.fury.io/py/action-dispatch)
[![Python Support](https://img.shields.io/pypi/pyversions/action-dispatch.svg)](https://pypi.org/project/action-dispatch/)
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
[![Tests](https://github.com/eowl/action-dispatch/workflows/Tests/badge.svg)](https://github.com/eowl/action-dispatch/actions)

A powerful and flexible Python library for multi-dimensional action dispatching. Route function calls dynamically based on context dimensions like user roles, environments, API versions, or any custom attributes.

## Features

**Multi-dimensional Routing** - Dispatch based on multiple context attributes simultaneously  
**Dynamic Handler Registration** - Register handlers using decorators or programmatically  
**Flexible Context Matching** - Support for exact matches and fallback strategies  

## Installation

**Requirements:** Python 3.9+

```bash
pip install action-dispatch
```

## Quick Start

```python
from action_dispatch import ActionDispatcher

# Create dispatcher with dimensions
dispatcher = ActionDispatcher(['role', 'environment'])

# Register handlers using decorators
@dispatcher.handler("create_user", role="admin")
def admin_create_user(params):
    return f"Admin creating user: {params.get('username')}"

@dispatcher.handler("create_user", role="manager")  
def manager_create_user(params):
    return f"Manager creating user: {params.get('username')}"

# Define context class
class RequestContext:
    def __init__(self, role, environment):
        self.role = role
        self.environment = environment

# Dispatch actions based on context
admin_context = RequestContext("admin", "production")
result = dispatcher.dispatch(admin_context, "create_user", username="john")
print(result)  # "Admin creating user: john"
```

## Advanced Usage

### Multi-dimensional Routing

```python
dispatcher = ActionDispatcher(['role', 'environment', 'feature_flag'])

@dispatcher.handler("process_payment", 
                   role="admin", 
                   environment="production", 
                   feature_flag="new_payment_system")
def new_payment_handler(params):
    return "Processing with new payment system"

@dispatcher.handler("process_payment", 
                   role="admin", 
                   environment="production")
def default_payment_handler(params):
    return "Processing with default system"
```

### Global Handlers

```python
# Global handlers work across all contexts
@dispatcher.global_handler("health_check")
def health_check(params):
    return {"status": "healthy", "timestamp": time.time()}

# Global handlers have priority over scoped handlers
result = dispatcher.dispatch(any_context, "health_check")
```

### Programmatic Registration

```python
def custom_handler(params):
    return "Custom response"

# Register without decorators
dispatcher.register("custom_action", custom_handler, role="user")

# Register global handler
dispatcher.register_global("system_status", lambda p: "OK")
```

### Error Handling

```python
from action_dispatch import HandlerNotFoundError, InvalidDimensionError

try:
    result = dispatcher.dispatch(context, "unknown_action")
except HandlerNotFoundError as e:
    print(f"No handler found for action: {e.action}")
    print(f"Context rules: {e.rules}")

try:
    dispatcher.register("action", handler, invalid_dimension="value")
except InvalidDimensionError as e:
    print(f"Invalid dimension: {e.dimension}")
    print(f"Available dimensions: {e.available_dimensions}")
```

## Real-World Examples

### Web API with Role-Based Access Control

```python
from action_dispatch import ActionDispatcher

# Set up dispatcher for API routing
api_dispatcher = ActionDispatcher(['role', 'api_version'])

@api_dispatcher.handler("get_users", role="admin", api_version="v2")
def get_users_admin_v2(params):
    return {
        "users": get_all_users(),
        "total": count_all_users(),
        "permissions": ["read", "write", "delete"]
    }

@api_dispatcher.handler("get_users", role="user", api_version="v2") 
def get_users_regular_v2(params):
    return {
        "users": get_user_own_data(params['context_object']),
        "permissions": ["read"]
    }

# API endpoint
def api_get_users(request):
    try:
        result = api_dispatcher.dispatch(
            request.user, 
            "get_users",
            request_id=request.id
        )
        return JsonResponse(result)
    except HandlerNotFoundError:
        return JsonResponse({"error": "Forbidden"}, status=403)
```

### Microservices Environment Routing

```python
service_dispatcher = ActionDispatcher(['environment', 'service_version'])

@service_dispatcher.handler("process_order", 
                           environment="production", 
                           service_version="v2")
def process_order_prod_v2(params):
    # Use production database and new algorithm
    return production_order_processor.process(params['order_data'])

@service_dispatcher.handler("process_order", 
                           environment="staging")
def process_order_staging(params):
    # Use staging database with verbose logging
    return staging_order_processor.process(params['order_data'])
```

### Plugin System

```python
plugin_dispatcher = ActionDispatcher(['plugin_type', 'version'])

# Plugins can register their handlers
@plugin_dispatcher.handler("transform_data", 
                          plugin_type="image_processor", 
                          version="2.0")
def image_transform_v2(params):
    return enhanced_image_transform(params['data'])

# Dynamic plugin loading
for plugin in load_plugins():
    plugin.register_handlers(plugin_dispatcher)

# Process with appropriate plugin
result = plugin_dispatcher.dispatch(context, "transform_data", data=input_data)
```

## API Reference

### ActionDispatcher

#### `__init__(dimensions=None)`
Create a new dispatcher with optional dimensions.

- `dimensions` (list, optional): List of dimension names for routing

#### `@handler(action, **kwargs)`
Decorator to register a handler for specific action and dimensions.

- `action` (str): Action name
- `**kwargs`: Dimension values for routing

#### `@global_handler(action)`
Decorator to register a global handler that works across all contexts.

- `action` (str): Action name

#### `register(action, handler, **kwargs)`
Programmatically register a handler.

- `action` (str): Action name  
- `handler` (callable): Handler function
- `**kwargs`: Dimension values for routing

#### `dispatch(context_object, action_name, **kwargs)`
Dispatch an action based on context.

- `context_object`: Object with dimension attributes
- `action_name` (str): Action to dispatch
- `**kwargs`: Additional parameters passed to handler

### Exceptions

- `ActionDispatchError`: Base exception class
- `InvalidDimensionError`: Raised for invalid dimension parameters
- `HandlerNotFoundError`: Raised when no handler is found
- `InvalidActionError`: Raised for invalid action names

## Development

### Setting up Development Environment

```bash
# Clone the repository
git clone https://github.com/eowl/action-dispatch.git
cd action-dispatch

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

# Install development dependencies
pip install -e ".[dev]"

# Install pre-commit hooks
pre-commit install
```

### Running Tests

```bash
# Run all tests
python -m unittest discover tests -v

# Run with coverage
coverage run -m unittest discover tests
coverage report
coverage html

# Run specific test file
python -m unittest tests.test_action_dispatcher -v

# Run specific test class
python -m unittest tests.test_action_dispatcher.TestActionDispatcher -v
```

### Code Quality

```bash
# Format code
black action_dispatch tests

# Lint code  
flake8 action_dispatch tests

# Type checking
mypy action_dispatch
```

## Contributing

We welcome contributions! Please see our [Contributing Guide](CONTRIBUTING.md) for details.

1. Fork the repository
2. Create a feature branch (`git checkout -b feature/amazing-feature`)
3. Make your changes
4. Add tests for your changes
5. Ensure all tests pass (`python -m pytest`)
6. Commit your changes (`git commit -m 'Add amazing feature'`)
7. Push to the branch (`git push origin feature/amazing-feature`)
8. Open a Pull Request

## License

This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.

## Changelog

See [CHANGELOG.md](CHANGELOG.md) for a list of changes and releases.

<!-- ## Support

- 📖 [Documentation](https://action-dispatch.readthedocs.io)
- 🐛 [Issue Tracker](https://github.com/eowl/action-dispatch/issues)
- 💬 [Discussions](https://github.com/eowl/action-dispatch/discussions) -->

## Acknowledgments

- Inspired by the need for flexible routing in complex applications
- Built with modern Python best practices
- Thoroughly tested and documented

            

Raw data

            {
    "_id": null,
    "home_page": null,
    "name": "action-dispatch",
    "maintainer": null,
    "docs_url": null,
    "requires_python": ">=3.9",
    "maintainer_email": "eowl <eowl@me.com>",
    "keywords": "action, context-based, dispatch, framework, handler, middleware, multi-dimensional, routing",
    "author": null,
    "author_email": "eowl <eowl@me.com>",
    "download_url": "https://files.pythonhosted.org/packages/23/5d/9a3bdfa633dbffb7e24d8fa52e20e1c1573309a926dbe0ada95e104116c2/action_dispatch-0.1.0.tar.gz",
    "platform": null,
    "description": "# Action Dispatch\n\n[![PyPI version](https://badge.fury.io/py/action-dispatch.svg)](https://badge.fury.io/py/action-dispatch)\n[![Python Support](https://img.shields.io/pypi/pyversions/action-dispatch.svg)](https://pypi.org/project/action-dispatch/)\n[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)\n[![Tests](https://github.com/eowl/action-dispatch/workflows/Tests/badge.svg)](https://github.com/eowl/action-dispatch/actions)\n\nA powerful and flexible Python library for multi-dimensional action dispatching. Route function calls dynamically based on context dimensions like user roles, environments, API versions, or any custom attributes.\n\n## Features\n\n**Multi-dimensional Routing** - Dispatch based on multiple context attributes simultaneously  \n**Dynamic Handler Registration** - Register handlers using decorators or programmatically  \n**Flexible Context Matching** - Support for exact matches and fallback strategies  \n\n## Installation\n\n**Requirements:** Python 3.9+\n\n```bash\npip install action-dispatch\n```\n\n## Quick Start\n\n```python\nfrom action_dispatch import ActionDispatcher\n\n# Create dispatcher with dimensions\ndispatcher = ActionDispatcher(['role', 'environment'])\n\n# Register handlers using decorators\n@dispatcher.handler(\"create_user\", role=\"admin\")\ndef admin_create_user(params):\n    return f\"Admin creating user: {params.get('username')}\"\n\n@dispatcher.handler(\"create_user\", role=\"manager\")  \ndef manager_create_user(params):\n    return f\"Manager creating user: {params.get('username')}\"\n\n# Define context class\nclass RequestContext:\n    def __init__(self, role, environment):\n        self.role = role\n        self.environment = environment\n\n# Dispatch actions based on context\nadmin_context = RequestContext(\"admin\", \"production\")\nresult = dispatcher.dispatch(admin_context, \"create_user\", username=\"john\")\nprint(result)  # \"Admin creating user: john\"\n```\n\n## Advanced Usage\n\n### Multi-dimensional Routing\n\n```python\ndispatcher = ActionDispatcher(['role', 'environment', 'feature_flag'])\n\n@dispatcher.handler(\"process_payment\", \n                   role=\"admin\", \n                   environment=\"production\", \n                   feature_flag=\"new_payment_system\")\ndef new_payment_handler(params):\n    return \"Processing with new payment system\"\n\n@dispatcher.handler(\"process_payment\", \n                   role=\"admin\", \n                   environment=\"production\")\ndef default_payment_handler(params):\n    return \"Processing with default system\"\n```\n\n### Global Handlers\n\n```python\n# Global handlers work across all contexts\n@dispatcher.global_handler(\"health_check\")\ndef health_check(params):\n    return {\"status\": \"healthy\", \"timestamp\": time.time()}\n\n# Global handlers have priority over scoped handlers\nresult = dispatcher.dispatch(any_context, \"health_check\")\n```\n\n### Programmatic Registration\n\n```python\ndef custom_handler(params):\n    return \"Custom response\"\n\n# Register without decorators\ndispatcher.register(\"custom_action\", custom_handler, role=\"user\")\n\n# Register global handler\ndispatcher.register_global(\"system_status\", lambda p: \"OK\")\n```\n\n### Error Handling\n\n```python\nfrom action_dispatch import HandlerNotFoundError, InvalidDimensionError\n\ntry:\n    result = dispatcher.dispatch(context, \"unknown_action\")\nexcept HandlerNotFoundError as e:\n    print(f\"No handler found for action: {e.action}\")\n    print(f\"Context rules: {e.rules}\")\n\ntry:\n    dispatcher.register(\"action\", handler, invalid_dimension=\"value\")\nexcept InvalidDimensionError as e:\n    print(f\"Invalid dimension: {e.dimension}\")\n    print(f\"Available dimensions: {e.available_dimensions}\")\n```\n\n## Real-World Examples\n\n### Web API with Role-Based Access Control\n\n```python\nfrom action_dispatch import ActionDispatcher\n\n# Set up dispatcher for API routing\napi_dispatcher = ActionDispatcher(['role', 'api_version'])\n\n@api_dispatcher.handler(\"get_users\", role=\"admin\", api_version=\"v2\")\ndef get_users_admin_v2(params):\n    return {\n        \"users\": get_all_users(),\n        \"total\": count_all_users(),\n        \"permissions\": [\"read\", \"write\", \"delete\"]\n    }\n\n@api_dispatcher.handler(\"get_users\", role=\"user\", api_version=\"v2\") \ndef get_users_regular_v2(params):\n    return {\n        \"users\": get_user_own_data(params['context_object']),\n        \"permissions\": [\"read\"]\n    }\n\n# API endpoint\ndef api_get_users(request):\n    try:\n        result = api_dispatcher.dispatch(\n            request.user, \n            \"get_users\",\n            request_id=request.id\n        )\n        return JsonResponse(result)\n    except HandlerNotFoundError:\n        return JsonResponse({\"error\": \"Forbidden\"}, status=403)\n```\n\n### Microservices Environment Routing\n\n```python\nservice_dispatcher = ActionDispatcher(['environment', 'service_version'])\n\n@service_dispatcher.handler(\"process_order\", \n                           environment=\"production\", \n                           service_version=\"v2\")\ndef process_order_prod_v2(params):\n    # Use production database and new algorithm\n    return production_order_processor.process(params['order_data'])\n\n@service_dispatcher.handler(\"process_order\", \n                           environment=\"staging\")\ndef process_order_staging(params):\n    # Use staging database with verbose logging\n    return staging_order_processor.process(params['order_data'])\n```\n\n### Plugin System\n\n```python\nplugin_dispatcher = ActionDispatcher(['plugin_type', 'version'])\n\n# Plugins can register their handlers\n@plugin_dispatcher.handler(\"transform_data\", \n                          plugin_type=\"image_processor\", \n                          version=\"2.0\")\ndef image_transform_v2(params):\n    return enhanced_image_transform(params['data'])\n\n# Dynamic plugin loading\nfor plugin in load_plugins():\n    plugin.register_handlers(plugin_dispatcher)\n\n# Process with appropriate plugin\nresult = plugin_dispatcher.dispatch(context, \"transform_data\", data=input_data)\n```\n\n## API Reference\n\n### ActionDispatcher\n\n#### `__init__(dimensions=None)`\nCreate a new dispatcher with optional dimensions.\n\n- `dimensions` (list, optional): List of dimension names for routing\n\n#### `@handler(action, **kwargs)`\nDecorator to register a handler for specific action and dimensions.\n\n- `action` (str): Action name\n- `**kwargs`: Dimension values for routing\n\n#### `@global_handler(action)`\nDecorator to register a global handler that works across all contexts.\n\n- `action` (str): Action name\n\n#### `register(action, handler, **kwargs)`\nProgrammatically register a handler.\n\n- `action` (str): Action name  \n- `handler` (callable): Handler function\n- `**kwargs`: Dimension values for routing\n\n#### `dispatch(context_object, action_name, **kwargs)`\nDispatch an action based on context.\n\n- `context_object`: Object with dimension attributes\n- `action_name` (str): Action to dispatch\n- `**kwargs`: Additional parameters passed to handler\n\n### Exceptions\n\n- `ActionDispatchError`: Base exception class\n- `InvalidDimensionError`: Raised for invalid dimension parameters\n- `HandlerNotFoundError`: Raised when no handler is found\n- `InvalidActionError`: Raised for invalid action names\n\n## Development\n\n### Setting up Development Environment\n\n```bash\n# Clone the repository\ngit clone https://github.com/eowl/action-dispatch.git\ncd action-dispatch\n\n# Create virtual environment\npython -m venv venv\nsource venv/bin/activate  # On Windows: venv\\Scripts\\activate\n\n# Install development dependencies\npip install -e \".[dev]\"\n\n# Install pre-commit hooks\npre-commit install\n```\n\n### Running Tests\n\n```bash\n# Run all tests\npython -m unittest discover tests -v\n\n# Run with coverage\ncoverage run -m unittest discover tests\ncoverage report\ncoverage html\n\n# Run specific test file\npython -m unittest tests.test_action_dispatcher -v\n\n# Run specific test class\npython -m unittest tests.test_action_dispatcher.TestActionDispatcher -v\n```\n\n### Code Quality\n\n```bash\n# Format code\nblack action_dispatch tests\n\n# Lint code  \nflake8 action_dispatch tests\n\n# Type checking\nmypy action_dispatch\n```\n\n## Contributing\n\nWe welcome contributions! Please see our [Contributing Guide](CONTRIBUTING.md) for details.\n\n1. Fork the repository\n2. Create a feature branch (`git checkout -b feature/amazing-feature`)\n3. Make your changes\n4. Add tests for your changes\n5. Ensure all tests pass (`python -m pytest`)\n6. Commit your changes (`git commit -m 'Add amazing feature'`)\n7. Push to the branch (`git push origin feature/amazing-feature`)\n8. Open a Pull Request\n\n## License\n\nThis project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.\n\n## Changelog\n\nSee [CHANGELOG.md](CHANGELOG.md) for a list of changes and releases.\n\n<!-- ## Support\n\n- \ud83d\udcd6 [Documentation](https://action-dispatch.readthedocs.io)\n- \ud83d\udc1b [Issue Tracker](https://github.com/eowl/action-dispatch/issues)\n- \ud83d\udcac [Discussions](https://github.com/eowl/action-dispatch/discussions) -->\n\n## Acknowledgments\n\n- Inspired by the need for flexible routing in complex applications\n- Built with modern Python best practices\n- Thoroughly tested and documented\n",
    "bugtrack_url": null,
    "license": null,
    "summary": "A flexible action dispatching system with multi-dimensional routing capabilities",
    "version": "0.1.0",
    "project_urls": {
        "Bug Tracker": "https://github.com/eowl/action-dispatch/issues",
        "Changelog": "https://github.com/eowl/action-dispatch/blob/main/CHANGELOG.md",
        "Documentation": "https://action-dispatch.readthedocs.io",
        "Homepage": "https://github.com/eowl/action-dispatch",
        "Repository": "https://github.com/eowl/action-dispatch.git"
    },
    "split_keywords": [
        "action",
        " context-based",
        " dispatch",
        " framework",
        " handler",
        " middleware",
        " multi-dimensional",
        " routing"
    ],
    "urls": [
        {
            "comment_text": null,
            "digests": {
                "blake2b_256": "6a19c3d485a6101d7afaf6639c9682a191b303ef4b54d2697135caa4b7e4cd85",
                "md5": "22cefac501618fdf80a6fe8575484e65",
                "sha256": "1d7632aba2699cfcf950e746b4265090f6e5a3d5a0b52cd35e208026d5e54cde"
            },
            "downloads": -1,
            "filename": "action_dispatch-0.1.0-py3-none-any.whl",
            "has_sig": false,
            "md5_digest": "22cefac501618fdf80a6fe8575484e65",
            "packagetype": "bdist_wheel",
            "python_version": "py3",
            "requires_python": ">=3.9",
            "size": 8168,
            "upload_time": "2025-07-20T18:27:12",
            "upload_time_iso_8601": "2025-07-20T18:27:12.710894Z",
            "url": "https://files.pythonhosted.org/packages/6a/19/c3d485a6101d7afaf6639c9682a191b303ef4b54d2697135caa4b7e4cd85/action_dispatch-0.1.0-py3-none-any.whl",
            "yanked": false,
            "yanked_reason": null
        },
        {
            "comment_text": null,
            "digests": {
                "blake2b_256": "235d9a3bdfa633dbffb7e24d8fa52e20e1c1573309a926dbe0ada95e104116c2",
                "md5": "d71e241bf0d9257dac193bc8388ce6b5",
                "sha256": "d99d5a932642df1959550549bd41926d35d7c22b5d6e945d7445e1edef7c5dc4"
            },
            "downloads": -1,
            "filename": "action_dispatch-0.1.0.tar.gz",
            "has_sig": false,
            "md5_digest": "d71e241bf0d9257dac193bc8388ce6b5",
            "packagetype": "sdist",
            "python_version": "source",
            "requires_python": ">=3.9",
            "size": 93306,
            "upload_time": "2025-07-20T18:27:14",
            "upload_time_iso_8601": "2025-07-20T18:27:14.220155Z",
            "url": "https://files.pythonhosted.org/packages/23/5d/9a3bdfa633dbffb7e24d8fa52e20e1c1573309a926dbe0ada95e104116c2/action_dispatch-0.1.0.tar.gz",
            "yanked": false,
            "yanked_reason": null
        }
    ],
    "upload_time": "2025-07-20 18:27:14",
    "github": true,
    "gitlab": false,
    "bitbucket": false,
    "codeberg": false,
    "github_user": "eowl",
    "github_project": "action-dispatch",
    "travis_ci": false,
    "coveralls": false,
    "github_actions": true,
    "requirements": [],
    "lcname": "action-dispatch"
}
        
Elapsed time: 1.09615s