Советы, которые могут спасти Вас от ужасов PyYAML

Моя цель - предложение широкого ассортимента товаров и услуг на постоянно высоком качестве обслуживания по самым выгодным ценам.

Попытка сделать нашу жизнь с PyYAML проще.

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

Код в этой статье гарантированно работает только в Python 3

Всегда используйте safe_load/safe_dump

Способность YAML конструировать любой объект Python делает его опасным для использования вслепую. Для вашего приложения может быть опасно загружать документ при помощи yaml.load из ненадежного источника, такого как Интернет или пользовательский ввод.

См. официальную документацию PyYAML:

Предупреждение: Вызывать yaml.load с любыми данными, полученными из ненадежного источника, небезопасно! yaml.load является таким же мощным инструментом, как pickle.load, и может вызывать любую функцию Python.

Короче говоря, Вы всегда должны использовать yaml.safe_load и yaml.safe_dump в качестве стандартных методов ввода/вывода для YAML.

Сохраняйте порядок ключей (загрузка/выгрузка)

В Python 3.7+ порядок ключей словаря сохраняется естественным образом, поэтому словарь, который Вы получаете от yaml.safe_load, имеет тот же порядок ключей, что и исходный файл.

>>> import yaml
>>> text = """---
... c: 1
... b: 1
... d: 1
... a: 1
... """
>>> d = yaml.safe_load(text)
>>> d
{'c': 1, 'b': 1, 'd': 1, 'a': 1}
>>> list(d)
['c', 'b', 'd', 'a']

При дампе словаря в строку YAML, убедитесь, что добавили аргумент sort_keys=False, чтобы сохранить порядок ключей.

>>> print(yaml.safe_dump(d))
a: 1
b: 1
c: 1
d: 1
>>> d['e'] = 1
>>> print(yaml.safe_dump(d, sort_keys=False))
c: 1
b: 1
d: 1
a: 1
e: 1

Если Ваша версия Python ниже 3.7, или Вы хотите быть уверены, что исходный порядок ключей всегда сохраняется, Вы можете использовать библиотеку oyaml в качестве замены PyYAML.

>>> import oyaml as yaml
>>> d = yaml.safe_load(text)
>>> d
OrderedDict([('c', 1), ('b', 1), ('d', 1), ('a', 1)])
>>> d['e'] = 1
>>> print(yaml.safe_dump(d, sort_keys=False))
c: 1
b: 1
d: 1
a: 1
e: 1

Улучшение отступов в списке (dump)

По умолчанию в PyYAML элементы списка отступают на том же уровне, что и их родитель.

>>> d = {'a': [1, 2, 3]}
>>> print(yaml.safe_dump(d))
a:
- 1
- 2
- 3

Это не очень хороший формат в соответствии с такими руководствами по стилю, как Ansible и HomeAssistant. Он также не распознается редакторами кода, такими как VSCode, делая элементы списка неразворачиваемыми в редакторе.

Чтобы решить эту проблему, Вы можете использовать приведенный ниже фрагмент для определения класса IndentDumper:

class IndentDumper(yaml.Dumper):
    def increase_indent(self, flow=False, indentless=False):
        return super(IndentDumper, self).increase_indent(flow, False)

Затем передайте его в качестве аргумента Dumper в функции yaml.dump.

>>> print(yaml.dump(d, Dumper=IndentDumper))
a:
  - 1
  - 2
  - 3

Обратите внимание, что Dumper не может быть передан в yaml.safe_dump, у которого определен свой собственный dumper-класс.

Вывод читаемого UTF-8 (dump)

По умолчанию PyYAML предполагает, что пользователь хочет получить на выходе только ASCII код, поэтому он преобразует символы UTF-8 в Юникод представление Python.

>>> d = {'a': '你好'}
>>> print(yaml.safe_dump(d))
a: "\u4F60\u597D"

Это делает вывод трудночитаемым для человека.

