Используйте заполнители в yaml

Есть ли способ использовать заполнители в yaml следующим образом:

foo: &FOO
    <<propname>>: 
        type: number 
        default: <<default>>

bar:
    - *FOO 
       propname: "some_prop"
       default: "some default" 

4 ответа

Решение

контекст

  • YAML версия 1.2
  • пользователь хочет включить переменные заполнители в YAML

проблема

  • YAML изначально не поддерживает переменные заполнители
  • Якоря и псевдонимы допускают некоторый уровень абстракции и косвенности, но они не работают как переменные заполнители, которые могут быть вставлены в произвольные области по всему тексту YAML. Они должны быть размещены как отдельные узлы YAML
  • Есть некоторые дополнительные библиотеки, которые поддерживают произвольные переменные-заполнители, но они не являются частью собственной спецификации YAML

пример

Рассмотрим следующий пример YAML. Это правильно сформированный синтаксис YAML, однако он использует (нестандартные) фигурные скобки со встроенными выражениями.

Встроенные выражения не дают желаемого результата в YAML, потому что они не являются частью собственной спецификации YAML, но они помогают проиллюстрировать, что доступно со стандартным YAML, а что нет.

part01_customer_info:
  cust_fname:   "Homer"
  cust_lname:   "Himpson"
  cust_motto:   "I love donuts!"
  cust_email:   homer@himpson.org

part01_government_info:
  govt_sales_taxrate: 1.15

part01_purchase_info:
  prch_unit_label:    "Bacon-Wrapped Fancy Glazed Donut"
  prch_unit_price:    3.00
  prch_unit_quant:    7
  prch_product_cost:  "{{prch_unit_price * prch_unit_quant}}"
  prch_total_cost:    "{{prch_product_cost * govt_sales_taxrate}}"

part02_shipping_info:
  cust_fname:   "{{cust_fname}}"
  cust_lname:   "{{cust_lname}}"
  ship_city:    Houston
  ship_state:   Hexas

part03_email_info:
  cust_email:     "{{cust_email}}"
  mail_subject:   Thanks for your DoughNutz order!
  mail_notes: |
    We want the mail_greeting to have all the expected values
    with filled-in placeholders (and not curly-braces).
  mail_greeting: |
    Greetings {{cust_fname}} {{cust_lname}}!

    We love your motto "{{cust_motto}}" and we agree with you!

    Your total purchase price is {{prch_total_cost}}

    Thank you for your order!

объяснение

  • Замены, помеченные ЗЕЛЕНЫМ, легко доступны в стандартном YAML с использованием якорей, псевдонимов и ключей слияния.
  • Заметки, помеченные ЖЕЛТЫМ, технически доступны в стандартном YAML, но не без специального объявления типа или какого-либо другого механизма связывания.
  • Заметки, помеченные в RED, недоступны в стандартном YAML и требуют использования какого-либо механизма форматирования строк или шаблонов строк (например, Python str.format, например).

Изображение, объясняющее различные типы подстановки переменных в YAML

подробности

Часто запрашиваемая функция для YAML - это возможность вставлять произвольные переменные заполнители, которые поддерживают произвольные перекрестные ссылки и выражения, которые относятся к другому контенту в том же (или включенном) файле (ах) YAML.

YAML поддерживает привязки и псевдонимы, но эта функция не поддерживает произвольное размещение заполнителей и выражений где-либо в тексте YAML. Они работают только с узлами YAML.

Библиотеки дополнений YAML

Существуют библиотеки расширений YAML, но они не являются частью собственной спецификации YAML.

обходные

  • Используйте YAML в сочетании с системой шаблонов, такой как Jinja2 или Twig
  • Используйте библиотеку расширений YAML
  • использование sprintf или же str.format функциональность стиля от языка хостинга

Смотрите также

С помощью Yglu Structural Templating ваш пример можно записать:

foo: !()
  !? $.propname: 
     type: number 
     default: !? $.default

bar:
  !apply .foo: 
    propname: "some_prop"
    default: "some default"

Отказ от ответственности: я автор или Yglu.

Я полагаю, что https://get-ytt.io/ будет приемлемым решением вашей проблемы

Я также хотел использовать шаблоны в файлах, и я нашел действительно полезным в качестве отправной точки. После нескольких часов исследований и кодирования это мой ответ, пожалуйста, дайте мне знать, если/как я могу это улучшить.

Я не делаю ничего особенного, я пытаюсь использовать синтаксис шаблонов строк Python и немного злоупотребляю методом форматирования строк. Так что это все шаблоны строк Python и подстановка, которые творят здесь магию. Я изменил способ ответ dreftymac, которым ответ dreftymac создал его шаблон yamlфайл для использования в качестве примера.

YAML:

      part01_customer_info:
  cust_fname: "Homer"
  cust_lname: "Himpson"
  cust_motto: "I love donuts!"
  cust_email: homer@himpson.org

part01_government_info:
  govt_sales_taxrate: 1.15

part01_purchase_info:
  prch_unit_label: "Bacon-Wrapped Fancy Glazed Donut"
  prch_unit_price: 3.00
  prch_unit_quant: 7
  prch_product_cost: "eval!#{part01_purchase_info[prch_unit_price]} * {part01_purchase_info[prch_unit_quant]}"
  prch_total_cost: "eval!#{part01_purchase_info[prch_product_cost]} * {part01_government_info[govt_sales_taxrate]}"

part02_shipping_info:
  cust_fname: "{part01_customer_info[cust_fname]}"
  cust_lname: "{part01_customer_info[cust_lname]}"
  ship_city: Houston
  ship_state: Hexas

part03_email_info:
  cust_email: "{part01_customer_info[cust_email]}"
  mail_subject: Thanks for your DoughNutz order!
  mail_notes: |
    We want the mail_greeting to have all the expected values
    with filled-in placeholders (and not curly-braces).
  mail_greeting: |
    Greetings {part01_customer_info[cust_fname]} {part01_customer_info[cust_lname]}!

    We love your motto "{part01_customer_info[cust_motto]}" and we agree with you!

    Your total purchase price is {part01_purchase_info[prch_total_cost]}

я изменился {{}}к {}и добавил eval!#который является идентификатором

Питон:

      from pprint import pprint
import yaml

EVAL_IDENTIFIER = "eval!#"


def eval_math_expr(val):
    if val.startswith(EVAL_IDENTIFIER):
        val = val.replace(EVAL_IDENTIFIER, "")
        val = eval(val)
    return val


def str_template_substitute(full, val=None, initial=True):
    val = val or full if initial else val
    if isinstance(val, dict):
        for k, v in val.items():
            val[k] = str_template_substitute(full, v, False)
    elif isinstance(val, list):
        for idx, i in enumerate(val):
            val[idx] = str_template_substitute(full, i, False)
    elif isinstance(val, str):
        # NOTE:
        # Templating shouldn't be confused or tasked with extra work.
        # I am attaching evaluation to string substitution here,
        # just to prove this can be done.
        val = eval_math_expr(val.format(**full))
    return val


data = yaml.load(open('./data.yml'))
str_template_substitute(data)

pprint(data)

Примечание. Эта функция довольно мощная, поскольку она может работать со словарями, в которые конвертируются JSON/YAML и многие другие форматы в python.

Другие вопросы по тегам