Stevedore (Python Plugin Manager)

5.7.0 · active · verified Sun Mar 29

Stevedore is a Python library designed to simplify the management of dynamic plugins for Python applications. It builds on top of `setuptools` entry points, providing a standardized and less repetitive way to discover and load extensions at runtime. The library abstracts away much of the boilerplate associated with `__import__` or `importlib` for plugin systems. [2, 5]

Warnings

Install

Imports

Quickstart

This quickstart demonstrates how to define a plugin interface and a concrete plugin, then use `stevedore.ExtensionManager` to discover and load it. It simulates `setuptools` entry point registration for a self-contained, runnable example. In a real application, plugins would be installed as separate packages declaring their entry points in `setup.py` or `pyproject.toml`. [1, 3, 5]

import os
from setuptools import setup, find_packages
from stevedore import extension

# 1. Define a plugin interface (e.g., in myapp/plugins/base.py)
# In a real scenario, this would be in a separate package.
# For this quickstart, we'll simulate it.
plugin_base_code = '''
import abc

class FormatterBase(abc.ABC):
    @abc.abstractmethod
    def format(self, data):
        """Format the data and return a string."""
        pass
'''

# 2. Implement a concrete plugin (e.g., in myapp_simple_formatter/formatter.py)
# In a real scenario, this would be in a separate package.
plugin_impl_code = '''
from myapp.plugins.base import FormatterBase

class SimpleFormatter(FormatterBase):
    def format(self, data):
        return f"Simple format: {data['value']}"
'''

# Create dummy files for demonstration
if not os.path.exists('myapp/plugins'):
    os.makedirs('myapp/plugins')
with open('myapp/plugins/base.py', 'w') as f:
    f.write(plugin_base_code)
if not os.path.exists('myapp_simple_formatter'):
    os.makedirs('myapp_simple_formatter')
with open('myapp_simple_formatter/formatter.py', 'w') as f:
    f.write(plugin_impl_code)
with open('myapp/__init__.py', 'w') as f: pass
with open('myapp_simple_formatter/__init__.py', 'w') as f: pass

# 3. Define entry points in setup.py (or pyproject.toml)
# For a runnable quickstart, we'll use a dummy setup for the plugin
# and then manually register it to simulate installation.
# In a real project, myapp_simple_formatter would have its own setup.py
# and be installed via pip install -e .

# Simulate plugin registration by creating a pseudo-entry-point
# This is for quickstart demonstration; usually setuptools handles this via installation.
# This specific 'hack' for dynamic registration during runtime is not standard Stevedore usage,
# but it makes the quickstart runnable without a full package installation process.

# We'll use a hack to make the quickstart self-contained and runnable.
# In a real scenario, 'myapp_simple_formatter' would be an installed package
# with its entry point declared in its setup.py (or pyproject.toml).
# To simulate: we directly add the module to sys.modules and define a mock entry_points function.

import sys
sys.path.insert(0, os.path.abspath('.'))

# Temporarily import the base and plugin to make them available
from myapp.plugins.base import FormatterBase
from myapp_simple_formatter.formatter import SimpleFormatter

# Mock the entry point discovery for demonstration
def mock_entry_points(group=None):
    if group == 'myapp.formatters':
        class MockEntryPoint:
            def __init__(self, name, load_callable):
                self.name = name
                self._load_callable = load_callable
            def load(self):
                return self._load_callable
        return {
            'simple': MockEntryPoint('simple', SimpleFormatter)
        }
    return {}

# Replace the real entry_points discovery for this script's scope
# This is a highly simplified mock for quickstart purposes and not how stevedore usually discovers plugins
# In practice, entry points are discovered via 'importlib.metadata.entry_points()' after package installation.
import stevedore.extension
stevedore.extension.entry_points = mock_entry_points


# 4. Use stevedore in your application to load plugins
def main():
    print("Loading formatters...")
    mgr = extension.ExtensionManager(
        namespace='myapp.formatters',
        invoke_on_load=True
    )

    if not mgr.extensions:
        print("No formatters found. Ensure plugin packages are installed and define 'myapp.formatters' entry points.")
        return

    print(f"Found {len(mgr.extensions)} formatter(s).")
    for ext in mgr.extensions:
        print(f"  - {ext.name}: {ext.obj.format({'value': 123})}")

if __name__ == '__main__':
    main()

# Clean up dummy files/dirs
import shutil
shutil.rmtree('myapp')
shutil.rmtree('myapp_simple_formatter')

view raw JSON →