В этом посте я расскажу про не очень известные особенности языка YAML.

Пролог


Системное администрирование за последние несколько лет несколько изменилось. Вместо маленьких скриптиков на bash у нас теперь огромные проекты системы конфигурации. Puppet с миллионом модулей готов «отконфигурять» для нас любую машинку, все поставить и все настроить. И конечно же, венчает это торжество автоматизации Hiera — система управления системой управления.

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

Примеры



Многострочный текст

Очень часто нужно запихнуть в hiera многострочный текст. Для этого есть как минимум 3 способа.

multiliners:
    ugly_multiline: "ugly\nugly\nugly\nugly\n"

    multiline_with_line_ending: |
        multiline text
        with ending

    multiline_without_line_ending: |-
        multiline text
        without ending

Пожалуйста, теперь, когда вы узнали, как делать многострочный текст, не используйте первый способ.

Однострочный текст

Иногда нужно запихать в одну строку много под-строк. В YAML и это можно делать как минимум тремя способами.

singleliners:
    simple: 
        single
        line
        text

    single-line-text: >-
        single
        line
        text

    single-line-text-with-line-ending: >
        single
        line
        text

Интересно, что все 3 способа могут использоваться где угодно, например, в списках:

commands:
    - do something with --a long --list of --parameters 
    - do something 
      with 
      --a long 
      --list of 
      --parameters 

JSON-style

YAML с версии 1.2 — это надмножество JSON. То есть, все, что правильно для JSON, годится и для YAML. Иногда это можно использовать для улучшения читаемости.

json:
    vm-profiles-yaml:
        small:
            cpu:  2
            ram:  2
            disk: 10
            os: rhel6 
        large:
            cpu:  4
            ram:  4
            disk: 10
            os: rhel6 

    vm-profiles-json:
        small:  { cpu: 2, ram: 2, disk: 10, os: rhel6 } 
        large:  { cpu: 4, ram: 4, disk: 10, os: rhel6 } 

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

Матрицы

Еще одним случаем использования JSON-style можно считать определение матриц.

matrices:
    matrix_json_style: [
        [1, 0, 0],
        [0, 1, 0],
        [0, 0, 1],
    ]
    matrix_yaml_style: 
      - [1, 0, 0]
      - [0, 1, 0]
      - [0, 0, 1]

Наследование

Не ожидали? Я тоже удивился. Оказывается в YAML есть еще и наследование.

inheritance:
    _basic: &basic
        cpu:  2
        ram:  2
        disk: 10
        os: rhel6 

    vm-profiles:
        small: 
            <<: *basic
            cpu: 1
        large: 
            <<: *basic
            cpu: 4

Или, если переписать еще короче, используя JSON, можно получить красивую табличку:

inheritance:
    _basic: &basic
        cpu:  2
        ram:  2
        disk: 10
        os: rhel6 

    vm-profiles:
        small: {<<: *basic,  cpu: 1}
        large: {<<: *basic,  cpu: 4}

Ссылки

В предыдущем примере мы рассмотрели наследование, и вы наверное заметили элементы & и *. Эти элементы позволяют определить ссылку на элемент и затем его использовать.

references:
    value1: &reference "Don't repeat yourself!"  
    value2: *reference 

Проверка


Ну, и напоследок, однострочник для проверки YAML файлов:

# requires PyYAML
alias yaml2json='python -c "import sys,yaml,json;sys.tracebacklimit=0;print(json.dumps(yaml.load(open(sys.argv[1]).read()), indent=2))"'

Почитать


Спецификация формата YAML 1.2
Yaml Cookbook for Ruby

И да пребудет с вами KISS и DRY.

UPD
Буду рад в комментариях увидеть ваши примеры интересного использования YAML.

