Metadata-Version: 2.4
Name: scalo
Version: 2.32.7
Summary: Opinionated-for-scale integrated runtime of config, logging, secrets, cli and more for control-plane services.
Keywords: observability,structured-logging,configuration,dynaconf,prometheus,opentelemetry,metrics,tracing,secrets-management,vault,kafka,cel,asyncio,httpx,fastapi,kubernetes,docker,containerization
Author: HyperI Team
Author-email: HyperI Team <dev@hyperi.io>
License-Expression: Apache-2.0
License-File: LICENSE
License-File: NOTICE
Classifier: Development Status :: 5 - Production/Stable
Classifier: Intended Audience :: Developers
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3 :: Only
Classifier: Operating System :: OS Independent
Classifier: Topic :: Software Development :: Libraries :: Application Frameworks
Classifier: Topic :: System :: Monitoring
Classifier: Topic :: System :: Logging
Requires-Dist: dynaconf>=3.2.13
Requires-Dist: loguru>=0.7.3
Requires-Dist: python-dotenv>=1.0.0
Requires-Dist: pyyaml>=6.0.3
Requires-Dist: mergedeep>=1.3.4
Requires-Dist: tomli-w>=1.0.0
Requires-Dist: typer>=0.25.0
Requires-Dist: dulwich>=1.2.15
Requires-Dist: anyio>=4.14.2
Requires-Dist: asyncer>=0.0.17
Requires-Dist: detect-secrets>=1.5.0
Requires-Dist: phonenumbers>=9.0.0
Requires-Dist: python-stdnum>=2.2
Requires-Dist: regex>=2026.0.0
Requires-Dist: fastapi>=0.142.2 ; extra == 'api'
Requires-Dist: pydantic>=2.13.0 ; extra == 'api'
Requires-Dist: cryptography>=50.0.2 ; extra == 'api'
Requires-Dist: typer>=0.25.0 ; extra == 'cli'
Requires-Dist: jmespath>=1.1.0 ; extra == 'cli'
Requires-Dist: pydantic>=2.13.0 ; extra == 'deployment'
Requires-Dist: cryptography>=50.0.0 ; extra == 'dev'
Requires-Dist: pytest>=9.1.1 ; extra == 'dev'
Requires-Dist: pytest-cov>=7.0.0 ; extra == 'dev'
Requires-Dist: pytest-asyncio>=1.4.0 ; extra == 'dev'
Requires-Dist: pytest-httpx>=0.36.0 ; extra == 'dev'
Requires-Dist: httpx>=0.28.0 ; extra == 'dev'
Requires-Dist: stamina>=26.1.0 ; extra == 'dev'
Requires-Dist: boto3>=1.43.0 ; extra == 'dev'
Requires-Dist: aiobotocore>=3.6.0 ; extra == 'dev'
Requires-Dist: moto[secretsmanager]>=5.2.0 ; extra == 'dev'
Requires-Dist: pydantic>=2.13.0 ; extra == 'dev'
Requires-Dist: ruff>=0.16.0 ; extra == 'dev'
Requires-Dist: ty>=0.0.34 ; extra == 'dev'
Requires-Dist: mypy>=2.4.0 ; extra == 'dev'
Requires-Dist: bandit[toml]>=1.7.0 ; extra == 'dev'
Requires-Dist: pip-audit>=2.6.0 ; extra == 'dev'
Requires-Dist: vulture>=2.16 ; extra == 'dev'
Requires-Dist: mergedeep>=1.3.4 ; extra == 'dev'
Requires-Dist: pre-commit>=4.6.0 ; extra == 'dev'
Requires-Dist: tiktoken>=0.14.0 ; extra == 'dev'
Requires-Dist: faker>=40.0.0 ; extra == 'dev'
Requires-Dist: jsonschema>=4.26.0 ; extra == 'dev'
Requires-Dist: sphinx>=9.1.0,<10 ; extra == 'docs'
Requires-Dist: sphinx-rtd-theme>=3.0.0 ; extra == 'docs'
Requires-Dist: myst-parser>=5.0.0 ; extra == 'docs'
Requires-Dist: sphinx-autobuild>=2025.8.25 ; extra == 'docs'
Requires-Dist: common-expression-language>=0.10.0 ; extra == 'expression'
Requires-Dist: httpx>=0.28.0 ; extra == 'http'
Requires-Dist: stamina>=26.1.0 ; extra == 'http'
Requires-Dist: confluent-kafka>=2.14.0 ; extra == 'kafka'
Requires-Dist: genson>=1.3.0 ; extra == 'kafka'
Requires-Dist: scalo[api] ; extra == 'mcp'
Requires-Dist: mcp>=2.3.0 ; extra == 'mcp'
Requires-Dist: httpx2>=2.13.1 ; extra == 'mcp'
Requires-Dist: jsonschema>=4.26.0 ; extra == 'mcp'
Requires-Dist: prometheus-client>=0.25.0 ; extra == 'metrics'
Requires-Dist: psutil>=7.2.0 ; extra == 'metrics'
Requires-Dist: opentelemetry-api>=1.41.0 ; extra == 'metrics'
Requires-Dist: opentelemetry-sdk>=1.41.0 ; extra == 'metrics'
Requires-Dist: opentelemetry-exporter-otlp>=1.41.0 ; extra == 'metrics'
Requires-Dist: opentelemetry-exporter-prometheus>=0.62b0 ; extra == 'metrics'
Requires-Dist: opentelemetry-api>=1.41.0 ; extra == 'opentelemetry'
Requires-Dist: opentelemetry-sdk>=1.41.0 ; extra == 'opentelemetry'
Requires-Dist: opentelemetry-exporter-otlp>=1.41.0 ; extra == 'opentelemetry'
Requires-Dist: opentelemetry-exporter-prometheus>=0.62b0 ; extra == 'opentelemetry'
Requires-Dist: stamina>=26.1.0 ; extra == 'resilience'
Requires-Dist: httpx>=0.28.0 ; extra == 'secrets'
Requires-Dist: stamina>=26.1.0 ; extra == 'secrets'
Requires-Dist: boto3>=1.43.0 ; extra == 'secrets'
Requires-Dist: aiobotocore>=3.6.0 ; extra == 'secrets'
Requires-Dist: google-cloud-secret-manager>=2.28.0 ; extra == 'secrets'
Requires-Dist: azure-keyvault-secrets>=4.11.0 ; extra == 'secrets'
Requires-Dist: azure-identity>=1.25.1 ; extra == 'secrets'
Requires-Dist: boto3>=1.43.0 ; extra == 'secrets-aws'
Requires-Dist: aiobotocore>=3.6.0 ; extra == 'secrets-aws'
Requires-Dist: azure-keyvault-secrets>=4.11.0 ; extra == 'secrets-azure'
Requires-Dist: azure-identity>=1.25.1 ; extra == 'secrets-azure'
Requires-Dist: google-cloud-secret-manager>=2.28.0 ; extra == 'secrets-gcp'
Requires-Dist: httpx>=0.28.0 ; extra == 'secrets-kubernetes'
Requires-Dist: stamina>=26.1.0 ; extra == 'secrets-kubernetes'
Requires-Dist: httpx>=0.28.0 ; extra == 'secrets-vault'
Maintainer: HyperI Team
Maintainer-email: HyperI Team <dev@hyperi.io>
Requires-Python: >=3.14
Project-URL: Homepage, https://github.com/hyperi-io/scalo-py
Project-URL: Documentation, https://github.com/hyperi-io/scalo-py/tree/main/docs
Project-URL: Repository, https://github.com/hyperi-io/scalo-py
Project-URL: Bug Reports, https://github.com/hyperi-io/scalo-py/issues
Project-URL: Changelog, https://github.com/hyperi-io/scalo-py/blob/main/CHANGELOG.md
Provides-Extra: api
Provides-Extra: cli
Provides-Extra: deployment
Provides-Extra: dev
Provides-Extra: docs
Provides-Extra: enhanced
Provides-Extra: expression
Provides-Extra: http
Provides-Extra: kafka
Provides-Extra: mcp
Provides-Extra: metrics
Provides-Extra: opentelemetry
Provides-Extra: resilience
Provides-Extra: secrets
Provides-Extra: secrets-aws
Provides-Extra: secrets-azure
Provides-Extra: secrets-gcp
Provides-Extra: secrets-kubernetes
Provides-Extra: secrets-vault
Provides-Extra: version-check
Description-Content-Type: text/markdown

