1#!/usr/bin/env python3 2"""Copied from phosphor-settings-manager 3Loads a "target" YAML file and overwrites its values with values from 4"override" YAML files. 5 6Override files are processed in the order given. 7 8Usage: 9 merge_settings.py <target yaml> [override yamls] 10""" 11import sys 12import yaml 13import copy 14 15# Custom representer for None types. This is to handle empty dictionaries. 16# By default Pyyaml outputs these as "null", whereas we want an empty character. 17def represent_none(self, _): 18 return self.represent_scalar('tag:yaml.org,2002:null', '') 19 20def dict_merge(target, source): 21 """Deep merge for dicts. 22 23 Works like dict.update() that recursively updates any dict values present in 24 both parameters. 25 26 Args: 27 target (dict): Values to be overwritten by corresponding values from 28 `source`. 29 source (dict): Overriding values. Not changed by call. 30 31 Returns: 32 `target` with values overwritten from those in `source` at any and all 33 levels of nested dicts. 34 """ 35 if not isinstance(source, dict): 36 return source 37 for k, v in source.items(): 38 if k in target and isinstance(target[k], dict): 39 dict_merge(target[k], v) 40 else: 41 target[k] = copy.deepcopy(v) 42 return target 43 44if len(sys.argv) < 2: 45 sys.exit('Argument required: target yaml') 46 47if len(sys.argv) == 2: 48 # No overrides to handle 49 sys.exit(0) 50 51yaml.add_representer(type(None), represent_none) 52 53target_filename = sys.argv[1] 54with open(target_filename) as target_file: 55 data = yaml.safe_load(target_file) 56 print('Loaded target YAML file ' + target_filename) 57 58for override_filename in sys.argv[2:]: 59 with open(override_filename) as override_file: 60 override = yaml.safe_load(override_file) 61 dict_merge(data, override) 62 print('Merged override YAML file ' + override_filename) 63 64with open(target_filename, 'w') as target_file: 65 yaml.dump(data, target_file, default_flow_style=False) 66 print('Wrote merged target YAML file ' + target_filename) 67