Маркировка в YAML означает то же самое, что и в реальном мире: что вы снабжаете некоторый объект тегом.И тег не является заменой для этого объекта.
Когда вы делаете это: !Include: other/file
, это эквивалентно выполнению !Include Null: other/file
.И при разборе этого узла Null
у вас нет доступа к other/file
, он не анализируется и, возможно, еще даже не сканировался.
При разборе included: !Include other/file
узел other/file
не не имеет понятие своего контекста.Это может быть, например, в виде стека, из которого вы можете получить доступ к последнему объекту, но теперь реализован PyYAML.Это означает, что если вы сделаете это, вы можете заменить помеченный узел только структурой данных, загруженной из включаемого файла.
Что вы можете сделать, это определить «специальный ключ», например, +<
изатем поместите тег !Include
в сопоставление:
!Include
foo: bar
baz: buff
+<: other/file
Таким образом, вы можете реализовать конструктор для сопоставления для создания dict, но при обнаружении специального ключа используйте значение ключа, связанное со значением, в качестве именифайла для загрузки и вставки в качестве дополнительного ключа / значений в создаваемый вами dict (поэтому вам не нужно обращаться к некоторому недоступному контекстному узлу).Вам нужно было бы найти какой-то способ определения приоритета ключей, которые приходят из включенного файла, и ключей, которые исходят из фактического, включая отображение.Вы можете реализовать несколько включений, позволяя значению быть списком имен файлов.Это похоже на независимый от языка ключей слияния тип .
Вы могли бы даже сделать вышеупомянутое без использования тега вообще, добавив преобразователь для +<
(возьмите один дляфункция слияния в качестве примера), подкласс SafeLoader
и реализация метода flatten_mapping
для поддержки этого включения.Однако это будет означать, что для других не так очевидно, что происходит что-то особенное, как при наличии тега.
Обратите внимание:
- вы должны опустить директивуend-seperator (
---
), это излишне, поскольку у вас нет никаких директив. - ваши YAML-файлы должны иметь расширение
.yaml
, если это невозможно (например,потому что файловая система не поддерживает суффиксы длиннее трех символов)
В ruamel.yaml вы можете реализовать это, используя:
import sys
import ruamel.yaml
from pathlib import Path
yaml = ruamel.yaml.YAML(typ='safe', pure=True)
yaml_str = """\
!Include
foo: bar
baz: buff
+<: other/file.yaml
"""
class Include:
@classmethod
def from_yaml(cls, constructor, node):
mapping = constructor.construct_mapping(node)
file_names = mapping.get('+<')
if file_names is None:
return mapping
if not isinstance(file_names, list):
file_names = [file_names]
y = constructor.loader
yaml = ruamel.yaml.YAML(typ=y.typ, pure=y.pure)
for file_name in file_names:
for key, value in yaml.load(Path(file_name)).items():
if key in mapping:
continue
mapping[key] = value
return mapping
yaml.register_class(Include)
data = yaml.load(yaml_str)
print(data)
, что дает:
{'foo': 'bar', 'baz': 'buff', '+<': 'other/file.yaml', 'special': 'value'}
в PyYAML вы должны быть в состоянии сделать что-то подобное, с большим количеством кода и поддержкой только спецификации YAML 1.1 (которая была заменена в 2009 году).