Skip to main content

A powerful, framework-agnostic plugin system for Python with advanced features like async/await support, hook priorities, timeouts, event namespacing, and plugin discovery.

Project description

Nitro Dispatch

A powerful, framework-agnostic plugin system for Python with advanced features like async/await support, hook priorities, timeouts, event namespacing, and plugin discovery.

Requirements

Python 3.8 or higher is required.

Installation

pip install nitro-dispatch

Features

Core Features

  • Simple API - Easy to learn with minimal boilerplate
  • Framework Agnostic - Works with any Python application
  • Hook System - Register callbacks for custom events with @hook decorator
  • Data Filtering - Each hook can modify and transform data
  • Error Isolation - Plugin errors don't crash your application
  • Dependency Management - Automatic dependency resolution
  • Zero Dependencies - No external dependencies required

Advanced Features

  • Async/Await Support - Native async hook execution
  • Hook Priorities - Control execution order (higher priority = runs first)
  • Timeout Protection - Prevent slow plugins from blocking
  • Event Namespacing - Organize events hierarchically (user.login, db.save)
  • Wildcard Events - Listen to multiple events (user.*, db.before_*)
  • Plugin Discovery - Auto-discover plugins from directories
  • Hot Reloading - Reload plugins without restarting
  • Stop Propagation - Halt event chain from within hooks
  • Hook Tracing - Debug with detailed execution timing
  • Built-in Lifecycle Events - Hook into plugin lifecycle
  • Metadata Validation - Ensure plugin quality

Quick Start

from nitro_dispatch import PluginManager, PluginBase, hook

class WelcomePlugin(PluginBase):
    name = "welcome"

    @hook('user.login', priority=100)
    def greet_user(self, data):
        print(f"Welcome, {data['username']}!")
        data['greeted'] = True
        return data

manager = PluginManager()
manager.register(WelcomePlugin)
manager.load_all()

result = manager.trigger('user.login', {'username': 'Alice'})
# Output: Welcome, Alice!

Core Concepts

1. Plugins

Plugins inherit from PluginBase:

class MyPlugin(PluginBase):
    name = "my_plugin"              # Required: unique identifier
    version = "1.0.0"               # Plugin version
    description = "Does cool stuff" # Human-readable description
    author = "Your Name"            # Plugin author
    dependencies = []               # List of required plugin names

2. Hooks

Register callbacks for events using the @hook decorator:

class ValidationPlugin(PluginBase):
    name = "validator"

    @hook('before_save', priority=100, timeout=5.0)
    def validate(self, data):
        if not data.get('email'):
            raise ValueError("Email required")
        return data

Or register manually in on_load():

class LoggingPlugin(PluginBase):
    name = "logger"

    def on_load(self):
        self.register_hook('before_save', self.log_data, priority=50)

    def log_data(self, data):
        print(f"Saving: {data}")
        return data

3. Data Filtering

Hooks execute in priority order (highest first). Each hook receives data, modifies it, and returns it:

# Hook 1 (priority=100)
@hook('process_data', priority=100)
def add_timestamp(self, data):
    data['timestamp'] = datetime.now()
    return data

# Hook 2 (priority=50)
@hook('process_data', priority=50)
def add_id(self, data):
    data['id'] = generate_id()
    return data

# Data flows: original → add_timestamp → add_id → final result

Advanced Usage

Hook Priorities

Control execution order with priority values (higher = earlier):

class SecurityPlugin(PluginBase):
    @hook('user.login', priority=100)  # Runs first
    def security_check(self, data):
        return data

class LoggingPlugin(PluginBase):
    @hook('user.login', priority=10)  # Runs last
    def log_login(self, data):
        return data

Async/Await Support

Native support for async hooks:

class AsyncPlugin(PluginBase):
    @hook('data.fetch')
    async def fetch_from_api(self, data):
        result = await aiohttp.get('https://api.example.com')
        data['result'] = await result.json()
        return data

# Trigger async
result = await manager.trigger_async('data.fetch', {})

Hook Timeouts

Prevent slow plugins from blocking:

@hook('process_data', timeout=2.0)  # 2 second timeout
def slow_process(self, data):
    # If this takes > 2s, HookTimeoutError is raised
    time.sleep(5)  # This will timeout!
    return data

Event Namespacing with Wildcards

Organize events hierarchically and use wildcards:

class AuditPlugin(PluginBase):
    @hook('user.*')  # Matches user.login, user.logout, etc.
    def audit_user_events(self, data):
        log.info(f"User event: {data}")
        return data

    @hook('db.before_*')  # Matches db.before_save, db.before_delete
    def audit_db_operations(self, data):
        log.info(f"DB operation: {data}")
        return data

# Trigger events
manager.trigger('user.login', {})      # Caught by user.*
manager.trigger('db.before_save', {})  # Caught by db.before_*

Stop Propagation

Stop the hook chain from within a hook:

from nitro_dispatch import StopPropagation

class ValidationPlugin(PluginBase):
    @hook('process_data', priority=100)
    def validate(self, data):
        if not data.get('valid'):
            raise StopPropagation("Invalid data")
        return data

# Hooks with lower priority won't execute if validation fails

Plugin Discovery

Auto-discover and load plugins from directories:

manager = PluginManager()

# Discover plugins from directory
discovered = manager.discover_plugins(
    '~/.myapp/plugins',
    pattern='*_plugin.py',
    recursive=True
)

