Andrew Geissler | 384f190 | 2020-04-02 13:11:28 +0000 | [diff] [blame] | 1 | #!/usr/bin/env python3 |
Xo Wang | 49abd7e | 2017-03-17 18:15:10 -0700 | [diff] [blame] | 2 | """Loads a "target" YAML file and overwrites its values with values from |
| 3 | "override" YAML files. |
| 4 | |
| 5 | Override files are processed in the order given. |
| 6 | |
| 7 | Usage: |
Gaurav Gandhi | d08d106 | 2022-02-18 21:24:19 +0000 | [diff] [blame] | 8 | merge_settings.py <target yaml> [override/remove yamls] |
Xo Wang | 49abd7e | 2017-03-17 18:15:10 -0700 | [diff] [blame] | 9 | """ |
| 10 | import sys |
| 11 | import yaml |
| 12 | import copy |
| 13 | |
Gaurav Gandhi | d08d106 | 2022-02-18 21:24:19 +0000 | [diff] [blame] | 14 | def dict_merge(target, source, remove): |
Xo Wang | 49abd7e | 2017-03-17 18:15:10 -0700 | [diff] [blame] | 15 | """Deep merge for dicts. |
| 16 | |
Gaurav Gandhi | d08d106 | 2022-02-18 21:24:19 +0000 | [diff] [blame] | 17 | Works like dict.update() that recursively updates/removes any dict values |
| 18 | present in both parameters. |
Xo Wang | 49abd7e | 2017-03-17 18:15:10 -0700 | [diff] [blame] | 19 | |
| 20 | Args: |
| 21 | target (dict): Values to be overwritten by corresponding values from |
| 22 | `source`. |
| 23 | source (dict): Overriding values. Not changed by call. |
Gaurav Gandhi | d08d106 | 2022-02-18 21:24:19 +0000 | [diff] [blame] | 24 | remove (bool): If this is true then it removes the entry provided in |
| 25 | 'source' along with all entries under it from 'target'. Otherwise |
| 26 | it overrides the values from 'source' |
Xo Wang | 49abd7e | 2017-03-17 18:15:10 -0700 | [diff] [blame] | 27 | |
| 28 | Returns: |
Gaurav Gandhi | d08d106 | 2022-02-18 21:24:19 +0000 | [diff] [blame] | 29 | `target` with values overwritten/removed from those in `source` at any |
| 30 | and all levels of nested dicts. |
Xo Wang | 49abd7e | 2017-03-17 18:15:10 -0700 | [diff] [blame] | 31 | """ |
| 32 | if not isinstance(source, dict): |
| 33 | return source |
Andrew Geissler | 384f190 | 2020-04-02 13:11:28 +0000 | [diff] [blame] | 34 | for k, v in source.items(): |
Xo Wang | 49abd7e | 2017-03-17 18:15:10 -0700 | [diff] [blame] | 35 | if k in target and isinstance(target[k], dict): |
Gaurav Gandhi | d08d106 | 2022-02-18 21:24:19 +0000 | [diff] [blame] | 36 | dict_merge(target[k], v, remove) |
Xo Wang | 49abd7e | 2017-03-17 18:15:10 -0700 | [diff] [blame] | 37 | else: |
Allen.Wang | 4818d0e | 2022-03-07 17:05:21 +0800 | [diff] [blame] | 38 | if remove is True and k in target: |
Gaurav Gandhi | d08d106 | 2022-02-18 21:24:19 +0000 | [diff] [blame] | 39 | target.pop(k) |
| 40 | else: |
| 41 | target[k] = copy.deepcopy(v) |
Xo Wang | 49abd7e | 2017-03-17 18:15:10 -0700 | [diff] [blame] | 42 | return target |
| 43 | |
| 44 | if len(sys.argv) < 2: |
| 45 | sys.exit('Argument required: target yaml') |
| 46 | |
| 47 | if len(sys.argv) == 2: |
| 48 | # No overrides to handle |
| 49 | sys.exit() |
| 50 | |
| 51 | target_filename = sys.argv[1] |
| 52 | with open(target_filename) as target_file: |
| 53 | data = yaml.safe_load(target_file) |
| 54 | print('Loaded target YAML file ' + target_filename) |
| 55 | |
| 56 | for override_filename in sys.argv[2:]: |
Gaurav Gandhi | d08d106 | 2022-02-18 21:24:19 +0000 | [diff] [blame] | 57 | if override_filename.endswith('.override.yml'): |
| 58 | with open(override_filename) as override_file: |
| 59 | override = yaml.safe_load(override_file) |
| 60 | dict_merge(data, override, False) |
| 61 | print('Merged override YAML file ' + override_filename) |
| 62 | elif override_filename.endswith('.remove.yml'): |
| 63 | with open(override_filename) as override_file: |
| 64 | override = yaml.safe_load(override_file) |
| 65 | dict_merge(data, override, True) |
| 66 | print('Removed data from source YAML file' + override_filename) |
Xo Wang | 49abd7e | 2017-03-17 18:15:10 -0700 | [diff] [blame] | 67 | |
| 68 | with open(target_filename, 'w') as target_file: |
| 69 | yaml.dump(data, target_file) |
| 70 | print('Wrote merged target YAML file ' + target_filename) |