@@ -110,6 +110,8 @@ def __init__(self, isolated: bool, load_only: Optional[Kind] = None) -> None:
110
110
self .isolated = isolated
111
111
self .load_only = load_only
112
112
113
+ self ._load_order : Tuple [Kind , ...] = OVERRIDE_ORDER
114
+
113
115
# Because we keep track of where we got the data from
114
116
self ._parsers : Dict [Kind , List [Tuple [str , RawConfigParser ]]] = {
115
117
variant : [] for variant in OVERRIDE_ORDER
@@ -125,6 +127,13 @@ def load(self) -> None:
125
127
if not self .isolated :
126
128
self ._load_environment_vars ()
127
129
130
+ @property
131
+ def load_order (self ) -> Tuple [Kind , ...]:
132
+ # The act of computing the config dictionary will result in the _load_order
133
+ # attribute being correctly updated.
134
+ _ = self ._dictionary
135
+ return self ._load_order
136
+
128
137
def get_file_to_edit (self ) -> Optional [str ]:
129
138
"""Returns the file with highest priority in configuration"""
130
139
assert self .load_only is not None , "Need to be specified a file to be editing"
@@ -225,11 +234,45 @@ def _ensure_have_load_only(self) -> None:
225
234
@property
226
235
def _dictionary (self ) -> Dict [str , Any ]:
227
236
"""A dictionary representing the loaded configuration."""
237
+
238
+ # We always read the config in the default load-order first, giving
239
+ # us a deterministic load-order configuration value.
240
+ config = self ._blended_config_dict (OVERRIDE_ORDER )
241
+
242
+ # Re-compose the config based on the desired override-order, if
243
+ # different to the default.
244
+ config_precedence : List [str ] = (
245
+ str (config .get ("global.config-precedence" , "" )).strip ().splitlines ()
246
+ )
247
+ if config_precedence :
248
+ bad_values = [
249
+ value
250
+ for value in config_precedence
251
+ if value not in kinds .reverse_mapping
252
+ ]
253
+ if bad_values :
254
+ term_or_terms = "term" if len (bad_values ) == 1 else "terms"
255
+ raise ConfigurationError (
256
+ f"Invalid config-precedence { term_or_terms } provided "
257
+ f"({ ',' .join (bad_values )} ). "
258
+ f"Valid values are { ', ' .join (map (repr , kinds .reverse_mapping ))} ."
259
+ )
260
+ self ._load_order = tuple (
261
+ getattr (kinds , kinds .reverse_mapping [value ])
262
+ for value in config_precedence
263
+ )
264
+ if self ._load_order != OVERRIDE_ORDER :
265
+ config = self ._blended_config_dict (self ._load_order )
266
+ return config
267
+
268
+ def _blended_config_dict (
269
+ self , load_order : Tuple [Kind , ...] = OVERRIDE_ORDER
270
+ ) -> Dict [str , Any ]:
228
271
# NOTE: Dictionaries are not populated if not loaded. So, conditionals
229
272
# are not needed here.
230
273
retval = {}
231
274
232
- for variant in OVERRIDE_ORDER :
275
+ for variant in load_order :
233
276
retval .update (self ._config [variant ])
234
277
235
278
return retval
0 commit comments