# scalo

The runtime layer for Python control-plane services: config, logging, metrics, health probes, secrets and a service CLI, already wired together. Subclass one class and your service reads its config, logs in the right format for where it runs, and serves its health probes.

It is built for APIs, orchestrators, CLI tools and integration glue. For the hot path there is a Rust sister library, [scalo-rs](https://github.com/hyperi-io/scalo-rs), with the same conventions and a different API.

## What it gives you

- A seven-layer config cascade, from CLI flags and env vars down to YAML and defaults, that reads the same in a laptop shell and a Kubernetes pod.
- Structured logs, JSON in a container and readable text at a terminal. Credentials, tokens and keys are masked, and so are cards, IBANs, emails and phone numbers, and Australian ABN, ACN, TFN and Medicare numbers where a keyword names them. TRACE records skip the scrubber by default.
- `ServiceApp`: subclass it and get `run`, `version`, `config-check` and `generate-artefacts`, with config and logging started for you, and metrics too once `scalo[metrics]` is installed.
- `/livez` and `/readyz` on an observability port kept apart from your application's own.
- With the `metrics` extra, Prometheus and OpenTelemetry metrics, process and container gauges included, scraped from `/metrics` on the same port. Without a metrics manager that path answers 404.
- Secrets from OpenBao or Vault, AWS, GCP, Azure, and Kubernetes, one extra per backend.
- A deployment contract: return one from `deployment_contract()` and `generate-artefacts` writes your runtime Dockerfile and the contract the scalo-service Helm chart renders from.
- Kafka, CEL expressions and an HTTP client with jittered retry, each behind its own extra.

## Example

```console
uv add scalo
```

```python
from scalo.cli import ServiceApp, VersionInfo
from scalo.logger import logger


class Greeter(ServiceApp):
    name = "greeter"
    env_prefix = "GREETER"

    def version_info(self) -> VersionInfo:
        return VersionInfo(self.name, "1.0.0")

    def run_service(self, config) -> None:
        logger.info("hello", target=config.get("target", "world"))


if __name__ == "__main__":
    Greeter().cli()
```

`GREETER_TARGET=team python greeter.py run` logs `hello` with `target=team`, and `python greeter.py config-check` validates the config without starting anything.

## Extras

| Extra | Adds |
|---|---|
| `api` | FastAPI conventions: operation ids, ETag and If-Match, problem+json errors, pagination |
| `cli` | A whole CLI generated from an OpenAPI spec |
| `mcp` | MCP tools generated from a `scalo.api` app's spec, served on the same app |
| `http` | An httpx client with jittered retry |
| `metrics` | Prometheus and OTLP export, process and container gauges |
| `opentelemetry` | The OpenTelemetry half of `metrics` on its own |
| `expression` | CEL expressions |
| `kafka` | Produce, consume and schema inference |
| `deployment` | The deployment contract and its Dockerfile and manifest generators |
| `secrets-vault`, `secrets-aws`, `secrets-gcp`, `secrets-azure`, `secrets-kubernetes` | One secrets backend each |
| `secrets` | Every secrets backend |

Pick the secrets backend you use rather than `secrets`, since each backend brings its own cloud SDK.

Docs and source are at <https://github.com/hyperi-io/scalo-py>. Requires Python 3.14 or later. Licensed Apache-2.0: <https://github.com/hyperi-io/scalo-py/blob/main/LICENSE>.
