Можно ли сделать подстановку строк в YAML?
есть ли способ заменить строку в YAML. Например, я хотел бы определить sub
один раз и используйте его во всем файле YAML.
sub: ['a', 'b', 'c']
command:
params:
cmd1:
type: string
enum : # Get the list defined in 'sub'
description: Exclude commands from the test list.
cmd2:
type: string
enum: # Get the list defined in 'sub'
1 ответов
вы не можете действительно заменить строковые значения в YAML, как при замене подстроки некоторой строки другой подстрокой1. Однако YAML имеет возможность пометить узел (в вашем случае список ['a', 'b', 'c'] с помощью якорь и повторно использовать, что как узел псевдоним.
якоря принимают форму &some_id
и вставляются перед узлом, а узлы псевдонимов задаются *some_id
(вместо a узел.)
это не то же самое, что подстановка на строковом уровне, потому что при разборе файла YAML ссылка может быть сохранена. Как и в случае загрузки YAML в Python для любых якорей в типах коллекций (т. е. не при использовании якорей на скалярах):
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)
это выведет:
sub: &sub0 [a, X, 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
обратите внимание, что оригинальное название якорь preserved2 в ruamel.и YAML.
если вы не хотите якоря и псевдонимы в выходных данных вы можете переопределить ignore_aliases
метод RoundTripRepresenter
подкласс RoundTripDumper
(этот метод принимает два аргумента, но с помощью lambda *args: ....
вы не должны знать об этом):
dumper = yaml.RoundTripDumper
dumper.ignore_aliases = lambda *args : True
yaml.dump(data1, sys.stdout, Dumper=dumper, indent=4)
что дает:
sub: [a, X, c]
command:
params:
cmd1:
type: string
# Get the list defined in 'sub'
enum: [a, X, c]
description: Exclude commands from the test list.
cmd2:
type: string
# Get the list defined in 'sub'
enum: [a, X, c]
и этот трюк можно использовать для чтения в файле YAML, как если бы вы сделали замену строки, перечитав материал, который вы сбросили, игнорируя псевдонимы:
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)
а теперь только один 'b'
is превратился в 'X'
:
sub: [a, b, c]
command:
params:
cmd1:
type: string
# Get the list defined in 'sub'
enum: [a, b, c]
description: Exclude commands from the test list.
cmd2:
type: string
# Get the list defined in 'sub'
enum: [a, X, c]
как указано выше, это необходимо только при использовании якорей / псевдонимов для типов коллекций, а не при использовании его на скаляре.
1 поскольку YAML может создавать объекты, однако можно влиять
синтаксический анализатор, если эти объекты созданы. ответ описывает, как это сделать.
2 сохранение имен изначально было недоступно, но было реализовано в обновлении руамель.и YAML