В современном мире широко поддерживается UTF-8, поэтому безопасно писать UTF-8 в выводе. Передайте allow_unicode=True в yaml.safe_dump, чтобы включить эту возможность.

>>> print(yaml.safe_dump(d, allow_unicode=True))
a: 你好

Не нужен аргумент default_flow_style (dump)

В большинстве случаев мы не хотим, чтобы в выходных данных присутствовал стиль потока (т.е. никакого JSON в YAML). Согласно документации PyYAML, для достижения этого в yaml.safe_dump следует передать default_flow_style=False.

Покопавшись в исходном коде последней версии PyYaml (6.0), можно обнаружить, что это больше не нужно. Вы можете удалить этот аргумент, чтобы сохранить код более чистым и менее запутанным.

Библиотеки

oyaml

Ссылка: https://github.com/wimglenn/oyaml

Как упоминалось выше, oyaml - это замена PyYAML, которая сохраняет упорядочивание словарей.

Используйте oyaml, если Вы уже используете PyYAML в своем коде.

Стоит отметить, что oyaml - это однофайловая библиотека, содержащая всего 53 строки кода. Это делает ее очень гибкой в использовании, Вы можете просто скопировать код в свою библиотеку и настроить ее в соответствии с Вашими потребностями.

strictyaml

Ссылка: https://github.com/crdoconnor/strictyaml

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

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

Используйте StrictYAML, если Вы сильно беспокоитесь о безопасности Вашего приложения.

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

ruamel.yaml

Ссылка: https://yaml.readthedocs.io/en/latest/overview.html

ruamel.yaml - это форк PyYAML, он был выпущен в 2009 году и постоянно поддерживается в течение последнего десятилетия.

Различия с PyYAML перечислены здесь. В целом, ruamel.yaml ориентируется на YAML 1.2 с некоторыми улучшениями в синтаксисе, сделанными автором.

Самым интересным в этой библиотеке является round-trip в процессе загрузки/выгрузки. Это работает как черная магия. Вот объяснение из документации ruamel.yaml:

Round-trip - это последовательность YAML загрузка-модификация-сохранение, и ruamel.yaml пытается сохранить, среди прочего:

- комментарии
- стиль блоков и порядок следования ключей, поэтому Вы можете использовать diff для данных прошедших round-trip
- последовательности в стиле потока ('a: b, c, d')
- имена якорей, созданные вручную (т.е. не в форме idNNN)
- слияния в словарях сохраняются

Стоит использовать ruamel.yaml, если у Вас есть потребность максимально сохранить оригинальное содержимое.

Обратите внимание - метод safe_load в ruamel.yaml (YAML(typ='safe').load) не может разобрать коллекцию в стиле потока (a: {"foo": "bar"}), это недокументированное различие с PyYAML.

Резюме

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

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

Источник: https://habr.com/ru/post/669684/


Интересные статьи

Интересные статьи

Если ваша задача — не просто научиться писать код, а понять, как стать тем, без кого поддержка и развитие проекта просто немыслимы, то этот текст для вас. Заодно поговорим о том, как помочь коллегам п...
Статистический подход к объяснению ошибочных дедлайнов в инженерных проектах Кем бы вы ни были — джуниором, сениором, менеджером проекта или менеджером верхнего звена с двадцатилетним ...
Бесплатный Wi-Fi в баре или кафе теперь стал обычным делом, и клиенты благодарны за эту услугу, особенно иностранцы в роуминге. Во многих заведениях посетителям просто сообщают на...
В этой статье мы рассмотрим, как система управления 1С-Битрикс справляется с большими нагрузками. Данный вопрос особенно актуален сегодня, когда электронная торговля начинает конкурировать по обороту ...
Мало кто думает о разрешении или предотвращении конфликтный ситуаций в процессе сотрудничества до их возникновения. Очень часто к проблемам и порче отношений приводят не какие-то непримиримые раз...