¿Alguna biblioteca de yaml en Python que admita el volcado de cadenas largas como bloques literales o bloques plegados?

Me gustaría poder volcar un diccionario que contenga cadenas largas que me gustaría tener en el estilo de bloque para facilitar la lectura. Por ejemplo:

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

PyYAML admite la carga de documentos con este estilo, pero parece que no puedo encontrar una manera de volcar documentos de esta manera. ¿Me estoy perdiendo de algo?

 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) 

El resultado:

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

Para completar, uno también debe tener implementaciones de str, pero voy a ser perezoso 🙂

pyyaml admite el volcado de bloques literales o plegados.

Usando Representer.add_representer

tipos de definición:

 class folded_str(str): pass class literal_str(str): pass class folded_unicode(unicode): pass class literal_unicode(str): pass 

Luego puedes definir los representantes para esos tipos. Tenga en cuenta que si bien la solución de Gary funciona bien para Unicode, es posible que necesite más trabajo para que las cadenas funcionen correctamente (consulte la implementación de represent_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) 

Luego, puede agregar esos representantes al dumper predeterminado:

 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) 

… y probarlo:

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

resultado:

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

Utilizando default_style

Si está interesado en que todas sus cadenas sigan un estilo predeterminado, también puede usar el argumento de palabra clave default_style , por ejemplo:

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

o para literales plegados:

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

o para literales entre comillas dobles:

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

Advertencias:

Aquí hay un ejemplo de algo que no puedes esperar:

 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) 

resultados en:

 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) caracteres no imprimibles

Consulte la especificación de YAML para los caracteres escapados ( Sección 5.7 ):

Tenga en cuenta que las secuencias de escape solo se interpretan en escalares entre comillas dobles. En todos los demás estilos escalares, el carácter “\” no tiene un significado especial y los caracteres no imprimibles no están disponibles.

Si desea conservar caracteres no imprimibles (p. Ej., TAB), debe usar scalars de comillas dobles. Si puede volcar un escalar con un estilo literal, y hay un carácter no imprimible (por ejemplo, TAB) allí, su dumper YAML no es compatible.

Por ejemplo, pyyaml detecta el carácter no imprimible \t y utiliza el estilo entre comillas dobles aunque se especifique un estilo predeterminado:

 >>> 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) espacios en blanco iniciales y finales

Otro bit de información útil en la especificación es:

Todos los caracteres de espacio en blanco iniciales y finales se excluyen del contenido

Esto significa que si su cadena tiene espacios en blanco al principio o al final, estos no se conservarán en estilos escalares distintos de los que figuran entre comillas dobles. Como consecuencia, pyyaml intenta detectar lo que está en su escalar y puede forzar el estilo de doble cita.

Esto se puede hacer de manera relativamente fácil, el único “obstáculo” es cómo indicar cuál de los espacios de la cadena, que debe representarse como un escalar plegado, debe convertirse en un pliegue. El escalar literal tiene nuevas líneas explícitas que contienen esa información, pero esto no se puede usar para escalas plegadas, ya que pueden contener nuevas líneas explícitas, por ejemplo, en caso de que haya espacios en blanco iniciales y también se necesite una nueva línea al final para no ser representados con un chumping de extracción. indicador ( >- )

 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) 

lo que da:

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

El atributo fold_pos espera un iterable reversible, que representa las posiciones de los espacios que indican dónde plegarse.

Si nunca tiene caracteres de canalización (‘|’) en sus cadenas, podría haber hecho algo como:

 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) 

lo que también da exactamente la salida que esperas