toml-combine 1.0.1__py3-none-any.whl → 1.0.3__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
toml_combine/cli.py CHANGED
@@ -70,7 +70,7 @@ def cli(argv) -> int:
70
70
  try:
71
71
  config = combiner.build_config(dict_config)
72
72
  except exceptions.TomlCombineError as exc:
73
- print(exc, file=sys.stderr)
73
+ print(f"Error: {exc}", file=sys.stderr)
74
74
  return 1
75
75
 
76
76
  # Parse all arguments
@@ -86,7 +86,7 @@ def cli(argv) -> int:
86
86
  try:
87
87
  result = lib.combine(config=dict_config, **mapping)
88
88
  except exceptions.TomlCombineError as exc:
89
- print(exc, file=sys.stderr)
89
+ print(f"Error: {exc}", file=sys.stderr)
90
90
  return 1
91
91
 
92
92
  if args.format == "toml":
toml_combine/combiner.py CHANGED
@@ -21,6 +21,7 @@ class Override:
21
21
  class Config:
22
22
  dimensions: Mapping[str, list[str]]
23
23
  default: Mapping[str, Any]
24
+ # List of overrides, in order of increasing specificity
24
25
  overrides: Sequence[Override]
25
26
 
26
27
 
@@ -30,6 +31,7 @@ def clean_dimensions_dict(
30
31
  """
31
32
  Recreate a dictionary of dimension values with the same order as the
32
33
  dimensions list.
34
+ Also check that the values are valid.
33
35
  """
34
36
  result = {}
35
37
  if invalid_dimensions := set(to_sort) - set(clean):
@@ -63,7 +65,7 @@ T = TypeVar("T", dict, list, str, int, float, bool)
63
65
 
64
66
  def merge_configs(a: T, b: T, /) -> T:
65
67
  """
66
- Recursively merge two configuration dictionaries, with b taking precedence.
68
+ Recursively merge two configuration dictionaries a and b, with b taking precedence.
67
69
  """
68
70
  if isinstance(a, dict) != isinstance(b, dict):
69
71
  raise ValueError(f"Cannot merge {type(a)} with {type(b)}")
@@ -116,6 +118,9 @@ def are_conditions_compatible(
116
118
 
117
119
 
118
120
  def build_config(config: dict[str, Any]) -> Config:
121
+ """
122
+ Build a finalized Config object from the given configuration dictionary.
123
+ """
119
124
  config = copy.deepcopy(config)
120
125
  # Parse dimensions
121
126
  dimensions = config.pop("dimensions")
@@ -165,6 +170,13 @@ def generate_for_mapping(
165
170
  config: Config,
166
171
  mapping: Mapping[str, str],
167
172
  ) -> Mapping[str, Any]:
173
+ """
174
+ Generate a configuration based on the provided mapping of dimension values.
175
+ The mapping should contain only the dimensions defined in the config.
176
+ If a dimension is not defined in the mapping, the default value for that
177
+ dimension will be used.
178
+ """
179
+
168
180
  result = copy.deepcopy(config.default)
169
181
  keys_to_conditions: dict[tuple[str, ...], list[Mapping[str, list[str]]]] = {}
170
182
  # Apply each matching override
@@ -20,7 +20,7 @@ class TomlEncodeError(TomlCombineError):
20
20
 
21
21
 
22
22
  class IncompatibleOverrides(TomlCombineError):
23
- """In override {id}: Overrides defining the same configuration keys must be included in one another or mutually exclusive.\nKey defined multiple times: {key}\nOther override: {other_override}"""
23
+ """Incompatible overrides `{id}` and `{other_override}`: When they're both applicable, overrides defining a common overridden key ({key}) must be a subset of one another"""
24
24
 
25
25
 
26
26
  class DimensionNotFound(TomlCombineError):
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: toml-combine
3
- Version: 1.0.1
3
+ Version: 1.0.3
4
4
  Summary: A tool for combining complex configurations in TOML format.
5
5
  Author-email: Joachim Jablon <ewjoachim@gmail.com>
6
6
  License-Expression: MIT
@@ -47,7 +47,10 @@ name = "my-service"
47
47
  registry = "gcr.io/my-project/"
48
48
  container.image_name = "my-image"
49
49
  container.port = 8080
50
- service_account = "my-service-account"
50
+
51
+ [[override]]
52
+ when.environment = "production"
53
+ service_account = "my-production-service-account"
51
54
 
52
55
  [[override]]
53
56
  when.environment = "staging"
@@ -68,29 +71,115 @@ The common configuration to start from, before we start overlaying overrides on
68
71
 
69
72
  ### Overrides
70
73
 
71
- Overrides define a set of condition where they apply (`when.<dimension> =
72
- "<value>"`) and the values that are overridgden when they're applicable.
73
-
74
- - In case 2 overrides are applicable and define a value for the same key, if one is more
75
- specific than the other (e.g. env=prod,region=us is more specific than env=prod) then
76
- its values will have precedence.
77
- - If 2 applicable overrides both define a dimension that the other one doesn't, they're
78
- incompatible, and running the tool with a configuration that would select both of them
79
- will yield an error.
80
-
81
- Examples:
82
- - Override 1: `env=staging` & Override 2: `region=eu` are incompatible (1 defines
83
- `env` not in 2, 2 defines `region` not in 1).
84
- - Override 1: `env=staging` & Override 2: `env=staging, region=eu` are compatible
85
- (all dimensions defined in 1 are also in 2)
86
- - Override 1: `env=staging` & Override 2: `env=prod` are compatible
87
- (they define the same dimensions)
88
- - Override 1: `env=staging, service=frontend` & Override 2: `region=eu, service=frontend`
89
- are incompatible (1 defines `env` not in 2, 2 defines `region` not in 1)
90
-
91
- > [!Note]
74
+ Each override defines a set of condition where it applies (`when.<dimension> =
75
+ "<dimension_value>"`) and a set of overridden key/values.
76
+
77
+ ```toml
78
+ [[override]]
79
+ # Keys starting with `when.` are "conditions"
80
+ when.environment = "staging"
81
+ when.region = "us"
82
+
83
+ # Other keys in an override are "overridden keys" / "overridden values"
84
+ service_account = "my-us-staging-service-account"
85
+ ```
86
+
87
+ If you run `toml-combine` with a given mapping that selects multiple overrides, they
88
+ will be checked for _compatibility_ with one another, and an error will be raised if
89
+ they're _not compatible_.
90
+
91
+ Compatibility rules:
92
+
93
+ - If the two overrides don't share any _overridden key_, then they're always compatible.
94
+
95
+ <details>
96
+ <summary>Example (click to expand)</summary>
97
+
98
+ ```toml
99
+ [dimensions]
100
+ environment = ["staging"]
101
+ region = ["eu"]
102
+
103
+ [[override]]
104
+ when.environment = "staging"
105
+ service_account = "my-staging-service-account"
106
+
107
+ [[override]]
108
+ when.region = "eu"
109
+ env.CURRENCY = "EUR"
110
+ ```
111
+
112
+ </details>
113
+
114
+ - If an override defines a set of conditions (say `env=prod`) and the other one defines
115
+ strictly more conditions (say `env=prod, region=eu`, in other words, it defines all
116
+ the conditions of the first override and then some more), then they're compatible.
117
+ Also, in that case, **the override with more conditions will have precedence**.
118
+
119
+ <details>
120
+ <summary>Example</summary>
121
+
122
+ ```toml
123
+ [dimensions]
124
+ environment = ["staging"]
125
+ region = ["eu"]
126
+
127
+ [[override]]
128
+ when.environment = "staging"
129
+ service_account = "my-staging-service-account"
130
+
131
+ [[override]]
132
+ when.environment = "staging"
133
+ when.region = "eu"
134
+ service_account = "my-staging-eu-service-account"
135
+ ```
136
+
137
+ </details>
138
+
139
+ - If they both define a dimension that the other one doesn't, they're incompatible.
140
+
141
+ <details>
142
+ <summary>Example (click to expand)</summary>
143
+
144
+ Incompatible overrides: neither is a subset of the other one and they both
145
+ define a value for `service_account`:
146
+
147
+ ```toml
148
+ [dimensions]
149
+ environment = ["staging"]
150
+ region = ["eu"]
151
+
152
+ [default]
153
+ service_account = "my-service-account"
154
+
155
+ [[override]]
156
+ when.environment = "staging"
157
+ service_account = "my-staging-service-account"
158
+
159
+ [[override]]
160
+ when.region = "eu"
161
+ service_account = "my-eu-service-account"
162
+ ```
163
+
164
+ ```console
165
+ $ toml-combine config.toml --environment=staging --region=eu
166
+ Error: Incompatible overrides `{'region': ['eu']}` and `{'environment': ['staging']}`:
167
+ When they're both applicable, overrides defining a common overridden key (foo) must be
168
+ a subset of one another
169
+ ```
170
+
171
+ > [!NOTE]
172
+ > It's ok to have incompatible overrides in your config as long as you don't
173
+ > run `toml-combine` with a mapping that would select both of them. In the example
174
+ > above, if you run `toml-combine --environment=staging --region=eu`, the error
175
+ > will be triggered, but you can run `toml-combine --environment=staging`.
176
+
177
+ </details>
178
+
179
+ > [!NOTE]
92
180
  > Instead of defining a single value for the override dimensions, you can define a list.
93
181
  > This is a shortcut to duplicating the override with each individual value:
182
+ >
94
183
  > ```
95
184
  > [[override]]
96
185
  > when.environment = ["staging", "prod"]
@@ -0,0 +1,12 @@
1
+ toml_combine/__init__.py,sha256=TDkOwwEM-nS6hOh79u9Qae6g2Q6VfANpPpnKGfSgu80,84
2
+ toml_combine/__main__.py,sha256=hmF8N8xX6UEApzbKTVZ-4E1HU5-rjgUkdXNLO-mF6vo,100
3
+ toml_combine/cli.py,sha256=1eeh6sdKLsn5klRSc7E1rcBhN5Cf1O1Za5-Ule9FRVU,2867
4
+ toml_combine/combiner.py,sha256=fjsZ4WAKl7uO8BE8lYirJgo_1uj1t2o24rJA_FT1NFM,6147
5
+ toml_combine/exceptions.py,sha256=KrZpSdaI_ssYKw9LMsc3PAqNXEP-q6sT9scpgpXOo8o,1094
6
+ toml_combine/lib.py,sha256=jh6OG57JefpGa-WE-mLSIK6KjyJ0-1yGBynr_kiVTww,1634
7
+ toml_combine/toml.py,sha256=iBV8xj0qWcvGp2AZaML8FCT3i2X9DL7iA6jd-wcP5Bc,814
8
+ toml_combine-1.0.3.dist-info/METADATA,sha256=mP4H-UhEjmZedJSN55C6OOKvqOf2xgS1U4xYi2afKas,10653
9
+ toml_combine-1.0.3.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
10
+ toml_combine-1.0.3.dist-info/entry_points.txt,sha256=dXUQNom54uZt_7ylEG81iNYMamYpaFo9-ItcZJU6Uzc,58
11
+ toml_combine-1.0.3.dist-info/licenses/LICENSE,sha256=tA7wpipzIPGl7xL5xzMMg0RhhXz9CKOa-ZnlYzgiTKg,1059
12
+ toml_combine-1.0.3.dist-info/RECORD,,
@@ -1,12 +0,0 @@
1
- toml_combine/__init__.py,sha256=TDkOwwEM-nS6hOh79u9Qae6g2Q6VfANpPpnKGfSgu80,84
2
- toml_combine/__main__.py,sha256=hmF8N8xX6UEApzbKTVZ-4E1HU5-rjgUkdXNLO-mF6vo,100
3
- toml_combine/cli.py,sha256=hG03eDKz7xU-ydJIa1kDuu6WlFzNS3GTMJ6zals9M9c,2843
4
- toml_combine/combiner.py,sha256=HG7KV3EGwmcHOMruhp7sMkH-uXqWKpsO7_BvhNQ2KQY,5668
5
- toml_combine/exceptions.py,sha256=cRAZhxg3OHgzp5hJzyxNGG_jvGUm8gG8XzndQXBhvo8,1116
6
- toml_combine/lib.py,sha256=jh6OG57JefpGa-WE-mLSIK6KjyJ0-1yGBynr_kiVTww,1634
7
- toml_combine/toml.py,sha256=iBV8xj0qWcvGp2AZaML8FCT3i2X9DL7iA6jd-wcP5Bc,814
8
- toml_combine-1.0.1.dist-info/METADATA,sha256=K0j97FCiS7tpENiP_HVoxE1b6_nLDqk_E1OklTtFhpk,8812
9
- toml_combine-1.0.1.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
10
- toml_combine-1.0.1.dist-info/entry_points.txt,sha256=dXUQNom54uZt_7ylEG81iNYMamYpaFo9-ItcZJU6Uzc,58
11
- toml_combine-1.0.1.dist-info/licenses/LICENSE,sha256=tA7wpipzIPGl7xL5xzMMg0RhhXz9CKOa-ZnlYzgiTKg,1059
12
- toml_combine-1.0.1.dist-info/RECORD,,