Change <TEST> in python yaml file
I need to replace tags like these in the yaml file.
The data that has been replaced is saved in the list.
I have this yaml file
vagrantfile: target: local vm: provider: local: box: puphpet/ubuntu1404-x<BITS> # MEERKEUZE: 32 OF 64, STANDAARD 64 box_url: puphpet/ubuntu1404-x<BITS> # MEERKEUZE: 32 OF 64, STANDAARD 64 box_version: '0' chosen_virtualizer: <virtualiser> # MEERKEUZE: virtualbox OF vmware, STANDAARD 64 virtualizers: virtualbox: modifyvm: natdnshostresolver1: false showgui: 0 vmware: numvcpus: 1 parallels: use_linked_clone: 0 check_guest_tools: 0 update_guest_tools: 0 machines: vflm_7orc03npv15g: id: <ID> # STRING hostname: <HOSTNAME> # STRING network: private_network: <IP-ADDRESS> #lOKAAL IP, STANDARAARD 192.168.50.101 forwarded_port: hon1d: host: '80' guest: '80' memory: '<MEGABYTES>' # AANTAL, STANDAARD VEELVOUD VAN 1024 cpus: '<CORES>' # AANTAL CPUS, STANDAARD AANTAL CORES - 1, NIET HOGER DAN AANTAL CORES What can I do to change these tags?
Probably the easiest way to use pyyaml. For example. to set the cpus value:
import yaml foo = yaml.load(open('file.yml', 'r')) foo['vagrantfile']['vm']['provider']['local']['machines']['vflm_7orc03npv15g']['memory'] = 'bar' yaml.dump(foo, open('out.yml', 'w')) Only problem: you will lose comments this way.
You can convert yaml to a string pattern and use string formatting to achieve this
template = """vagrantfile: target: local vm: provider: local: box: puphpet/ubuntu1404-x%(BITS)s # MEERKEUZE: 32 OF 64, STANDAARD 64 box_url: puphpet/ubuntu1404-x%(BITS)s # MEERKEUZE: 32 OF 64, STANDAARD 64 box_version: '0' chosen_virtualizer: %(virtualiser)s # MEERKEUZE: virtualbox OF vmware, STANDAARD 64 virtualizers: virtualbox: modifyvm: natdnshostresolver1: false showgui: 0 vmware: numvcpus: 1 parallels: use_linked_clone: 0 check_guest_tools: 0 update_guest_tools: 0 machines: vflm_7orc03npv15g: id: %(ID)s # STRING hostname: %(OSTNAME)s # STRING network: private_network: %(IP-ADDRESS)s #lOKAAL IP, STANDARAARD 192.168.50.101 forwarded_port: hon1d: host: '80' guest: '80' memory: '%(MEGABYTES)s' # AANTAL, STANDAARD VEELVOUD VAN 1024 cpus: '%(CORES)s' # AANTAL CPUS, STANDAARD AANTAL CORES - 1, NIET HOGER DAN AANTAL CORES""" replaced = template % {"BITS": "some value", "virtualiser": "something", "ID": 2412, "OSTNAME": "OS name", "IP-ADDRESS": "some ip", "MEGABYTES": 100, "CORES": 16} You have to do this at the YAML level, just to make sure that if your substitution causes the resulting string to indicate that this is really happening. For example. if your <ID> should be replaced with something like @abc , the actual line in the YAML file should become
id: '@abc' but not
id: '@abc' as @ reserved. There are several such caveats, so line-level substitution is not a good idea.
You can do:
import sys import ruamel.yaml yaml_str = """\ vagrantfile: target: local vm: provider: local: box: puphpet/ubuntu1404-x<BITS> # MEERKEUZE: 32 OF 64, STANDAARD 64 box_url: puphpet/ubuntu1404-x<BITS> # MEERKEUZE: 32 OF 64, STANDAARD 64 box_version: '0' chosen_virtualizer: <virtualiser> # MEERKEUZE: virtualbox OF vmware, STANDAARD 64 virtualizers: virtualbox: modifyvm: natdnshostresolver1: false showgui: 0 vmware: numvcpus: 1 parallels: use_linked_clone: 0 check_guest_tools: 0 update_guest_tools: 0 machines: vflm_7orc03npv15g: id: <ID> # STRING hostname: <HOSTNAME> # STRING network: private_network: <IP-ADDRESS> #lOKAAL IP, STANDARAARD 192.168.50.101 forwarded_port: hon1d: host: '80' guest: '80' memory: '<MEGABYTES>' # AANTAL, STANDAARD VEELVOUD VAN 1024 cpus: '<CORES>' # AANTAL CPUS, STANDAARD AANTAL CORES - 1, NIET HOGER DAN AANTAL CORES """ def replace(data, values): def do_one(data, values): if isinstance(data, dict): for k in data: data[k] = do_one(data[k], values) return data elif isinstance(data, list): for idx, elem in enumerate(data): data[idx] = do_one(elem, values) return data elif isinstance(data, str): for k in values: # full match if data == k: return values[k] if '<' in data and '>' in data: for k in values: data = data.replace(k, str(values[k])) return data expanded = { ('<' + k + '>'): v for (k, v) in values.iteritems()} do_one(data, expanded) data = ruamel.yaml.round_trip_load(yaml_str) replace(data, { 'BITS': 64, 'virtualiser': 'virtualbox', 'HOSTNAME': 'localhost', 'IP-ADDRESS': '192.168.0.1', 'ID': '@abc', 'MEGABYTES': 2048, 'CORES': 8, }) ruamel.yaml.round_trip_dump(data, stream=sys.stdout, indent=4) which will give you:
vagrantfile: target: vm: provider: local: box: puphpet/ubuntu1404-x64 # MEERKEUZE: 32 OF 64, STANDAARD 64 box_url: puphpet/ubuntu1404-x64 # MEERKEUZE: 32 OF 64, STANDAARD 64 box_version: chosen_virtualizer: virtualbox # MEERKEUZE: virtualbox OF vmware, STANDAARD 64 virtualizers: virtualbox: modifyvm: natdnshostresolver1: showgui: vmware: numvcpus: parallels: use_linked_clone: check_guest_tools: update_guest_tools: machines: vflm_7orc03npv15g: id: '@abc' # STRING hostname: localhost # STRING network: private_network: 192.168.0.1 #lOKAAL IP, STANDARAARD 192.168.50.101 forwarded_port: hon1d: host: guest: memory: 2048 # AANTAL, STANDAARD VEELVOUD VAN 1024 cpus: 8 # AANTAL CPUS, STANDAARD AANTAL CORES - 1, NIET HOGER DAN AANTAL CORES Note:
- comments are saved
- that the values ββfor
cpusandmemoryare integers, not strings - so that the
idvalue is correctly quoted as needed.
The above example uses ruamel.yaml : disclaimer I am the author of this package. You can do the same with PyYAML, but you will lose comments, and you need to limit yourself to the YAML 1.1 standard (since 2005), and not the YAML 1.2 standard (since 2009)