Skip to main content

Opinionated framework for async web services

Project description

Pepelats

Opinionated framework for async web services in Python — compose on a fluent builder with DI, configuration, observability, and lifecycle built in.

Python Version Status License


Apps compose on WebHostBuilder or FastAPIHostBuilder: register services, mount routes or APIs, run background workers. Starlette is the HTTP layer; FastAPI is optional.

Features

  • Compose and run an async web service from a single builder
  • Inject services and configuration into handlers and constructors
  • Load typed settings from TOML and environment variables
  • Structured logging, tracing, and metrics
  • Run background work alongside the HTTP server
  • Optional FastAPI for REST APIs and OpenAPI (pepelats[fastapi])

Installation

uv add pepelats
uv add "pepelats[fastapi]"

Configuration

Pass a config_dir to the builder. Pepelats loads appsettings.toml from that directory (and merges appsettings.local.toml when present). Set the active environment with the ENVIRONMENT variable (defaults to default).

config/appsettings.toml:

[default]
environment = "local"

[default.service]
service_name = "my-service"
service_version = "1.0.0"

[default.logging]
log_level = "INFO"
sinks = ["console"]

[default.logging.console]
json_logs = false

[default.host]
bind = "127.0.0.1"
port = 8000

[default.observability]
otlp_endpoint = ""

[default.greeting]
punctuation = "!"
shout = false

service, logging, host, and observability are required for host bootstrap. Leave otlp_endpoint empty to run without OTLP export.

App-specific sections bind to Pydantic models. The section name defaults to the model name in snake_case (GreetingConfiggreeting). Register with add_configuration; services receive the config through constructor injection:

from pydantic import BaseModel

from pepelats.configuration import Configuration
from pepelats.dependency_injection import ServiceCollection


class GreetingConfig(BaseModel):
    punctuation: str
    shout: bool


class HealthService:
    def __init__(self, config: GreetingConfig) -> None:
        self._config = config

    def status(self) -> str:
        message = "ok"
        if self._config.shout:
            message = message.upper()
        return message + self._config.punctuation


def register(services: ServiceCollection, configuration: Configuration) -> None:
    services.add_configuration(GreetingConfig)
    services.add_singleton(HealthService)

Examples

Runnable full versions: tests/examples/di_showcase_generic.py (Starlette) and tests/examples/di_showcase.py (FastAPI).

Generic host

from pathlib import Path

from pydantic import BaseModel
from starlette.requests import Request
from starlette.responses import JSONResponse
from starlette.routing import Route

from pepelats.configuration import Configuration
from pepelats.dependency_injection import ServiceCollection
from pepelats.hosting import HostPipeline, WebHostBuilder, request_services
from pepelats.hosting.web_host import WebHost


class GreetingConfig(BaseModel):
    punctuation: str
    shout: bool


class HealthService:
    def __init__(self, config: GreetingConfig) -> None:
        self._config = config

    def status(self) -> str:
        message = "ok"
        if self._config.shout:
            message = message.upper()
        return message + self._config.punctuation


async def health(request: Request) -> JSONResponse:
    services = request_services(request)
    service = await services.get(HealthService)
    return JSONResponse({"status": service.status()})


def register(services: ServiceCollection, configuration: Configuration) -> None:
    services.add_configuration(GreetingConfig)
    services.add_singleton(HealthService)


def map_routes(pipeline: HostPipeline) -> None:
    pipeline.map(Route("/health", health))


def build_host(config_dir: Path) -> WebHost:
    return (
        WebHostBuilder.create(config_dir=config_dir)
        .configure_services(register)
        .configure_pipeline(map_routes)
        .build()
    )


if __name__ == "__main__":
    build_host(Path("config")).run()

FastAPI

from pathlib import Path

from fastapi import APIRouter, FastAPI
from pydantic import BaseModel

from pepelats.configuration import Configuration
from pepelats.dependency_injection import Inject, ServiceCollection
from pepelats.hosting.web_host import WebHost
from pepelats.integrations.fastapi import FastAPIHostBuilder, InjectRoute


class GreetingConfig(BaseModel):
    punctuation: str
    shout: bool


class HealthService:
    def __init__(self, config: GreetingConfig) -> None:
        self._config = config

    def status(self) -> str:
        message = "ok"
        if self._config.shout:
            message = message.upper()
        return message + self._config.punctuation


router = APIRouter(route_class=InjectRoute)


@router.get("/health")
async def health(service: Inject[HealthService]) -> dict[str, str]:
    return {"status": service.status()}


def register(services: ServiceCollection, configuration: Configuration) -> None:
    services.add_configuration(GreetingConfig)
    services.add_singleton(HealthService)


def configure_api(app: FastAPI) -> None:
    app.include_router(router)


def build_host(config_dir: Path) -> WebHost:
    return (
        FastAPIHostBuilder.create(config_dir=config_dir)
        .configure_services(register)
        .configure_api(configure_api)
        .build()
    )


if __name__ == "__main__":
    build_host(Path("config")).run()

Dependencies

Pepelats uses:

Optional:

  • FastAPI — API layer (pepelats[fastapi])

License

Apache-2.0 — 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

pepelats-0.1.0a1.tar.gz (1.7 MB view details)

Uploaded Source

Built Distribution

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

pepelats-0.1.0a1-py3-none-any.whl (40.7 kB view details)

Uploaded Python 3

File details

Details for the file pepelats-0.1.0a1.tar.gz.

File metadata

  • Download URL: pepelats-0.1.0a1.tar.gz
  • Upload date:
  • Size: 1.7 MB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: uv/0.11.21 {"installer":{"name":"uv","version":"0.11.21","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

File hashes

Hashes for pepelats-0.1.0a1.tar.gz
Algorithm Hash digest
SHA256 ac0c48aa93407bb2150cd37397ea97beaf6a95bdb6ef1232852d998451a705ea
MD5 e8dadd1ded555d7bd2c094b253e7ce95
BLAKE2b-256 fcda009759dd84d5bd3f2d78660d9dbf13c7e8a9bc0918d20f09e9370b143e37

See more details on using hashes here.

File details

Details for the file pepelats-0.1.0a1-py3-none-any.whl.

File metadata

  • Download URL: pepelats-0.1.0a1-py3-none-any.whl
  • Upload date:
  • Size: 40.7 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: uv/0.11.21 {"installer":{"name":"uv","version":"0.11.21","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

File hashes

Hashes for pepelats-0.1.0a1-py3-none-any.whl
Algorithm Hash digest
SHA256 da56ef1a174ff7785e51610429708df41c8a269ecedea14eab024b75e701460d
MD5 7baa4d3011c70b45ed377d25fa49562c
BLAKE2b-256 c55295d556b114e9cb74c08b965e762f8b0cc9d1d0f145d1771203062e409535

See more details on using hashes here.

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