PluginForge
Application-agnostic Python plugin framework built on pluggy.
PluginForge adds the layers that pluggy is missing: YAML configuration, plugin lifecycle management, enable/disable per config, dependency resolution, FastAPI integration, and i18n support.
Installation
pip install pluginforge
FastAPI integration is built in; install FastAPI alongside PluginForge if your application uses it:
pip install pluginforge fastapi
(The [fastapi] extra existed in pre-0.6.0 releases and was a non-functional shim; it was removed in v0.6.0. Install FastAPI as a normal dependency of your consuming application.)
Quickstart
1. Create a plugin
from pluginforge import BasePlugin
class HelloPlugin(BasePlugin):
name = "hello"
version = "1.0.0"
description = "A hello world plugin"
def activate(self):
print(f"Hello plugin activated with config: {self.config}")
def get_routes(self):
from fastapi import APIRouter
router = APIRouter()
@router.get("/hello")
def hello():
return {"message": self.config.get("greeting", "Hello!")}
return [router]
2. Configure your app
# config/app.yaml
app:
name: "MyApp"
version: "1.0.0"
default_language: "en"
plugins:
entry_point_group: "myapp.plugins"
enabled:
- "hello"
disabled: []
# config/plugins/hello.yaml
greeting: "Hello from PluginForge!"
3. Use PluginManager
from pluginforge import PluginManager
pm = PluginManager("config/app.yaml")
# Register plugins directly (or use entry points for auto-discovery)
result = pm.register_plugins([HelloPlugin])
print(f"Activated: {result.activated}") # ['hello']
print(f"Filtered: {result.filtered_out()}") # {} when all activated
# Access plugins
for plugin in pm.get_active_plugins():
print(f"Active: {plugin.name} v{plugin.version}")
# Mount FastAPI routes
from fastapi import FastAPI
app = FastAPI()
pm.mount_routes(app) # Routes under /api/ (configurable prefix)
Features
- YAML Configuration - App config, per-plugin config, and i18n strings
- Plugin Lifecycle - init, activate, deactivate with error handling
- Structured Diagnostics -
DiscoveryResultsurfaces per-pluginPluginState; structuredPluginErrors carry the cause exception, lifecycle phase, and severity.DiscoveryResult.by_filter_reason(reason)and (v0.10.0)DiscoveryDiff.by_filter_reason(reason)group plugins by filter outcome in one call - Hot-Reload - Reload an active plugin's module via
reload_plugin(name)without restarting the app - Entry Point Rediscovery - Pick up newly-installed plugins at runtime via
rediscover(), no process restart required - Enable/Disable - Control plugins via config lists
- Live Config Refresh -
refresh_config()replaces the app-config snapshot and notifies active plugins through theon_config_changedhook. v0.10.0 addsmerge_app_config(overlay, *, notify=True)for deep-merge overlay semantics (replaces the_app_config = ...hack consumers were using) plus anotify=Falsekwarg onrefresh_configfor the no-active-plugins startup path - Dependency Resolution - Topological sorting with circular dependency detection
- Extension Points - Query plugins by interface with
get_extensions(type) - Config Schema Validation - Declare expected config types per plugin
- Health Checks - Monitor plugin status via
health_check() - Pre-Activate Hooks - Reject plugins before activation (license checks, etc.)
- Version Gating - Enforce
api_versionandmin_app_versionwith configurable severity - Application Identity Gating - Declare
target_applicationon plugins andapp_idon the host. Plugins whosetarget_applicationmismatches the host'sapp_id, or whosetarget_applicationis not declared at all, refuse to activate. Permissive: hosts withoutapp_idsee no validation - Lifecycle Visibility -
PluginStatecarriesactivated_at/last_config_change/sourcetimestamps;inspect_plugin(name)aggregates state, config, health, hooks, routes, and identity into one snapshot;on_plugin_activated/on_plugin_deactivated/on_config_refreshedevent hooks notify subscribers after lifecycle transitions - FastAPI Integration - Mount plugin routes with configurable prefix
- Idempotent Route Mounting (v0.8.0) -
mount_routesis safe to re-call; no route-table accumulation acrossTestClientlifespans (closes the v0.7.0 recursion-cascade reported by downstream consumers) - Test Helpers (v0.8.0) -
pluginforge.testing.IsolatedPluginManagerandMockPluginfor consumer-app test wiring - Single-Router Convention (v0.8.0) - one router per
get_routes()is recommended; multi-router plugins emit aDeprecationWarning - Type Annotations (PEP 561) -
py.typedmarker shipped;mypy/pyrightconsume PluginForge's full type information - Alembic Support - Collect migration directories from plugins
- i18n - Multi-language strings from YAML with fallback
- Security - Plugin name validation and path traversal prevention
For detailed documentation, see the Wiki.
Entry Point Discovery
Register plugins as entry points in your pyproject.toml:
[project.entry-points."myapp.plugins"]
hello = "myapp.plugins.hello:HelloPlugin"
Then use discover_plugins() instead of register_plugins():
pm = PluginManager("config/app.yaml")
result = pm.discover_plugins() # Auto-discovers from entry points
# Later, after a new plugin is installed (e.g. poetry install in another shell):
diff = pm.rediscover()
print(f"Newly activated: {diff.added}")
print(f"Removed: {diff.removed}")
i18n
# config/i18n/en.yaml
common:
save: "Save"
cancel: "Cancel"
pm.get_text("common.save", "en") # "Save"
pm.get_text("common.save", "de") # "Speichern"
Documentation
The full documentation is available in the Wiki and the in-repo guides:
- Consumer Integration Guide - wire PluginForge into your FastAPI app from scratch (covers lifespan, mounting, hot reload, testing)
- Plugin Author Guide - write a PluginForge plugin (BasePlugin contract, identity gating, single-router convention, lifecycle hooks)
- Getting Started
- BasePlugin
- PluginManager
- Configuration
- Discovery and Dependencies
- Lifecycle
- Hooks
- Extensions
- FastAPI Integration
- Alembic Integration
- i18n
- Security
- Examples
- Changelog
- Roadmap
Development
make install-dev # Install with dev dependencies
make test # Run tests
make lint # Run ruff linter
make format # Format code
make ci # Full CI pipeline (lint + format-check + test)
make help # Show all available targets
Pre-commit hooks
This project ships a pre-commit configuration that runs ruff and ruff-format on every commit. After make install-dev, register the git hook once per checkout:
poetry run pre-commit install
From that point, every git commit runs the hooks; if ruff-format rewrites a file, the commit is aborted so you can re-stage and try again. To run the hooks against the entire repo on demand:
poetry run pre-commit run --all-files
The ruff version pinned in .pre-commit-config.yaml matches the ruff dev dependency in pyproject.toml. Bump both together when upgrading.
License
MIT
Metadata
Release files for pluginforge 0.10.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| pluginforge-0.10.0.tar.gz | 31.1 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| pluginforge-0.10.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 66.1 kB
Release files / pluginforge-0.10.0.tar.gz
| Download URL | pluginforge-0.10.0.tar.gz |
|---|---|
| Size | 31.1 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
91c5cae22b263c699d0644f441e05d8715e32881ed7a84af3ec1f84fdf9acd63
|
|
BLAKE2b-256 checksum How to use checksums |
371505f44ab468b779858e0d59c69b64c6ab0a2fbd53cc2c0f3c3b59d8b1e40d
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
poetry/2.3.2 CPython/3.11.13 Linux/6.8.0-117-lowlatency
|
Release files / pluginforge-0.10.0-py3-none-any.whl
| Download URL | pluginforge-0.10.0-py3-none-any.whl |
|---|---|
| Size | 35.0 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
da5281a034834c4177a931c74b9e830cadf5f1e70910cf7c59752d2fbd6cf07b
|
|
BLAKE2b-256 checksum How to use checksums |
e99ba091f2c23101417473ffb9848e97f8a7757eddc9fdf311a3e6edda281ca6
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
poetry/2.3.2 CPython/3.11.13 Linux/6.8.0-117-lowlatency
|