Skip to main content

Mark config values with typed anchors, resolve them all in one call. Pluggable backends for AWS KMS, SSM Parameter Store, Secrets Manager, and environment variables — with batching, defaults, and collected errors built in.

Project description

clever_config

Stop using external calls (for instance - boto3) and os.getenv() across your codebase. Mark your config values once, resolve them all in one call.

config = {
    "database": {
        "host":     "prod-db.internal",                                        # plain values stay as-is
        "password": {"type": "SSM",  "value": "/myapp/prod/db-password"},
        "api_key":  {"type": "ENV",  "value": "STRIPE_KEY"},
    },
    "encryption_key": {"type": "KMS", "value": "<base64-kms-ciphertext>"},
}

errors = dict_traversal(config, [SSMAction(), EnvLoaderAction(), KMSAction()])

# config["database"]["password"]  → fetched from SSM Parameter Store
# config["database"]["api_key"]   → read from the environment
# config["encryption_key"]        → decrypted by KMS

Anchors are resolved in place with one function call.


The problem it solves

A typical service ends up with code like this scattered across a dozen files:

db_password = boto3.client("ssm").get_parameter(Name="/prod/db-pass", WithDecryption=True)["Parameter"]["Value"]
api_key = os.environ["STRIPE_KEY"]
secret = json.loads(boto3.client("secretsmanager").get_secret_value(SecretId="myapp/stripe")["SecretString"])

This is tedious to write, hard to test, and makes it unclear at a glance what your service actually needs at startup.

clever_config flips the model: declare what each value is and where it lives, then fetch everything at once — with batching, default fallbacks, and collected errors built in.


Install

pip install clever_config
# or with Poetry:
poetry add clever_config

Requires Python 3.12+. Runtime dependencies: boto3, requests.


How it works

An anchor is a small dict that marks a value to be resolved:

{"type": "SSM", "value": "/my/app/secret"}

Scatter anchors anywhere in your config — at any depth, inside nested dicts or lists. Call dict_traversal() with the actions that match your anchor types, and each anchor is replaced with its real value in place.

You can also add a "default" key as a fallback for when a value is missing:

{"type": "ENV", "value": "DEBUG_MODE", "default": "false"}

Available actions

Action Anchor type What it fetches
EnvLoaderAction ENV An environment variable
KMSAction KMS KMS-decrypted value (base64 ciphertext → plaintext)
SSMAction SSM SSM Parameter Store — full path
PrefixSSMAction APP-SSM SSM with an auto-prepended path prefix
ConventionalSSMAction DEFAULT-SSM SSM with /profile/service/name convention
ConventionalAppSSMAction CNV-SSM SSM with /app/profile/service/name convention
SecretManagerAction SECRET-MANAGER An AWS Secrets Manager secret
SecretManagerKeysAction SECRET-MANAGER-KEY A specific key from a JSON secret (my-secret.key)

All imports are available from clever_config.actions.


Usage examples

Mixing multiple sources

from clever_config.dict_traversal import dict_traversal
from clever_config.actions import SSMAction, SecretManagerAction, EnvLoaderAction

config = {
    "app": {
        "debug":       {"type": "ENV",            "value": "APP_DEBUG",          "default": "false"},
        "db_password": {"type": "SSM",            "value": "/myapp/prod/db-pass"},
        "stripe":      {"type": "SECRET-MANAGER", "value": "myapp/stripe"},
    }
}

errors = dict_traversal(config, [SSMAction(), SecretManagerAction(), EnvLoaderAction()])
if errors:
    raise RuntimeError(f"Config errors:\n" + "\n".join(errors))

SSM with a shared path prefix

from clever_config.actions import PrefixSSMAction

# No need to repeat the prefix in every anchor
action = PrefixSSMAction(prefix="myapp/prod")

config = {
    "db_host":     {"type": "APP-SSM", "value": "db-host"},      # resolves to /myapp/prod/db-host
    "db_password": {"type": "APP-SSM", "value": "db-password"},  # resolves to /myapp/prod/db-password
}
errors = dict_traversal(config, [action])