print(f"Discovered: {discovered}")
manager.load_all()

Hot Reloading

Reload plugins without restarting:

# Reload a specific plugin
manager.reload('my_plugin')

# The plugin will be unloaded, module reloaded, and loaded again

Enable/Disable Plugins

Toggle plugins at runtime:

# Disable a plugin (hooks won't execute)
manager.disable_plugin('optional_plugin')

# Re-enable it
manager.enable_plugin('optional_plugin')

Built-in Lifecycle Events

Hook into the plugin system's lifecycle:

def on_plugin_loaded(data):
    print(f"Plugin loaded: {data['plugin_name']}")
    return data

manager.register_hook(
    PluginManager.EVENT_PLUGIN_LOADED,
    on_plugin_loaded
)

# Built-in events:
# - nitro.plugin.registered
# - nitro.plugin.loaded
# - nitro.plugin.unloaded
# - nitro.plugin.error
# - nitro.app.startup
# - nitro.app.shutdown

Hook Tracing/Debugging

Enable detailed logging for debugging:

manager = PluginManager(log_level='DEBUG')
manager.enable_hook_tracing(True)

# Now all hook executions are logged with timing info
result = manager.trigger('user.login', {})

Error Handling Strategies

Configure how errors are handled:

# Log and continue (default)
manager.set_error_strategy('log_and_continue')

# Stop on first error
manager.set_error_strategy('fail_fast')

# Collect all errors
manager.set_error_strategy('collect_all')

Plugin Configuration

Pass configuration to plugins:

config = {
    'cache': {
        'max_size': 100,
        'ttl': 3600
    }
}
manager = PluginManager(config=config)

class CachePlugin(PluginBase):
    name = "cache"

    def on_load(self):
        max_size = self.get_config('max_size', 50)
        ttl = self.get_config('ttl', 1800)

API Reference

PluginManager

Method Description
__init__(config, log_level, validate_metadata) Initialize manager
register(plugin_class) Register a plugin class
load(plugin_name) Load a specific plugin
load_all() Load all registered plugins
unload(plugin_name) Unload a plugin
unload_all() Unload all plugins
reload(plugin_name) Hot reload a plugin
discover_plugins(directory, pattern, recursive) Auto-discover plugins
trigger(event, data) Trigger event (sync)
trigger_async(event, data) Trigger event (async)
enable_plugin(name) Enable a plugin
disable_plugin(name) Disable a plugin
enable_hook_tracing(enabled) Enable debugging
set_error_strategy(strategy) Set error handling

PluginBase

Attribute/Method Description
name Plugin name (required)
version Plugin version
description Plugin description
author Plugin author
dependencies List of required plugins
on_load() Called when plugin loads
on_unload() Called when plugin unloads
on_error(error) Called on hook errors
register_hook(event, callback, priority, timeout) Register a hook
get_config(key, default) Get configuration value

@hook Decorator

@hook(event_name, priority=50, timeout=None, async_hook=False)
Parameter Description
event_name Event to listen for (supports wildcards)
priority Execution priority (higher = earlier). Default: 50
timeout Max execution time in seconds. Default: None
async_hook Whether hook is async (auto-detected)

Examples

Basic Usage

python examples/basic_usage.py

Advanced Features

python examples/advanced_usage.py
python examples/advanced_features.py

Plugin Discovery

python examples/discovery_example.py

Development

Setup

git clone https://github.com/nitro/nitro-dispatch.git
cd nitro-dispatch
pip install -e ".[dev]"

Run Tests

pytest
pytest --cov=nitro_dispatch

Format Code

black nitro_dispatch tests examples

License

Please see LICENSE for licensing details.

Author

github.com/sn

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

nitro_dispatch-1.0.0.tar.gz (34.9 kB view details)

Uploaded Source

Built Distribution

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

nitro_dispatch-1.0.0-py3-none-any.whl (18.9 kB view details)

Uploaded Python 3

File details

Details for the file nitro_dispatch-1.0.0.tar.gz.

File metadata

  • Download URL: nitro_dispatch-1.0.0.tar.gz
  • Upload date:
  • Size: 34.9 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.1.0 CPython/3.13.7

File hashes

Hashes for nitro_dispatch-1.0.0.tar.gz
Algorithm Hash digest
SHA256 2827aecff01b127be106c0f1191fa36d81d7ee97212d41e9b1e5a01bcb1acb21
MD5 e1293cb8ec9aaee77c5a7b8ace096359
BLAKE2b-256 61dc2fbfecc815b2e5e5984d8813787f616811a265ff34c5b5190b9f5fbf485e

See more details on using hashes here.

File details

Details for the file nitro_dispatch-1.0.0-py3-none-any.whl.

File metadata

  • Download URL: nitro_dispatch-1.0.0-py3-none-any.whl
  • Upload date:
  • Size: 18.9 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.1.0 CPython/3.13.7

File hashes

Hashes for nitro_dispatch-1.0.0-py3-none-any.whl
Algorithm Hash digest
SHA256 b958ae54990e8a73fc1d9cf931b8830df871f2e3edcb93a5814f1a17a3234f87
MD5 cfa43191233c2d28982b14493b477c5e
BLAKE2b-256 503e4161c6bb8bfed68edb79afe93e8512372e418fb5bd750088ec578dee0eeb

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