Комментарии (15)


  1. develop7
    03.11.2015 18:52
    +6

    YAML с версии 1.2 — это подмножество JSON
    наоборот


    1. igan
      03.11.2015 23:04

      Поправил. Спасибо.

      И еще спасибо всем кто написал об этом в личку.


  1. ZyXI
    03.11.2015 23:29
    +3

    В YAML вообще много интересных вещей. Например, словари?ключи: ? {This: is a key}: (now find library which will swallow dictionary in key). Pyyaml можно заставить это сожрать, используя собственный конструктор, но по?умолчанию будет

    ConstructorError: while constructing a mapping
      in "<unicode string>", line 1, column 1:
        ? {This: is a key}: (now find li ... 
        ^
    found unhashable key
      in "<unicode string>", line 1, column 3:
        ? {This: is a key}: (now find libr ...
    Более реальный пример:
    print(yaml.dump({(True, False): 1}))
    ? !!python/tuple [true, false]
    : 1


    И, конечно, есть тёги, позволяющие сериализовать и десериализовать множество классов «из коробки» и вообще всё, что угодно:
    class A(object):
        def __init__(self, **kwargs):
            self.__dict__.update(kwargs)
    
    print(yaml.dump(A(foo=True)))
    !!python/object:__main__.A {foo: true}


    Это возможности языка, а не библиотеки pyyaml — от последней здесь конкретный вид нестандартных тёгов (!!python/…), — если вы решите написать YAML парсер вам придётся всё это поддерживать, хотя предоставлять какие?либо конкретные нестандартные тёги от парсера не требуется (точнее, предоставлять их вообще, требуется предоставить возможность эти тёги создать).


  1. ZyXI
    03.11.2015 23:34

    Кстати, на сайте yaml.org есть фактически 3 библиотеки с поддержкой YAML-1.2: Haskell (reference parser), C++ и VimL (относительно последнего знаю про 1.2 только потому что сам писал). Всё остальное: 1.0 или 1.1. Это как?то странно.


    1. Borro
      04.11.2015 01:09

      Похоже сайт не особо обновляется. Вот для PHP поддержка YAML-1.2: github.com/symfony/yaml


      1. lazycommit
        04.11.2015 13:48

        Только без наследования. Проверял по примеру из поста. Да и в документации к 1.2 про "<<: " ничего не сказано. Это, видимо, фича от python?


        1. lazycommit
          04.11.2015 13:53

          UPD: ан-нет, упоминание наследования есть в п. 3.2 (только без примеров). Видимо symfony упустили этот момент.


          1. lazycommit
            04.11.2015 13:59

            UPD2: github.com/symfony/yaml наследование не работает только в JSON нотации. В обычной всё ок.


            1. ZyXI
              04.11.2015 19:18
              +2

              А вы в JSON нотации использовали << или "<<"? Второе и не должно работать: resolver’ы традиционно разрешают только plain scalar’ы.

              Для тех, кто не в курсе: pyyaml устроен так: reader > scanner > parser > composer > resolver > constructor. Reader просто читает ввод и декодирует его. Scanner генерирует token’ы, и его, наверное, можно назвать lexer’ом. Parser из token’ов собирает более высокоуровневые события, но ещё не AST. Composer собирает из событий дерево. Resolver присваивает тёги всем значениям, если только они не имеют их в явном виде (именно из?за последнего 1 — это не строка: по стандарту в failsafe schema это вполне себе "1" в виде plain scalar’а). И уже constructor генерирует непосредственно возвращаемый результат.


              1. ZyXI
                04.11.2015 19:30

                Хотя, наверное, в JSON схеме просто нету нужного resolver’а для <<. Попробуйте перед этим явно указать тёг: !!tag:yaml.org,2002:merge <<: *…


  1. vintage
    04.11.2015 07:21
    +1

    > И да пребудет с вами KISS

    Авторы YAML не смогли выбрать какой-то один синтаксис, поэтому теперь там есть по 5 способов записать одно и то же. Из-за чего библиотеки огромные, глючат, тормозят и отстают от спецификации, а люди то и дело косячат выбирая не тот тип многострочного текста и тп. Как-то не очень это KISS.


    1. develop7
      04.11.2015 09:10

      теперь там есть по 5 способов записать одно и то же
      Приведите примеров для наглядности.


    1. igan
      04.11.2015 09:54
      +1

      Способы все таки немного отличаются и каждый раз можно выбрать то что лучше подходит.
      Так возможно не нужно было выделять >, >- (и до кучи >+ ) в отдельные способы, тк они всего лишь по разному трактуют конец строки.

      А в прочем согласен, KISS в спецификации не хвататет, и собственно по этому KISS нужен прежде всего тому кто на YAML пишет.


  1. amarao
    04.11.2015 13:25
    +5

    Ox, я не уверен, что это хорошо. Один напишет, другим читать.


  1. ammaaim
    04.11.2015 20:55
    +2

    С момента первого открытия Makefile побаиваюсь всяких $$+, $<, ит.п. приблуд призванных 'упростить' нам жизнь.