scalo
| Package | |
| Meta |
An integrated runtime for control-plane services. Attach and 'enterprise-up' your application.
Key features
- configuration cascade for local test through at scale k8s deployments (env, config, cli)
- automatically configured and integrated logger (line and json) for enterprise deployments
- auto enabled Otel and Prometheus metrics in expected forms and buckets for cloud and k8s deployments
- secrets management integration
- authentication and secrets integration for most common deployments and services
- deployment contracts, your code automatically generates docker, and k8s artefacts for deployment consumption
Install
uv add scalo # core
uv add "scalo[http,metrics,kafka]" # common extras
uv add "scalo[http,metrics,expression,kafka,opentelemetry,secrets,deployment]"
scalo on PyPI, scalo for imports. Extras and their install sizes are in
docs/EXTRAS-FLAGS.md -- secrets-aws alone is ~100 MB, so
pick the backend you use rather than secrets.
What it is for
Control-plane APIs, UI backends, orchestrators, CLI tools, integration glue, batch workloads, configuration management.
Not the hot path. Processing millions of messages a second belongs in Rust -- scalo-rs is the sister library for that. scalo-py is fast enough for control plane and integration; scalo-rs is fast enough for the hot path. So scalo-py leans to stability, expressiveness and integration: readable abstractions over inlined ones, heavier deps where they earn their keep, no agonising over async dispatch overhead.
Same conventions, DIFFERENT API, separate repo. Never assume parity.
This module exists because of this -- but for the backend: https://www.youtube.com/watch?v=xE9W9Ghe4Jk
What is in the box
Core, always installed:
| Module | What it does | Deps |
|---|---|---|
logger |
Structured JSON logging, PII masked, secrets filtered, container-aware | loguru |
config |
7-layer cascade (CLI -> ENV -> .env -> YAML -> defaults) | dynaconf, pyyaml, python-dotenv, mergedeep, tomli-w, dulwich |
runtime |
Detects K8s / Docker / local, resolves config and data paths | stdlib only |
cli |
ServiceApp -- subclass for run / version / config-check |
typer |
version-check |
Optional startup check for a newer release | httpx (lazy) |
By extra: http (retry with jitter), metrics (Prometheus + process and container
gauges), expression (CEL), kafka (produce, consume, schema inference),
opentelemetry (OTLP traces and metrics), secrets (OpenBao/Vault, AWS, GCP,
Azure), deployment (Dockerfile, Helm, Argo, compose generators).
Wired together already, which is the part you would otherwise build: config,
logging and metrics are global singletons with no init dance; the cascade feeds the
CLI so run, version and config-check work without you parsing an argument;
the metrics and health wiring feeds the Kubernetes probes; the deployment contract
writes your chart, Dockerfile and Argo manifests from the config the app already
declares.
Documentation
docs/README.md is the index. The ones you want first:
| Topic | Doc |
|---|---|
| Config cascade | core-pillars/CONFIG.md |
| Logging and PII scrubbing | core-pillars/LOGGING.md |
| Metrics | core-pillars/METRICS.md |
| Health, and the observability port | core-pillars/HEALTH.md |
ServiceApp and the CLI |
api/CLI.md |
| Secrets backends | api/SECRETS.md |
| Kafka | transport/KAFKA.md |
| Path resolution | runtime/RUNTIME-CONTEXT.md |
| Deployment contract | deployment/CONTRACT.md |
| What wires itself | AUTO-WIRING.md |
| Layering and the module graph | architecture.md |
Two things worth reading before you deploy: /livez and /readyz are the whole
health surface and there are no aliases, and the observability port is separate
from your application's on purpose -- exposing user traffic must never expose the
operator surface. Both are in HEALTH.md.
Development
make quality # lint, type-check, security audit
make test # run test suite
make build # build wheel
License
Apache-2.0. Third-party attributions are recorded in NOTICE.
Related
- scalo-rs -- sister library for Rust services. Same opinions, same patterns, native Rust performance for hot-path workloads.
- Migrating from hyperi-pylib --
scalois the renamed, Apache-2.0 continuation ofhyperi-pylib; this guide covers the mechanical changes.
Context
What this is
HyperI's shared Python library -- config cascade, logging, metrics, health,
secrets, Kafka and the deployment-contract generators -- published as scalo on
PyPI under Apache-2.0.
It is scalo-rs's SISTER, not its twin: same conventions, DIFFERENT API, separate
repo. Never assume parity. The divergence is recorded rather than theoretical --
src/scalo/data/masking-patterns.yaml names the sensitive fields each side knows
and the other does not, and the two match differently, so the corpus holds only
cases inside the overlap.
Where things live
| Path | What it holds |
|---|---|
src/scalo/<module>/ |
One directory per module: config, logger, metrics, health, http, kafka, secrets, deployment, cli |
src/scalo/data/ |
Vendored corpora and rule files, landed by tools/vendor_patterns.sh |
templates/helm/scalo-app/ |
The chart the deployment generators render against |
tests/{unit,integration,e2e,smoke}/ |
Split by what each needs in order to run |
docs/architecture.md |
Layering, the module dependency graph, and what scalo-rs has that this does not |
Commands that prove a change
make quality # lint, type-check, security audit
make test # the suite
make build # the wheel
addopts deselects no markers, so make test runs integration and e2e as well,
and on a developer box that is not green. This run was 9 failed, 2552 passed, 31
skipped in 7m01s -- 6 GCP secrets tests wanting gcloud auth application-default login, 2 CLI tests binding a fixed 0.0.0.0:9090, and one
Dockerfile build. Read the per-job CI result, never a local summary. Coverage is
gated in .hyperi-ci.yaml.
What tends to bite
| Don't | Do | Why |
|---|---|---|
| Read a skip as a pass | Make a guard fail when what it guards is absent | Both parity modules read fixtures from a scalo-spec checkout that never existed, so every case skipped everywhere including CI -- and the canary was itself skipif(not path.exists()), its skip condition being its own assertion negated. Tightening the import guards then caught test_import_http importing create_client, which does not exist (2ca8329) |
| Copy an API name or an env var out of the docs | Check the control_var call sites and the module |
The docs named for_pylib_extras and for_rustlib_features, really for_scalo_extras and for_scalo_features, plus eight HYPERI_* variables removed with no fallback (da005a0) |
| Assume a config key you added is read | Follow it to its reader | enable_sighup was honoured only when poll_interval > 0, so "reload on SIGHUP, never poll" registered no handler. A duplicate status key in the OTel label map exported every HTTP metric as task.status (6a2b21b) |
Where this sits
Generated from dfe-infra/suite.yaml via dfe-stack suite. Nothing in the suite
feeds this repo -- it declares no inbound edges.
| Repo | Kind | Mechanism, outbound |
|---|---|---|
| dfe-engine | python-dep, potential |
Declares scalo by range in pyproject.toml, no upper bound |
| dfe-engine | contract-guard, lockstep |
It overrides our runtime base image with its own literal, and its own test fails when its committed Dockerfile stops matching. The validator is ours, scalo.deployment.validate_dockerfile |
| culvert | python-dep, potential |
Declares scalo by range. A second range, for the deployment-contract generators, is dev-only |
| vector-vrl | python-dep, potential |
Build system only, not the published wheel |
Release files for scalo 2.30.1
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| scalo-2.30.1.tar.gz | 336.4 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| scalo-2.30.1-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 745.8 kB
Release files / scalo-2.30.1.tar.gz
| Download URL | scalo-2.30.1.tar.gz |
|---|---|
| Size | 336.4 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
47db6b4434804b55d700d16f3eb46615d23dc2dec80c9b1cadffb0ac3fcfe6a0
|
|
BLAKE2b-256 checksum How to use checksums |
3d07a3f743885249b12f66a1f05c48bdd5be360220f12e5afe5362e81561afa3
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
uv/0.12.18 {"installer":{"name":"uv","version":"0.12.18","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}
|
Release files / scalo-2.30.1-py3-none-any.whl
| Download URL | scalo-2.30.1-py3-none-any.whl |
|---|---|
| Size | 409.4 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
770576c95e8782e22156cb33bd33d5c9b4d26576cb9cec7038471bdb4102d3de
|
|
BLAKE2b-256 checksum How to use checksums |
6bf0876785a3782ccc0f93639478432e38db893b85f02b23539cdc27695a0d16
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
uv/0.12.18 {"installer":{"name":"uv","version":"0.12.18","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}
|