Skip to main content

loopback-singleton

loopback-singleton is a lightweight Python package that gives multiple local processes access to a single shared object instance hosted in a background daemon on 127.0.0.1.

It is useful when you want one process-external object (cache, counter, coordinator, adapter, etc.) and you want all local workers to call into that object without standing up a full RPC system.

Current status (v0.2.4)

Current release: 0.2.4.

What works today

  • Local singleton daemon auto-start on first use.
  • Concurrent startup coordination with file locking to reduce duplicate daemons.
  • Authenticated handshake (shared token in runtime dir) between client and daemon.
  • Sequential method execution on the singleton object (single executor queue).
  • Idle TTL auto-shutdown for daemon cleanup.
  • Recovery from stale or corrupted runtime metadata.
  • Cross-platform runtime location strategy (Windows + POSIX fallback behavior).

Installation

pip install loopback-singleton

For local development:

pip install -e .[dev]

Quickstart

Create a module with a factory target (class or callable):

# mypkg/services.py
class Counter:
    def __init__(self):
        self.value = 0

    def inc(self) -> int:
        self.value += 1
        return self.value

    def ping(self) -> str:
        return "pong"

Use local_singleton from any process:

from loopback_singleton import local_singleton

svc = local_singleton(
    name="my-counter",
    factory="mypkg.services:Counter",
    idle_ttl=2.0,
    serializer="pickle",
)

with svc.proxy() as obj:
    print(obj.ping())
    print(obj.inc())

API overview

local_singleton(
    name: str,
    factory: str | callable | type,
    *,
    factory_args: tuple = (),
    factory_kwargs: dict | None = None,
    scope: str = "user",
    idle_ttl: float = 2.0,
    serializer: str = "pickle",
    connect_timeout: float = 0.5,
    start_timeout: float = 3.0,
)
  • name: singleton identity (shared runtime namespace).
  • factory: import string ("module:callable_or_class") or module-level importable callable/class object.
  • factory_args, factory_kwargs: constructor args used when creating singleton instance (factory_kwargs=None behaves as {}).
  • scope: currently only "user" is implemented.
  • idle_ttl: daemon stops after this many seconds with zero active connections.
  • serializer: currently only "pickle" is implemented.
  • connect_timeout, start_timeout: socket/startup tuning.

svc.proxy() returns a dynamic proxy where method calls are forwarded to the daemon.

Additional lifecycle APIs are available on LocalSingletonService:

svc.ensure_started()
info = svc.ping()
svc.shutdown()

Discover running singletons

Use list_local_singletons() to inspect singleton runtime directories without changing daemon state.

from loopback_singleton import list_local_singletons

for item in list_local_singletons():
    print(item.name, item.status, item.host, item.port, item.pid, item.serializer)

Notes:

  • Read-only API: it does not take startup locks, spawn daemons, or clean stale files.
  • Connection-related metadata comes from runtime.bin when available (host, port, pid, serializer, protocol fields).
  • Factory details are included by default (factory_import, factory_args, factory_kwargs), and can be skipped with include_factory=False.
  • Auth secrets are not returned (only the auth file path is exposed in the result object).
  • Directories that contain only auth.bin (without runtime/factory metadata) are ignored by default.
  • Optional probe=True performs best-effort connectivity checks against already-running daemons and enriches reachable / ping_info without auto-starting services.

How it works

  1. Client computes runtime paths for the singleton name.
  2. Client attempts connection using runtime metadata.
  3. If missing/failing, it takes a file lock, cleans stale metadata, and spawns daemon.
  4. Daemon binds ephemeral loopback TCP port, writes runtime metadata, and serves requests.
  5. Each CALL request is executed sequentially against one in-memory object instance.

Lifecycle and robustness scenarios

Scenario 1 — Pass a class directly

from loopback_singleton import local_singleton
from mypkg.services import CounterService

svc = local_singleton(
    "counter",
    factory=CounterService,
    factory_args=(10,),
    factory_kwargs={"step": 2},
)

with svc.proxy() as p:
    assert p.inc() == 12
    assert p.inc() == 14

Scenario 2 — Pass a factory function directly

from loopback_singleton import local_singleton
from mypkg.factories import make_cache

svc = local_singleton(
    "cache",
    factory=make_cache,
    factory_kwargs={"max_items": 1000, "ttl": 60},
)

with svc.proxy() as cache:
    cache.put("k", "v")
    assert cache.get("k") == "v"

Scenario 3 — Warm-up a configured daemon without proxy creation

svc = local_singleton("svc", factory=MyService, factory_args=(...), factory_kwargs={...})
svc.ensure_started()
info = svc.ping()

Scenario 4 — Non-importable factory gets a clear error

svc = local_singleton("x", factory=lambda: object())
# -> TypeError: Factory must be importable (module-level). Pass 'pkg.mod:callable' string instead.

Factory consistency across concurrent clients

For a given singleton name, the first daemon start wins. Subsequent clients must use the same normalized factory + args/kwargs. If they differ, the client fails fast with FactoryMismatchError.

Error model

Main exception classes exported by the package:

  • LoopbackSingletonError (base)
  • DaemonConnectionError
    • ConnectionFailedError
    • HandshakeError
  • ProtocolError (invalid or oversized transport frames/messages)
  • FactoryMismatchError (running daemon factory config differs from requested config)
  • RemoteError (remote traceback payload)

Security notes (important)

This MVP uses pickle for transport serialization. pickle is not safe for untrusted input and can execute arbitrary code.

Use this package only in trusted local environments for now.

Runtime files and cleanup

Runtime files are created under:

  • Windows: %LOCALAPPDATA%/loopback-singleton/<name>/
  • Linux/macOS: $XDG_RUNTIME_DIR/loopback-singleton/<name>/
  • POSIX fallback: ~/.cache/loopback-singleton/<name>/

If startup repeatedly fails due to stale metadata, stop clients and remove the directory for that singleton name.

Known limitations (MVP)

  • Callable/class factories must be importable at module level when passed as objects (lambdas/nested functions are rejected).
  • No identity transparency for proxies (isinstance(proxy, MyType) is not preserved).
  • No magic-method forwarding (__len__, operators, iteration, etc.).
  • Only scope="user" implemented.
  • Only serializer="pickle" implemented (msgpack placeholder exists but not implemented).
  • Transport is loopback TCP only.

Development

Run checks and tests:

ruff check .
pytest -q

Build package:

python -m build

Future work

Planned directions for post-MVP releases:

  • Safer serialization options

    • Implement msgpack serializer path and typed payload envelopes.
    • Add optional schema validation for RPC payloads.
  • Richer proxy semantics

    • Support selected dunder/magic methods.
    • Improve error transport with structured remote exception metadata.
  • Lifecycle and observability

    • Add daemon health/metrics endpoint(s) and lightweight tracing hooks.
    • Expose explicit client APIs for graceful shutdown and restart policies.
  • Scope and deployment flexibility

    • Add additional scope modes beyond per-user.
    • Evaluate optional Unix domain socket transport on POSIX.
  • Robustness and compatibility

    • Protocol version negotiation for rolling upgrades.
    • Expanded stress/regression suite for high-concurrency scenarios.
  • Security hardening

    • Optional mutual-auth improvements and stricter runtime file hardening.
    • Guidance and tooling for locked-down local deployments.

Contributions and issue reports are welcome at:

Metadata

Release files for loopback-singleton 0.2.4

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

Source distribution (sdist)

Source distribution for loopback-singleton 0.2.4
File Size Uploaded
loopback_singleton-0.2.4.tar.gz 33.3 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for loopback-singleton 0.2.4
File Interpreter ABI Platform
loopback_singleton-0.2.4-py3-none-any.whl Python 3 none any Details

Total release size: 53.7 kB

Release files / loopback_singleton-0.2.4.tar.gz

Download URL loopback_singleton-0.2.4.tar.gz
Size 33.3 kB
Tags Source
SHA-256 checksum
How to use checksums
89bcb4f46992a1e779668726fd44ac3ed62112920fab8250bc51e15a32a78017
BLAKE2b-256 checksum
How to use checksums
1029804618cd872b06c61fc9e33e029a3486f2df8bf9b60998e09d291bf472e9
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/6.1.0 CPython/3.13.7

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Mar 15, 2026.

Transparency log

Release files / loopback_singleton-0.2.4-py3-none-any.whl

Download URL loopback_singleton-0.2.4-py3-none-any.whl
Size 20.3 kB
Tags Python 3
SHA-256 checksum
How to use checksums
12c6b784edc1ca584a97ff71073664c519a993d59d7adedd7955940fe8d4b791
BLAKE2b-256 checksum
How to use checksums
efe0065ce1a21cd6e2543a02be208dafaa378f33b970cf5389b29c27735cc025
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/6.1.0 CPython/3.13.7

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Mar 15, 2026.

Transparency log

Release history Release notifications | RSS feed

This release

0.2.4 This release

2 release files

0.2.3

2 release files

0.2.2

2 release files

0.2.1

2 release files

0.1.1

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