Какие-нибудь библиотеки yaml в Python, которые поддерживают вывод длинных строк в виде блочных литералов или свернутых блоков? - PullRequest
19 голосов
/ 22 июня 2011

Я хотел бы иметь возможность выгружать словарь, содержащий длинные строки, которые я хотел бы иметь в стиле блока для удобства чтения. Например:

foo: |
  this is a
  block literal
bar: >
  this is a
  folded block

PyYAML поддерживает загрузку документов с этим стилем, но я не могу найти способ выгрузить документы таким способом. Я что-то упустил?

Ответы [ 3 ]

23 голосов
/ 16 сентября 2011
import yaml

class folded_unicode(unicode): pass
class literal_unicode(unicode): pass

def folded_unicode_representer(dumper, data):
    return dumper.represent_scalar(u'tag:yaml.org,2002:str', data, style='>')
def literal_unicode_representer(dumper, data):
    return dumper.represent_scalar(u'tag:yaml.org,2002:str', data, style='|')

yaml.add_representer(folded_unicode, folded_unicode_representer)
yaml.add_representer(literal_unicode, literal_unicode_representer)

data = {
    'literal':literal_unicode(
        u'by hjw              ___\n'
         '   __              /.-.\\\n'
         '  /  )_____________\\\\  Y\n'
         ' /_ /=== == === === =\\ _\\_\n'
         '( /)=== == === === == Y   \\\n'
         ' `-------------------(  o  )\n'
         '                      \\___/\n'),
    'folded': folded_unicode(
        u'It removes all ordinary curses from all equipped items. '
        'Heavy or permanent curses are unaffected.\n')}

print yaml.dump(data)

Результат:

folded: >
  It removes all ordinary curses from all equipped items. Heavy or permanent curses
  are unaffected.
