A strictly-typed, generics-first plugin framework for Python 3.13: hooks with derived return types
Project description
pluginkit
A small, strictly-typed, generics-first plugin framework for Python 3.13+. Declare extension points, let plugins implement them, discover plugins via entry points - and, unlike untyped hook systems, get the right return type for every call, derived from the spec and checked by your type checker.
pm.caller(spec) hands back a caller whose result type matches the dispatch mode -
list[R] for collecting, R | None for firstresult, R for pipeline - with no
hand-annotations and no drift. Zero runtime dependencies, a py.typed marker, and a
few readable files.
uv add pluginkit # or: pip install pluginkit
from pluginkit import Extension, ExtensionPoint, PluginManager
extension_point = ExtensionPoint("greeter")
extension = Extension("greeter")
class Specs:
@staticmethod
@extension_point
def greeting(name: str) -> str:
"""Return a greeting for the given name."""
class Casual:
@extension
def greeting(self, name: str) -> str:
return f"hey {name}!"
pm = PluginManager("greeter")
pm.add_extension_points(Specs)
pm.register(Casual(), name="casual")
greetings = pm.caller(Specs.greeting)(name="Ada") # typed list[str] - derived, not asserted
print(greetings) # ['hey Ada!']
What it supports
- collecting,
firstresult, and pipeline (fold/middleware) hooks; - call ordering with
tryfirst/trylast, plusoptionalandtarget; - generator wrappers that decorate results and observe exceptions safely;
- historic hooks replayed to plugins registered later;
- async dispatch via
AsyncPluginManager(awaits coroutine impls); - plugin lifecycle:
register,unregister(by name or object),set_blocked, lookup,call_extra; - registration-time validation and call-time argument checking (failures are loud);
- external plugin discovery via the stdlib
importlib.metadata(no setuptools); - thread-safe registry mutation.
Layout
src/pluginkit/ the library (pure - no demo code)
examples/ everything that uses the library (not shipped):
cookbook/ worked examples: bite-size scripts + full apps
tour/ pluginkit-tour: a guided CLI walkthrough
external-plugin/ a separate distribution discovered via entry points
docs/ mkdocs + Material documentation
tests/ library, tour, and cookbook tests
Everything that demonstrates the library lives under examples/. The cookbook
holds standalone examples (from one-mechanism snippets to complete FastAPI/Click/pytest
apps); the tour is a guided walkthrough on one host; the external-plugin shows
cross-package discovery via entry points.
Use it
make install # uv sync (library + tour + external plugin)
make test # pytest (framework, tour, examples)
make lint # ruff + mypy + pyright
make docs-serve # serve the docs at http://127.0.0.1:8000
make docs-build # build the docs (strict)
Two ways to learn it
The tour (examples/tour/) walks through one mechanism at a time on a single host:
make run # run every step
make run DEMO=wrapper # run one
uv run pluginkit-tour list
The cookbook applies the library to realistic domains and frameworks - see
examples/cookbook/:
uv run python examples/cookbook/report_builder.py
uv run python examples/cookbook/fastapi_app.py
uv run python examples/cookbook/cli_app.py --help
uv run python examples/cookbook/app_lifecycle.py
Documentation
Full docs (concepts, one page per mechanism, production/hardening notes, and a
generated API reference) live under docs/. Serve them with
make docs-serve.
Is it production ready?
It is solid - exception-safe wrappers, fail-fast validation, lifecycle management, resilient discovery, thread-safe mutation, strict typing, and a test suite. But for anything you ship, prefer pluggy itself: it is maintained and battle tested by pytest, tox, and datasette. See docs/production/vs-pluggy.md for the honest inventory of what differs.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file pluginkit-0.4.6.tar.gz.
File metadata
- Download URL: pluginkit-0.4.6.tar.gz
- Upload date:
- Size: 15.9 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
6b14b6c680575a4bc59d89ad20a3ca93fb1743e812cacdafe09eaaf60829457c
|
|
| MD5 |
8e32ed7a657da91c487332ba05629d75
|
|
| BLAKE2b-256 |
41e1c8c23b8d05358ca55d3f0906d81c61ac146e268433e8ba6025a04e21cfef
|
Provenance
The following attestation bundles were made for pluginkit-0.4.6.tar.gz:
Publisher:
release.yml on winterop-com/pluginkit
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
pluginkit-0.4.6.tar.gz -
Subject digest:
6b14b6c680575a4bc59d89ad20a3ca93fb1743e812cacdafe09eaaf60829457c - Sigstore transparency entry: 1953402924
- Sigstore integration time:
-
Permalink:
winterop-com/pluginkit@a330c6f9225e9146ef6dc1ae976a021bf5c1ceeb -
Branch / Tag:
refs/tags/v0.4.6 - Owner: https://github.com/winterop-com
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@a330c6f9225e9146ef6dc1ae976a021bf5c1ceeb -
Trigger Event:
push
-
Statement type:
File details
Details for the file pluginkit-0.4.6-py3-none-any.whl.
File metadata
- Download URL: pluginkit-0.4.6-py3-none-any.whl
- Upload date:
- Size: 18.7 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
60ba0f5d4475ba5754875e71aeb5c7c50ce9806e55fe09c58afd62116e95db74
|
|
| MD5 |
d4ef2f16f617bc6849fd3a9adf228bad
|
|
| BLAKE2b-256 |
4591c430f9202485179296aea2e9c97334cdfe8db47ea1549057de2dd063dc33
|
Provenance
The following attestation bundles were made for pluginkit-0.4.6-py3-none-any.whl:
Publisher:
release.yml on winterop-com/pluginkit
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
pluginkit-0.4.6-py3-none-any.whl -
Subject digest:
60ba0f5d4475ba5754875e71aeb5c7c50ce9806e55fe09c58afd62116e95db74 - Sigstore transparency entry: 1953403255
- Sigstore integration time:
-
Permalink:
winterop-com/pluginkit@a330c6f9225e9146ef6dc1ae976a021bf5c1ceeb -
Branch / Tag:
refs/tags/v0.4.6 - Owner: https://github.com/winterop-com
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@a330c6f9225e9146ef6dc1ae976a021bf5c1ceeb -
Trigger Event:
push
-
Statement type: