Usage

svcs-pyramid uses Pyramid’s pyramid.registry.Registry to store its own svcs.Registry (yes, unfortunate name clash) and a tween that attaches a fresh svcs.Container to every request and closes it afterwards.

Installation

$ uv pip install svcs-pyramid

Note

svcs-pyramid used to be the svcs.pyramid module that shipped with svcs itself. If you’re migrating, the only change you have to make is the import:

import svcs           →  import svcs_pyramid
svcs.pyramid.init(…)  →  svcs_pyramid.init(…)

The names under which the registry and the container are stored are unchanged, so both packages are interchangeable.

Initialization

The most important API is svcs_pyramid.init() that takes an pyramid.config.Configurator and optionally the positions where to put its tween using the tween_under and tween_over arguments.

You can use svcs_pyramid.register_factory() and svcs_pyramid.register_value() that work like their svcs.Registry counterparts but take a pyramid.config.Configurator as the first option (or any other object that has a registry: dict field, really).

So you application factory is going to look something like this:

def make_app():
    ...

    with Configurator(settings=settings) as config:
        svcs_pyramid.init(config)
        svcs_pyramid.register_factory(config, Database, db_factory)

        ...

        return config.make_wsgi_app()

Service acquisition

You can use svcs_pyramid.svcs_from() to access a request-scoped svcs.Container from a request object:

from svcs_pyramid import svcs_from


def view(request):
    db = svcs_from(request).get(Database)

Or you can use svcs_pyramid.get() to access a service from the current request directly:

import svcs_pyramid


def view(request):
    db = svcs_pyramid.get(request, Database)

Thread locals

Despite being discouraged, you can use Pyramid’s thread locals to access the active container.

So this:

def view(request):
    registry = svcs_pyramid.get_registry()
    container = svcs_pyramid.svcs_from()

is equivalent to this:

def view(request):
    registry = svcs_pyramid.get_registry(request)
    container = svcs_pyramid.svcs_from(request)

Caution

These functions only work from within active Pyramid requests.

Health checks

You can use svcs_pyramid.get_pings() to get all registered health checks.

A health endpoint could look like this:

from __future__ import annotations

import json

from pyramid.request import Request
from pyramid.response import Response
from pyramid.view import view_config

import svcs_pyramid


@view_config(route_name="healthy")
def healthy_view(request: Request) -> Response:
    """
    Ping all external services.
    """
    ok: list[str] = []
    failing: dict[str, str] = {}
    status = 200

    for svc in svcs_pyramid.get_pings(request):
        try:
            svc.ping()
            ok.append(svc.name)
        except Exception as e:
            failing[svc.name] = repr(e)
            status = 500

    return Response(
        content_type="application/json",
        status=status,
        body=json.dumps({"ok": ok, "failing": failing}).encode("ascii"),
    )

Testing

Assuming you have an application factory your_app.make_app()[1] that initializes and configures svcs using svcs_pyramid.init(), you can use the following fixtures to get a WebTest application and its registry for overrides:

from your_app import make_app

import pytest
import svcs_pyramid
import webtest


@pytest.fixture
def app():
    app = make_app()

    with svcs_pyramid.get_registry(app):
        yield webtest.TestApp(app)


@pytest.fixture
def registry(app):
    return svcs_pyramid.get_registry(app)

Now you can write a test like this:

from sqlalchemy import Engine


def test_broken_database(app, registry):
    boom = Mock(spec_set=Engine)
    boom.execute.side_effect = RuntimeError("Boom!")

    registry.register_value(Engine, boom)  # ← override the database

    resp = app.get("/some-url")

    assert 500 == resp.status_code

Since init() takes a registry keyword argument, you can also go the other way around and pass a (potentially pre-configured) svcs.Registry into your application factory.

Cleanup

You can use svcs_pyramid.close_registry() to close the registry that is attached to the pyramid.registry.Registry of the config or app object that you pass as the only parameter.