Extracting individual keys from a JSON secret

from clever_config.actions import SecretManagerKeysAction

# The secret "myapp/db-creds" stores: {"user": "admin", "pass": "s3cr3t"}
config = {
    "db_user": {"type": "SECRET-MANAGER-KEY", "value": "myapp/db-creds.user"},
    "db_pass": {"type": "SECRET-MANAGER-KEY", "value": "myapp/db-creds.pass"},
}
errors = dict_traversal(config, [SecretManagerKeysAction()])

Lambda — using the Parameters & Secrets Extension

Skip the boto3 round-trip inside Lambda by using the sidecar HTTP client instead:

from clever_config.actions import SSMAction

action = SSMAction(use_ssm_lambda_extension=True)

This uses the AWS Parameters and Secrets Lambda Extension and reads from localhost — faster cold starts, no extra IAM calls.

Deserializing JSON values

If a parameter value is a JSON string, pass deserialize_values=True to parse it automatically into a dict or list:

SSMAction(deserialize_values=True)
SecretManagerAction(deserialize_values=True)

Key behaviors

Errors are collected, not thrown. dict_traversal returns a list of error strings. Each failed lookup is appended to the list, so you see all problems at once rather than failing on the first missing value. Fatal misconfigurations (e.g. two actions with the same type in one call) raise immediately.

AWS reads are batched. SSM and Secrets Manager actions collect all required paths during traversal, then fetch them in chunks of 10 — minimizing the number of AWS round-trips regardless of how many anchors you have.

Any nesting is supported. Anchors inside lists, deeply nested dicts, or mixed structures are all found and resolved.

Plain values are untouched. Only dicts shaped exactly like an anchor ({"type": "...", "value": "..."}) are resolved. Everything else passes through unchanged.


Development

make test           # run unit tests
make coverage       # tests + coverage report
make lint           # mypy type check
make check-format   # formatting check
make build          # build the package

Releasing to PyPI

Publishing is automated on tag push via .github/workflows/publish-pypi.yml.

  1. Bump version in pyproject.toml, commit, and merge to your default branch.
  2. Tag and push: git tag vX.Y.Z && git push origin vX.Y.Z
  3. Configure PyPI Trusted Publishing for this repository (setup guide) with environment name pypi.

The workflow builds with python -m build (PEP 517 / poetry-core). To use an API token instead of OIDC, see the comment at the top of the workflow file.


License

MIT · github.com/osipov-andrey/clever_config

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

clever_config-3.2.4.tar.gz (13.7 kB view details)

Uploaded Source

Built Distribution

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

clever_config-3.2.4-py3-none-any.whl (13.6 kB view details)

Uploaded Python 3

File details

Details for the file clever_config-3.2.4.tar.gz.

File metadata

  • Download URL: clever_config-3.2.4.tar.gz
  • Upload date:
  • Size: 13.7 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for clever_config-3.2.4.tar.gz
Algorithm Hash digest
SHA256 8a0b67757fb81a21b97546eb6b065985d622500b410ac008c5c17b21b9da2b54
MD5 43f65dfbf05d0e842fe51f14535d6957
BLAKE2b-256 c2b1006a5a0c0e81ddb2ff7eb64b984ee0709d8d5fee5ca0e292faea5a502565

See more details on using hashes here.

File details

Details for the file clever_config-3.2.4-py3-none-any.whl.

File metadata

  • Download URL: clever_config-3.2.4-py3-none-any.whl
  • Upload date:
  • Size: 13.6 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for clever_config-3.2.4-py3-none-any.whl
Algorithm Hash digest
SHA256 655a6d6418a12ce9404ffa92bacba8989a1eff009df3fe14399313f9791f929d
MD5 eb9e035b58886869e4f43447841e251e
BLAKE2b-256 4e5d42f879282f75d9909099f3712ee48819b8d11b15a0d295b8406ee82d6d09

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