You cannot replace string values in YAML, as when replacing a substring of some string with another substring¹. However, YAML has the ability to mark node (in your case, the list ['a', 'b', 'c'] with the anchor and reuse it as an alias node .
The anchors take the form &some_id and are inserted before the node, and the nodes of the alias are given *some_id (instead of node).
This is not the same as line-level substitution, because during link parsing the YAML file this link can be saved. As with loading YAML in Python for any collection type anchors (i.e. not when using anchors on scalars):
import sys import ruamel.yaml as yaml yaml_str = """\ sub: &sub0 [a, b, c] command: params: cmd1: type: string # Get the list defined in 'sub' enum : *sub0 description: Exclude commands from the test list. cmd2: type: string # Get the list defined in 'sub' enum: *sub0 """ data1 = yaml.load(yaml_str, Loader=yaml.RoundTripLoader) # the loaded elements point to the same list assert data1['sub'] is data1['command']['params']['cmd1']['enum'] # change in cmd2 data1['command']['params']['cmd2']['enum'][3] = 'X' yaml.dump(data1, sys.stdout, Dumper=yaml.RoundTripDumper, indent=4)
This will output:
sub: &sub0 [a, X, c] command: params: cmd1: type: string
note that the original binding name is stored² in ruamel.yaml .
If you don't want bindings and aliases in your output, you can override the ignore_aliases method in the RoundTripRepresenter RoundTripDumper subclass (this method takes two arguments, but with lambda *args: .... you shouldn't know about that):
dumper = yaml.RoundTripDumper dumper.ignore_aliases = lambda *args : True yaml.dump(data1, sys.stdout, Dumper=dumper, indent=4)
What gives:
sub: [a, X, c] command: params: cmd1: type: string
And this trick can be used to read in the YAML file, as if you performed a line replacement by re-reading the material that you dropped, ignoring the aliases:
data2 = yaml.load(yaml.dump(yaml.load(yaml_str, Loader=yaml.RoundTripLoader), Dumper=dumper, indent=4), Loader=yaml.RoundTripLoader) # these are lists with the same value assert data2['sub'] == data2['command']['params']['cmd1']['enum'] # but the loaded elements do not point to the same list assert data2['sub'] is not data2['command']['params']['cmd1']['enum'] data2['command']['params']['cmd2']['enum'][5] = 'X' yaml.dump(data2, sys.stdout, Dumper=yaml.RoundTripDumper, indent=4)
And now only one 'b' changes to 'X' :
sub: [a, b, c] command: params: cmd1: type: string
As stated above, this is only necessary when using anchors / aliases in collection types, and not when you use it on a scalar.
¹ Since YAML can create objects, however, the parser can influence if these objects are created. This answer describes how to do this.
² Name retention was initially unavailable, but was implemented in the ruamel.yaml update