Skip to main content

pympacds

Python Multi Process Async-based Cooperative Discoverable Services Framework

A lightweight, D-Bus-native application framework for building modular, multi-process, asyncio-based daemons on Linux — particularly suited for embedded and IoT systems.

Overview

pympacds provides a common foundation for building systems composed of multiple cooperating daemons that communicate via D-Bus. Each daemon runs as an independent process (managed by systemd as a service), uses Python's asyncio for internal concurrency, and automatically discovers peer services on the D-Bus system bus.

The framework is designed for any domain — IoT gateways, industrial control, home automation, automotive, robotics, or any Linux-based distributed system.

Architecture

┌─────────────────────────────────────────────────────────────────┐
│                    D-Bus System Bus                             │
│  ┌────────────┐  ┌────────────┐  ┌────────────┐  ┌────────────┐ │
│  │ Service A  │  │ Service B  │  │ Service C  │  │ Service D  │ │
│  │ (sensor)   │  │ (storage)  │  │ (comm)     │  │ (ui)       │ │
│  │ ProcessBase│  │ ProcessBase│  │ ProcessBase│  │ ProcessBase│ │
│  │ DBusManager│  │ DBusManager│  │ DBusManager│  │ DBusManager│ │
│  │ task1      │  │ task1      │  │ task1      │  │ task1      │ │
│  │ task2      │  │ task2      │  │            │  │            │ │
│  └────────────┘  └────────────┘  └────────────┘  └────────────┘ │
│       │                 │              │               │        │
│  discover ←────────→ discover ←────→ discover ←───→ discover    │
│  (auto peer detection via NameOwnerChanged)                     │
└─────────────────────────────────────────────────────────────────┘
                              │
                    ┌─────────┴───────────┐
                    │    systemd (PID 1)  │
                    │  service-a.service  │
                    │  service-b.service  │
                    │  service-c.service  │
                    └─────────────────────┘

Features

pympacds provides:

  • Process lifecycle — ProcessBase with setup/init/main/close, structured logging, signal handling, and named task management.
  • D-Bus transport — DBusManager over dbus-fast, configurable namespace prefix, and graceful disconnect.
  • Service discovery — automatic peer detection and dynamic subscribe/unsubscribe between services.
  • Interface contracts — validated D-Bus interface definitions, with built-in Health, Lifecycle, Config, and Metrics contracts.
  • Middleware — optional lifecycle extensions (HTTP config provisioning, signal-action rules).

See Features and Philosophy for the full details.

Example: Minimal Service

import asyncio
import time
from pympacds.process import ProcessBase
from pympacds.dbus import DBusManager
from pympacds.contracts import ServiceContract, dbus_method, dbus_signal


class MyContract(ServiceContract):
    def __init__(self, ifname, base):
        super().__init__(ifname, base)
        self._require("dbus_my_method")

    @dbus_method()
    def my_method(self, arg: "s") -> "s":
        return self.base.dbus_my_method(arg)

    @dbus_signal()
    def heartbeat(self, uptime: "i") -> "i":
        return [uptime]


class MyService(ProcessBase):
    def __init__(self):
        super().__init__(
            name="myservice",
            version="1.0.0",
            description="A minimal pympacds service",
            bus_prefix="com.example.mysystem",
        )

    async def start_dbus(self):
        self.bus = DBusManager(
            logger=self.logger,
            busname="myservice",
            bus_prefix=self.bus_prefix,
        )
        self.iface = MyContract("com.example.MyInterface", self)
        self.bus.add_interface("MyService", self.iface)
        await self.bus.start()
        return True

    def update_tasks(self):
        if "periodic" not in self.tasklist:
            self.tasklist["periodic"] = asyncio.create_task(self._periodic(), name="periodic")

    def dbus_my_method(self, arg: str) -> str:
        return f"Got: {arg}"

    async def _periodic(self):
        try:
            while not self.exitevent.is_set():
                await self.do_waitexit(timeout=5)
                self.iface.heartbeat(int(time.time()))
        except asyncio.CancelledError:
            pass


MyService().start()

Installation

Using PIP

pip install pympacds

For minimal embedded deployments without the optional Cython extension:

SKIP_CYTHON=1 pip install --no-binary dbus-fast pympacds

Note: using pip doesn't install the system configurations files (it requires admin privileges to save D-Bus configurations). In order to work run sudo pympacds-admin setup before running services.

Using PPA

Debian packages are provided through launchpad ppa (dargor0/mpacds):

sudo add-apt-repository ppa:dargor0/mpacds
sudo apt update
sudo apt install python3-pympacds

Packages are installed system wide with all configurations and resource files (no need to do a setup step).

Repository has been tested on Ubuntu (plucky, resolute, stonking) and Debian (stable, testing, unstable).

Documentation

Detailed documentation lives in the doc/ directory:

Key Concepts

Concept Description
Service A single-purpose daemon that subclasses ProcessBase and exports a D-Bus interface
Bus D-Bus system bus used as the IPC backbone between services
Discovery Automatic peer detection via bus name conventions and NameOwnerChanged signals
Interface Contract A standardized D-Bus interface template that enforces required callbacks at construction time
Task An asyncio coroutine managed inside a service's event loop
Lifecycle setup → init_loop → main_loop → close_loop pattern inherited from ProcessBase

License

MIT

Metadata

Release files for pympacds 0.3.1

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for pympacds 0.3.1
File Size Uploaded
pympacds-0.3.1.tar.gz 83.3 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for pympacds 0.3.1
File Interpreter ABI Platform
pympacds-0.3.1-py3-none-any.whl Python 3 none any Details

Total release size: 133.4 kB

Release files / pympacds-0.3.1.tar.gz

Download URL pympacds-0.3.1.tar.gz
Size 83.3 kB
Tags Source
SHA-256 checksum
How to use checksums
6cb03f8879cc20e583debd836eb4301cee12bfe0a26ffb38deac291a8ac52aa6
BLAKE2b-256 checksum
How to use checksums
d6a7ab1361cb1b8e964f85b2c936be4dc9fae78d1bfa47aa27064424682bfa08
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.14.4

Release files / pympacds-0.3.1-py3-none-any.whl

Download URL pympacds-0.3.1-py3-none-any.whl
Size 50.1 kB
Tags Python 3
SHA-256 checksum
How to use checksums
bd79d9c48edc6798afe613a85ed686823a9fea11a1a12f92db2240815634d514
BLAKE2b-256 checksum
How to use checksums
da751f802dc7396956c99be0cb063ce3b18ee64133cda33b64769bad01d8803b
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.14.4

Release history Release notifications | RSS feed

0.3.2

2 release files

This release

0.3.1 This release

2 release files

0.3.0

2 release files

0.2.1

2 release files

0.2.0

2 release files

0.1.0

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page