literal: |
  by hjw              ___
     __              /.-.\
    /  )_____________\\  Y
   /_ /=== == === === =\ _\_
  ( /)=== == === === == Y   \
   `-------------------(  o  )
                        \___/

Для полноты, нужно также иметь реализации str, но я буду ленив: -)

18 голосов
/ 01 января 2014

pyyaml поддерживает дамп литеральных или сложенных блоков.

Использование Representer.add_representer

определения типов:

class folded_str(str): pass

class literal_str(str): pass

class folded_unicode(unicode): pass

class literal_unicode(str): pass

Затем вы можете определить представители для этихтипы.Обратите внимание, что, хотя Gary решение отлично работает для юникода, вам может потребоваться дополнительная работа для правильной работы строк (см. реализация представления_str ).

def change_style(style, representer):
    def new_representer(dumper, data):
        scalar = representer(dumper, data)
        scalar.style = style
        return scalar
    return new_representer

import yaml
from yaml.representer import SafeRepresenter

# represent_str does handle some corner cases, so use that
# instead of calling represent_scalar directly
represent_folded_str = change_style('>', SafeRepresenter.represent_str)
represent_literal_str = change_style('|', SafeRepresenter.represent_str)
represent_folded_unicode = change_style('>', SafeRepresenter.represent_unicode)
represent_literal_unicode = change_style('|', SafeRepresenter.represent_unicode)

Затем вы можете добавить эти представления в дампер по умолчанию:

yaml.add_representer(folded_str, represent_folded_str)
yaml.add_representer(literal_str, represent_literal_str)
yaml.add_representer(folded_unicode, represent_folded_unicode)
yaml.add_representer(literal_unicode, represent_literal_unicode)

... и проверить его:

data = {
    'foo': literal_str('this is a\nblock literal'),
    'bar': folded_unicode('this is a folded block'),
}

print yaml.dump(data)

результат:

bar: >-
  this is a folded block
foo: |-
  this is a
  block literal

Использование default_style

Если вы заинтересованы в том, чтобы все строки соответствовали стилю по умолчанию, вы также можете использовать аргумент ключевого слова default_style, например:

>>> data = { 'foo': 'line1\nline2\nline3' }
>>> print yaml.dump(data, default_style='|')
"foo": |-
  line1
  line2
  line3

или для сложенных литералов:

>>> print yaml.dump(data, default_style='>')
"foo": >-
  line1

  line2

  line3

или для двойных кавычек:

>>> print yaml.dump(data, default_style='"')
"foo": "line1\nline2\nline3"

Предостережения:

Вот пример того, чего вы можете не ожидать:

data = {
    'foo': literal_str('this is a\nblock literal'),
    'bar': folded_unicode('this is a folded block'),
    'non-printable': literal_unicode('this has a \t tab in it'),
    'leading': literal_unicode('   with leading white spaces'),
    'trailing': literal_unicode('with trailing white spaces  '),
}
print yaml.dump(data)

приводит к:

bar: >-
  this is a folded block
foo: |-
  this is a
  block literal
leading: |2-
     with leading white spaces
non-printable: "this has a \t tab in it"
trailing: "with trailing white spaces  "

1) непечатаемым символам

См. Спецификацию YAML для экранированных символов ( Раздел 5.7 ):

Обратите внимание, что escape-последовательности интерпретируются только в скалярах с двойными кавычками.Во всех других скалярных стилях символ «\» не имеет специального значения, а непечатные символы недоступны.

Если вы хотите сохранить непечатные символы (например,TAB), вам нужно использовать скаляры с двойными кавычками.Если вы можете создать скаляр с буквальным стилем, и там есть непечатаемый символ (например, TAB), ваш дампер YAML несовместим.

Например, pyyaml обнаруживает непечатныйсимвол \t и использует стиль в двойных кавычках, даже если указан стиль по умолчанию:

>>> data = { 'foo': 'line1\nline2\n\tline3' }
>>> print yaml.dump(data, default_style='"')
"foo": "line1\nline2\n\tline3"

>>> print yaml.dump(data, default_style='>')
"foo": "line1\nline2\n\tline3"

>>> print yaml.dump(data, default_style='|')
"foo": "line1\nline2\n\tline3"

2) начальные и конечные пробелы

Еще одна полезная информация в спецификации:

Все начальные и конечные пробельные символы исключаются из содержимого

Это означает, что если ваша строка имеет начальный или конечный пробел, они не будут сохраненыв скалярных стилях, кроме двойных кавычек.Как следствие, pyyaml пытается определить, что находится в вашем скаляре, и может вызвать двойные кавычки.

0 голосов
/ 23 августа 2018

Это может быть относительно легко сделать, единственное «препятствие» состоит в том, как указать, какое из пространств в строке, которое должно быть представлено в виде сложенного скаляра, должно стать сгибом.У литерального скаляра есть явные символы новой строки, содержащие эту информацию, но это не может использоваться для свернутых скаляров, поскольку они могут содержать явные символы новой строки, например, в случае, когда есть пробел в начале, а также в конце нужна новая строка, чтобы ее нельзя было представить с разбивкойиндикатор (>-)

import sys
import ruamel.yaml

folded = ruamel.yaml.scalarstring.FoldedScalarString
literal = ruamel.yaml.scalarstring.LiteralScalarString

yaml = ruamel.yaml.YAML()

data = dict(
    foo=literal('this is a\nblock literal\n'), 
    bar=folded('this is a folded block\n'),
)

data['bar'].fold_pos = [data['bar'].index(' folded')]

yaml.dump(data, sys.stdout)

, который дает:

foo: |
  this is a
  block literal
bar: >
  this is a
  folded block

Атрибут fold_pos ожидает обратимую итерацию, представляет позиции пробелов , указывающие, кудафолд.

Если в ваших строках никогда не было символов канала ('|'), вы могли бы сделать что-то вроде:

import re

s = 'this is a|folded block\n'
sf = folded(s.replace('|', ' '))  # need to have a space!
sf.fold_pos = [x.start() for x in re.finditer('\|', s)]  # | is special in re, needs escaping


data = dict(
    foo=literal('this is a\nblock literal\n'), 
    bar=sf,  # need to have a space
)

yaml = ruamel.yaml.YAML()
yaml.dump(data, sys.stdout)

, что также дает ожидаемый результат

...