A tool for combining complex configurations in TOML format.
Project description
Toml-combine
toml-combine is a Python lib and CLI-tool that reads a TOML configuration file
defining a default configuration alongside with overrides, and merges everything
following rules you define to get final configurations. Let's say: you have multiple
services, and environments, and you want to describe them all without repeating the
parts that are common to everyone.
Concepts
The config file
The configuration file is (usually) a TOML file. Here's a small example:
[dimensions]
environment = ["production", "staging"]
[default]
name = "my-service"
registry = "gcr.io/my-project/"
container.image_name = "my-image"
container.port = 8080
[[override]]
when.environment = "production"
service_account = "my-production-service-account"
[[override]]
when.environment = "staging"
service_account = "my-staging-service-account"
Dimensions
Consider all the configurations you want to generate. Each one differs from the others.
Dimensions lets you describe the main "thing" that makes the outputs differents, e.g.:
environment might be staging or production, region might be eu or us, and
service might be frontend or backend. Some combinations of dimensions might not
exists, for example, maybe there's no staging in eu.
Default
The common configuration to start from, before we start overlaying overrides on top.
Overrides
Each override defines a set of condition where it applies (when.<dimension> = "<dimension_value>") and a set of overridden key/values.
[[override]]
# Keys starting with `when.` are "conditions"
when.environment = "staging"
when.region = "us"
# Other keys in an override are "overridden keys" / "overridden values"
service_account = "my-us-staging-service-account"
If you run toml-combine with a given mapping that selects multiple overrides, they
will be checked for compatibility with one another, and an error will be raised if
they're not compatible.
Compatibility rules:
-
If the two overrides don't share any overridden key, then they're always compatible.
Example (click to expand)
[dimensions] environment = ["staging"] region = ["eu"] [[override]] when.environment = "staging" service_account = "my-staging-service-account" [[override]] when.region = "eu" env.CURRENCY = "EUR"
-
If an override defines a set of conditions (say
env=prod) and the other one defines strictly more conditions (sayenv=prod, region=eu, in other words, it defines all the conditions of the first override and then some more), then they're compatible. Also, in that case, the override with more conditions will have precedence.Example
[dimensions] environment = ["staging"] region = ["eu"] [[override]] when.environment = "staging" service_account = "my-staging-service-account" [[override]] when.environment = "staging" when.region = "eu" service_account = "my-staging-eu-service-account"
-
If they both define a dimension that the other one doesn't, they're incompatible.
Example (click to expand)
Incompatible overrides: neither is a subset of the other one and they both define a value for
service_account:[dimensions] environment = ["staging"] region = ["eu"] [default] service_account = "my-service-account" [[override]] when.environment = "staging" service_account = "my-staging-service-account" [[override]] when.region = "eu" service_account = "my-eu-service-account"
$ toml-combine config.toml --environment=staging --region=eu Error: Incompatible overrides `{'region': ['eu']}` and `{'environment': ['staging']}`: When they're both applicable, overrides defining a common overridden key (foo) must be a subset of one another
[!NOTE] It's ok to have incompatible overrides in your config as long as you don't run
toml-combinewith a mapping that would select both of them. In the example above, if you runtoml-combine --environment=staging --region=eu, the error will be triggered, but you can runtoml-combine --environment=staging.
[!NOTE] Instead of defining a single value for the override dimensions, you can define a list. This is a shortcut to duplicating the override with each individual value:
[[override]] when.environment = ["staging", "prod"] service_account = "my-service-account"
The configuration itself
Under the layer of dimensions/default/override/mapping system, what you actually
define in the configuration is completely up to you. That said, only nested
"dictionnaries"/"objects"/"tables"/"mapping" (those are all the same things in
Python/JS/Toml lingo) will be merged between the default and the applicable overrides,
while arrays will just replace one another. See Arrays below.
Arrays
Let's look at an example:
[dimensions]
environment = ["production", "staging"]
[default]
fruits = [{name="apple", color="red"}]
[[override]]
when.environment = "staging"
fruits = [{name="orange", color="orange"}]
In this example, with {"environment": "staging"}, fruits is
[{name="orange", color="orange"}] and not
[{name="apple", color="red"}, {name="orange", color="orange"}].
The only way to get multiple values to be merged is if they are dicts: you'll need
to chose an element to become the key:
[dimensions]
environment = ["production", "staging"]
[default]
fruits.apple.color = "red"
[[override]]
when.environment = "staging"
fruits.orange.color = "orange"
In this example, on staging, fruits is {apple={color="red"}, orange={color="orange"}}.
This example is simple because name is a natural choice for the key. In some cases,
the choice is less natural, but you can always decide to name the elements of your
list and use that name as a key. Also, yes, you'll loose ordering.
Mapping
When you call the tool either with the CLI or the lib (see both below), you will have to provide a mapping of the desired dimentions. These values will be compared to overrides to apply overrides when relevant. It's ok to omit some dimensions, corresponding overrides won't be selected.
By default, the output is toml though you can switch to json with --format=json
CLI
Example with the config from the previous section:
$ toml-combine path/to/config.toml --environment=staging
[fruits]
apple.color = "red"
orange.color = "orange"
Lib
import toml_combine
result = toml_combine.combine(config_file=config_file, environment="staging")
print(result)
{
"fruits": {"apple": {"color": "red"}, "orange": {"color": "orange"}}
}
You can pass either config (TOML string or dict) or config_file (pathlib.Path or string path) to combine(). All other kwargs specify the mapping you want.
A bigger example
[dimensions]
environment = ["production", "staging", "dev"]
service = ["frontend", "backend"]
[default]
registry = "gcr.io/my-project/"
service_account = "my-service-account"
[[override]]
when.service = "frontend"
name = "service-frontend"
container.image_name = "my-image-frontend"
[[override]]
when.service = "backend"
name = "service-backend"
container.image_name = "my-image-backend"
container.port = 8080
[[override]]
when.service = "backend"
when.environment = "dev"
name = "service-dev"
container.env.DEBUG = true
[[override]]
when.environment = ["staging", "dev"]
when.service = "backend"
container.env.ENABLE_EXPENSIVE_MONITORING = false
This produces the following configs:
$ toml-combine example.toml --environment=production --service=frontend
registry = "gcr.io/my-project/"
service_account = "my-service-account"
name = "service-frontend"
[container]
image_name = "my-image-frontend"
$ toml-combine example.toml --environment=production --service=backend
registry = "gcr.io/my-project/"
service_account = "my-service-account"
name = "service-backend"
[container]
image_name = "my-image-backend"
port = 8080
$ toml-combine example.toml --environment=staging --service=frontend
registry = "gcr.io/my-project/"
service_account = "my-service-account"
name = "service-frontend"
[container]
image_name = "my-image-frontend"
$ toml-combine example.toml --environment=staging --service=backend
registry = "gcr.io/my-project/"
service_account = "my-service-account"
name = "service-backend"
[container]
image_name = "my-image-backend"
port = 8080
[container.env]
ENABLE_EXPENSIVE_MONITORING = false
$ toml-combine example.toml --environment=dev --service=backend
registry = "gcr.io/my-project/"
service_account = "my-service-account"
name = "service-backend"
[container]
image_name = "my-image-backend"
port = 8080
[container.env]
DEBUG = true
ENABLE_EXPENSIVE_MONITORING = false
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file toml_combine-1.0.3.tar.gz.
File metadata
- Download URL: toml_combine-1.0.3.tar.gz
- Upload date:
- Size: 19.6 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.12.9
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
17dfe0b0b81ac56497fd3ffba52122aaa9c1857a708d163d01d74c952e6dd84d
|
|
| MD5 |
f9e7703b23642fae7a734b6cc3b7fa2e
|
|
| BLAKE2b-256 |
9c3c5fd48ef07070a26784d3b374cbc04bdbf02403c68899c6a263ac8e13b766
|
Provenance
The following attestation bundles were made for toml_combine-1.0.3.tar.gz:
Publisher:
ci.yml on ewjoachim/toml-combine
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
toml_combine-1.0.3.tar.gz -
Subject digest:
17dfe0b0b81ac56497fd3ffba52122aaa9c1857a708d163d01d74c952e6dd84d - Sigstore transparency entry: 222308224
- Sigstore integration time:
-
Permalink:
ewjoachim/toml-combine@fad64340497bf959c76cd0b3bc4268066358d831 -
Branch / Tag:
refs/tags/1.0.3 - Owner: https://github.com/ewjoachim
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
ci.yml@fad64340497bf959c76cd0b3bc4268066358d831 -
Trigger Event:
push
-
Statement type:
File details
Details for the file toml_combine-1.0.3-py3-none-any.whl.
File metadata
- Download URL: toml_combine-1.0.3-py3-none-any.whl
- Upload date:
- Size: 10.8 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.12.9
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
a787cd69e033b365cb38b720cde8e9a4454028fb667128511210ef1f00d962b7
|
|
| MD5 |
6f261249cdfc09070cbfad39dab89d6a
|
|
| BLAKE2b-256 |
65ca4f023db9fe7427f0344dc42ba542de0da96a127ef30a8484e0736941af49
|
Provenance
The following attestation bundles were made for toml_combine-1.0.3-py3-none-any.whl:
Publisher:
ci.yml on ewjoachim/toml-combine
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
toml_combine-1.0.3-py3-none-any.whl -
Subject digest:
a787cd69e033b365cb38b720cde8e9a4454028fb667128511210ef1f00d962b7 - Sigstore transparency entry: 222308248
- Sigstore integration time:
-
Permalink:
ewjoachim/toml-combine@fad64340497bf959c76cd0b3bc4268066358d831 -
Branch / Tag:
refs/tags/1.0.3 - Owner: https://github.com/ewjoachim
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
ci.yml@fad64340497bf959c76cd0b3bc4268066358d831 -
Trigger Event:
push
-
Statement type: