Как преобразовать строку с символами новой строки в строку со сложенным стилем YAML в файле? - PullRequest
0 голосов
/ 15 февраля 2019

Во многих файлах YAML, которые у меня есть, обычно существует это:

query: "SELECT\n  *\nFROM\n  (SELECT\n  'apple' AS fruit,\n  'carrot' AS vegetable);"

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

query: >
SELECT
  *
FROM (
  SELECT
    "apple" AS fruit,
    "carrot" AS vegetable);

в YAMLfile.

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

Код того, что я пытался сделать:

from contextlib import redirect_stdout
import io

with io.StringIO() as buf, redirect_stdout(buf):
   print(query)
   yam['query'] = buf.getvalue()

НоЯ просто вернул строку с escape-символами.

1 Ответ

0 голосов
/ 16 февраля 2019

Использование сложенного скаляра, вероятно, не то, что вам нужно, потому что в соответствии с spec :

Складывание позволяет разбивать длинные строки в любом месте, где один пробел разделяет два-пространство символов.

И у вас есть несколько строк, разбитых между этими словами, но не все.

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

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


Я рекомендую перейти к буквальным скалярам, ​​где каждая новая строка является в точности новой строкой.Если ваш файл находится в input.yaml, следующие строки будут обрабатывать любые строки значений, которые соответствуют заданным вами критериям:

import sys
from pathlib import Path
import ruamel.yaml

def try_convert(s):
    """return None if not converted"""
    if 'SELECT' not in s:
        return None
    if '\n' not in s:
        return None
    return ruamel.yaml.scalarstring.LiteralScalarString(s)

def recurse(d):
    """this walks recursively over the datastructure you loaded, entering elements of list, 
       and values of mappings
    """
    if isinstance(d, dict):
        for k in d:
            v = d[k]
            res = try_convert(v)
            if res is not None:
                d[k] = res
            else:
                recurse(d[k])
    elif isinstance(d, list):
        for idx, elem in enumerate(d):
            res = try_convert(elem)
            if res is not None:
                d[idx] = res
            else:
                recurse(d)
    # nothing to do for scalars, which means a scalar in the root of a document will not be converted

file_name = Path('input.yaml')

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

, что дает:

query: |-
  SELECT
    *
  FROM
    (SELECT
    'apple' AS fruit,
    'carrot' AS vegetable);

Индикатор разброса полосы (-) после того, как индикатор литерального стиля (|) удаляет любое количество символов новой строки в конце этого скаляра, поэтому после загрузки не будет дополнительной строки после точки с запятой.

Можно, конечно, вызватьнекоторый SQL prettyprinter в конвертировании.Пока его вывод является допустимым SQL и включает в себя символы новой строки, вы можете передать его в LiteralScalarString и вернуть его из try_convert()

...