Skip to main content

A strictly-typed, generics-first plugin framework for Python 3.13: hooks with derived return types

Project description

pluginkit

PyPI Python CI Docs License: MIT

A small, strictly-typed, generics-first plugin framework for Python 3.13+. Declare hook specifications, 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.

pip install pluginkit   # or: uv add pluginkit
from pluginkit import HookimplMarker, HookspecMarker, PluginManager

hookspec = HookspecMarker("greeter")
hookimpl = HookimplMarker("greeter")


class Specs:
    @staticmethod
    @hookspec
    def greeting(name: str) -> str:
        """Return a greeting for the given name."""


class Casual:
    @hookimpl
    def greeting(self, name: str) -> str:
        return f"hey {name}!"


pm = PluginManager("greeter")
pm.add_hookspecs(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, plus optionalhook and specname;
  • 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):
  recipes/                   standalone single-file scripts, run directly
  tour/                      pluginkit-tour: a guided CLI walkthrough
  external-plugin/           a separate distribution discovered via entry points
docs/                      mkdocs + Material documentation
tests/                     library, tour, and recipe tests

Everything that demonstrates the library lives under examples/. The recipes are independent scripts you run on their own; 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 recipes apply the library to different realistic domains - see examples/:

uv run python examples/recipes/report_builder.py
uv run python examples/recipes/notification_router.py
uv run python examples/recipes/validation_rules.py
uv run python examples/recipes/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


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

pluginkit-0.3.1.tar.gz (14.0 kB view details)

Uploaded Source

Built Distribution

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

pluginkit-0.3.1-py3-none-any.whl (16.5 kB view details)

Uploaded Python 3

File details

Details for the file pluginkit-0.3.1.tar.gz.

File metadata

  • Download URL: pluginkit-0.3.1.tar.gz
  • Upload date:
  • Size: 14.0 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for pluginkit-0.3.1.tar.gz
Algorithm Hash digest
SHA256 c345a4ffe19834afe3d5147e7291e876745a5b485fa5f8915d31fddb36c9f231
MD5 88d1f919d64546e8edc1f97a6774a335
BLAKE2b-256 b52549a6354ea0bdd3a5912287932d56ee340d275b8b668743e2a8c3654d01aa

See more details on using hashes here.

Provenance

The following attestation bundles were made for pluginkit-0.3.1.tar.gz:

Publisher: release.yml on winterop-com/pluginkit

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file pluginkit-0.3.1-py3-none-any.whl.

File metadata

  • Download URL: pluginkit-0.3.1-py3-none-any.whl
  • Upload date:
  • Size: 16.5 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for pluginkit-0.3.1-py3-none-any.whl
Algorithm Hash digest
SHA256 1a371c1c724c5285224f35368fc3d24d9f920689856ca7df9e8c6fda8fb8dc0c
MD5 7b57d9365b71d474746d8a0baf21680e
BLAKE2b-256 10b81572d2024412ccc8ce9beb7025b640374843f2f48c6c97e68b9472334ec8

See more details on using hashes here.

Provenance

The following attestation bundles were made for pluginkit-0.3.1-py3-none-any.whl:

Publisher: release.yml on winterop-com/pluginkit

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

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