Можно ли сделать подстановку строк в 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(вместо узла).Это не то же самое, что замена на строковом уровне, потому что при разборе файла 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)Это выведет:
Обратите внимание, что оригинальное имя якоря сохранено в ruamel.ямл .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Если вам не нужны якоря и псевдонимы в выходных данных, вы можете переопределить метод
ignore_aliasesв подклассеRoundTripRepresenterRoundTripDumper(этот метод принимает два аргумента, но при использовании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'заменен на'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 поскольку ЯМЛ может создавать объекты, однако возможно влиять на них. синтаксический анализатор, если эти объекты создаются. этот ответ описывает, как это сделать.
2 сохранение имен изначально не было доступно, но было реализовано в обновлении ruamel.ямл
Comments