Skip to main content

A Signal-based reactive component microkernel for Python backend services

Project description

SignalPy Kernel

PyPI Python License Docs

A Signal-based reactive component microkernel for Python backend services.

The kernel is three reactive primitives — Signal, Computed, Effect — plus component wiring. Everything else (config, logging, credentials, storage, REST, MCP, CLI) is just components built on top.

Disclosure. Built with Claude's help. The author hopes it lands somewhere between "trash" and "god code" — and is actively asking Python folks who know reactive systems, DI containers, or microkernels to tell them which mistakes were made. Reviews welcome via Issues or Discussions.

Install

pip install signalpy-kernel             # core kernel only (zero deps)
pip install "signalpy-kernel[all]"      # + providers + REST/CLI + tracing

60-second example

import asyncio
from pydantic import BaseModel
from signalpy.kernel import (
    Kernel, component, provides, requires, runnable, lifecycle, computed, effect,
)
from signalpy.providers.config import ConfigProvider
from signalpy.providers.logging_provider import LoggingProvider


class GreetParams(BaseModel):
    name: str = "world"


@component("greeter", version="1.0")
@provides("IGreeter")
@requires(config="IConfig")
class Greeter:

    @lifecycle.activate
    def activate(self):
        pass  # self.rt.config, self.rt.logger, etc. now available

    @computed
    def prefix(self):
        # Cached. Auto-recomputes when config changes.
        return self.rt.config.get("greeter.prefix", "Hello")

    @effect
    def on_prefix_change(self):
        # Auto-tracks deps. Re-runs when they change.
        print(f"prefix is now: {self.rt.config.get('greeter.prefix')}")

    @runnable("greet", params=GreetParams, description="Greet someone by name")
    async def greet(self, params):
        return {"message": f"{self.prefix()}, {params.name}!"}


async def main():
    kernel = Kernel()
    kernel.discover([ConfigProvider, LoggingProvider, Greeter])
    await kernel.boot()

    result = await kernel.bus.invoke("greeter.greet", {"name": "Alice"})
    print(result)  # {"message": "Hello, Alice!"}

    # Change config — the @effect re-runs automatically.
    kernel.registry.require("IConfig").set("greeter.prefix", "Howdy")

    await kernel.shutdown()


asyncio.run(main())

The same @runnable is automatically exposed as a REST endpoint, MCP tool, or CLI command depending on which transport adapter you discover. Components never know which transport serves them.

What makes it different

  • Reactivity is the foundation, not a layer on top. Every injected service is a Signal. Reading self.rt.config inside an @effect or @computed is a tracked read — when config changes, the effect re-runs automatically. No manual callbacks, no @on_change, no re-injection hacks.

  • 12 decorators total. @component, @provides, @requires, @computed, @effect, @lifecycle.*, @runnable, @api, @subscribe, @kind, @skill, @prop. That's the whole API surface.

  • Two-axis architecture. Axis 1 (the kernel) is irreplaceable mechanism: ~2,600 LOC across 9 files, zero required dependencies. Axis 2 is replaceable vocabulary: config, logging, credentials, storage, REST/MCP/CLI transports — all just components. The kernel is small enough to read in one sitting.

  • Same @runnable → multiple transports. @api("rest", ...) exposes an HTTP endpoint, @api("mcp") exposes an MCP tool. Auth is enforced at the bus level, identical regardless of transport.

Documentation

The full guided tour is at https://bayeslearner.github.io/signalpy-kernel/:

  • Tutorials — first component → give-and-take → dynamic services → runnables → gateway → auth → building a provider
  • Concepts — architecture, reactivity by example, line-by-line annotated reactive engine, threading model, deployment scales
  • Patterns — reactive-intent recipes (batch, is_stale, cancel_on_supersede, cross-thread writes, mutate-in-place, first-run, cleanup), secret rotation, A/B testing, multi-tenant, hot code update, more
  • Reference — traits (L0–L3), all 13 decorators, contracts, kernel API

Project layout

src/signalpy/
├── kernel/                  Axis 1 — the irreplaceable core (~2,600 LOC, 9 files)
│   ├── reactive.py            Signal, Computed, Effect, batch
│   ├── component.py           13 decorators + metadata
│   ├── runtime.py             ReactiveRuntime: Signal-backed injection
│   ├── registry.py            ServiceRegistry: provide/require + ref counting
│   ├── bus.py                 Bus: invoke / publish / subscribe
│   ├── lifecycle_manager.py   Dependency-ordered activation, effect lifecycle
│   ├── traits.py              L0–L3 trait system
│   └── contracts.py           Protocol interfaces (IConfig, ILogger, IStorage, …)
│
├── providers/               Axis 2 — platform components (config, logging,
│                            credentials, storage, auth, tracing, gateway, …)
├── adapters/                Axis 2 — transport adapters (REST/FastAPI, MCP, CLI/Click)
├── examples/                Progressive examples 01–07
└── tests/                   ~290 tests

Constitution (the non-negotiable rules)

  1. Everything is a component. No privileged subsystems.
  2. Components give and take. No globals, singletons, or ambient state.
  3. The kernel has zero business logic.
  4. Transport is an adapter, never a core concern.
  5. Distribution can be transparent — the bus is designed for pluggable transports.
  6. Apps are deployment units, components are composition units.
  7. Lifecycle is explicit and managed.
  8. Every API is transport-agnostic.
  9. The kernel is small. Readable in one sitting.

Inspiration

  • Vue 3 / Preact Signals / SolidJS — the Signal/Computed/Effect reactive model
  • iPOPO — OSGi-style component lifecycle for Python
  • Dapr — building blocks as pluggable components
  • Engin / Uber Fx — give-and-take dependency injection

License

MIT — see LICENSE.

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

signalpy_kernel-0.2.0.tar.gz (302.5 kB view details)

Uploaded Source

Built Distribution

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

signalpy_kernel-0.2.0-py3-none-any.whl (165.5 kB view details)

Uploaded Python 3

File details

Details for the file signalpy_kernel-0.2.0.tar.gz.

File metadata

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

File hashes

Hashes for signalpy_kernel-0.2.0.tar.gz
Algorithm Hash digest
SHA256 42a109b7cad9374178e3b65fbeb5ed09fc18202e87c649caecdf26d20278b44b
MD5 2b1b655f6ddf78e6f6529d6bef435029
BLAKE2b-256 bf99389aa2732a40c7372ccc6be337aa9bad8b2e049a29e666dd29598ca53595

See more details on using hashes here.

Provenance

The following attestation bundles were made for signalpy_kernel-0.2.0.tar.gz:

Publisher: publish-pypi.yml on bayeslearner/signalpy-kernel

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

File details

Details for the file signalpy_kernel-0.2.0-py3-none-any.whl.

File metadata

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

File hashes

Hashes for signalpy_kernel-0.2.0-py3-none-any.whl
Algorithm Hash digest
SHA256 13bbaa9ee9af44490c9bf8158e8b688c9140d67eb259bd21ac8900f0a708d997
MD5 2e204e95c70012b58fc4fbb5849b8f76
BLAKE2b-256 08e33f10192b130f042251210ccdd84762e3b4d362965c0355789f02a6dbfaf3

See more details on using hashes here.

Provenance

The following attestation bundles were made for signalpy_kernel-0.2.0-py3-none-any.whl:

Publisher: publish-pypi.yml on bayeslearner/signalpy-kernel

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