381def combineConfigFiles(local, config_path, fragment_key="include"):
382 """
383 Recursively combine configuration fragments into `local`.
384
385 - Looks for `fragment_key` at any dict node.
386 - If value is a string/path: merge that fragment.
387 - If value is a list: merge all fragments in order.
388 For conflicts between fragments, the **earlier** file in the list wins.
389 Local keys still override the merged fragments.
390
391 Returns True if any merging happened below this node.
392 """
393 if not isinstance(config_path, (list, Path, str)):
394 raise ValueError("Please specify the path or a list of paths where configuration is expected to reside")
395 if isinstance(config_path, list):
396 config_paths = [Path(path) for path in config_path]
397 else:
398 warnings.warn(
399 "Passing a single path to combineConfigFiles is deprecated, please pass a list of paths instead",
400 TextConfigWarning,
401 stacklevel=2,
402 )
403 config_paths = [Path(config_path)]
404
405 combined = False
406
407
408 if isinstance(local, dict):
409 to_combine = local.values()
410 elif isinstance(local, list):
411 to_combine = local
412 else:
413 return combined
414
415
416 for sub in to_combine:
417 combined = combineConfigFiles(sub, config_paths, fragment_key=fragment_key) or combined
418
419
420 if fragment_key not in local:
421 return combined
422
423
424 if not isinstance(local, dict):
425 return combined
426
427
428 value = local[fragment_key]
429 if isinstance(value, (str, Path)):
430 warnings.warn(
431 f"{fragment_key} should be followed with a list of files",
432 TextConfigWarning,
433 stacklevel=2,
434 )
435 paths = [value]
436 elif isinstance(value, list):
437 paths = value
438 else:
439 raise TypeError(f"'{fragment_key}' must be a string path or a list of paths, got {type(value).__name__}")
440
441
442 fragments_acc = {}
443 for entry in paths:
444 fragment_path = _find_fragment(Path(entry), config_paths)
445 fragment = _load_fragment(fragment_path)
446
447
448 combineConfigFiles(fragment, [fragment_path.parent, *config_paths], fragment_key=fragment_key)
449
450
451 _merge_dicts(fragments_acc, fragment)
452
453
454 del local[fragment_key]
455
456
457 _merge_dicts(local, fragments_acc)
458
459 return True
460
461