如何自动重新缩进YAML文件?

时间:2015-06-04 11:10:50

标签: yaml

让我们考虑这个例子:

---
foo:
  alice: female
  bob:   male
bar:
  - node: 42
    name: none
  - node: 43
    name: none

如果我决定重新启动所有内容并从此开始:

---
foo:
alice: female
bob:   male
bar:
- node: 42
name: none
- node: 43
name: none

我没有足够的信息来做这件事。实际上vim的尝试是这样的:

---
foo:
    alice: female
bob:   male
bar:
    - node: 42
name: none
- node: 43
name: none

Ansible无法做到这一点:

---
foo:

alice: female
bob:   male
bar:

- node: 42
  name: none
  - node: 43
    name: none

我认为可能的解决方法是在缩进增加时添加一个空行:

---
foo:

  alice: female
  bob:   male
bar:

  - node: 42
    name: none
  - node: 43
    name: none

在我看来,YAML的主要问题是人们仍在使用制表符,空格和缩进大小。对于由多人编辑的大型YAML文件,无法再正确解析生成的文件。我看到的两个解决方案是:

  1. 仅使用制表符并强制制表标识,因为Makefiles就是这种情况
  2. 上述解决方案

2 个答案:

答案 0 :(得分:2)

您似乎想要做的是确保您的YAML文件均匀缩进(例如,在检入修订控制系统之前)。你的dedenting然后重新缩进的想法将不起作用,因为如果你弄平了你的结构,你会丢失信息。这样:

class SpanWidget(forms.Widget):
    '''Renders a value wrapped in a <span> tag.

    Requires use of specific form support. (see ReadonlyForm
    or ReadonlyModelForm)
    '''

    def render(self, name, value, attrs=None):
        final_attrs = self.build_attrs(attrs, name=name)
        return mark_safe(u'<span%s >%s</span>' % (
            forms.util.flatatt(final_attrs), self.display_value))

    def value_from_datadict(self, data, files, name):
        return self.original_value


class SpanField(forms.Field):
    '''A field which renders a value wrapped in a <span> tag.

    Requires use of specific form support. (see ReadonlyForm
    or ReadonlyModelForm)
    '''

    def __init__(self, *args, **kwargs):
        kwargs['widget'] = kwargs.get('widget', SpanWidget)
        super(SpanField, self).__init__(*args, **kwargs)
class Readonly(object):
    '''Base class for ReadonlyForm and ReadonlyModelForm which provides
    the meat of the features described in the docstings for those classes.
    '''

    class NewMeta:
        readonly = tuple()

    def __init__(self, *args, **kwargs):
        super(Readonly, self).__init__(*args, **kwargs)
        readonly = self.NewMeta.readonly
        if not readonly:
            return
        for name, field in self.fields.items():
            if name in readonly:
                field.widget = SpanWidget()
            elif not isinstance(field, SpanField):
                continue
            model_field = self.instance._meta.get_field_by_name(name)[0]
            field.widget.original_value = model_field.value_from_object(self.instance)
            field.widget.display_value = unicode(getattr(self.instance, name))


class ReadonlyForm(Readonly, forms.Form):
    '''A form which provides the ability to specify certain fields as
    readonly, meaning that they will display their value as text wrapped
    with a <span> tag. The user is unable to edit them, and they are
    protected from POST data insertion attacks.

    The recommended usage is to place a NewMeta inner class on the
    form, with a readonly attribute which is a list or tuple of fields,
    similar to the fields and exclude attributes on the Meta inner class.

        class MyForm(ReadonlyForm):
            foo = forms.TextField()
            class NewMeta:
                readonly = ('foo',)
    '''
    pass


class ReadonlyModelForm(Readonly, forms.ModelForm):
    '''A ModelForm which provides the ability to specify certain fields as
    readonly, meaning that they will display their value as text wrapped
    with a <span> tag. The user is unable to edit them, and they are
    protected from POST data insertion attacks.

    The recommended usage is to place a NewMeta inner class on the
    form, with a readonly attribute which is a list or tuple of fields,
    similar to the fields and exclude attributes on the Meta inner class.

        class Foo(models.Model):
            bar = models.CharField(max_length=24)

        class MyForm(ReadonlyModelForm):
            class Meta:
                model = Foo
            class NewMeta:
                readonly = ('bar',)
    '''
    pass

由两个映射组成:一个映射带有一个键,一个值是两个键映射到两个值。

此:

class MembershipForm(ReadonlyModelForm):
    class Meta:
        model = Membership
        fields = ('user','board', 'privileged', 'alumni')

    class NewMeta:
        readonly = ('user')
    def email(self):
        return self.instance.user.email

是一个带有三个键的映射,键foo: alice: female bob: male 的值为空标量(除了空字符串外,还可写,foo: alice: female bob: male foo,{{1}在YAML文件中)。

大多数YAML解析器在将文件读入内部数据时会丢失信息:

  • 评论被删除
  • 不会为映射保留密钥排序
  • 不保留标量周围的额外空格

ruamel.yaml Python包(我是作者)是一个增强的解析器,它允许将YAML文件往返三进制数据并返回YAML以保留更多原始信息。它将保留评论和密钥排序,但它会降低,例如单线标量周围的额外间距。

这种往返通常会在第二次往返时稳定下来,因此可以用来重新加载YAML文件。包ruamel.yaml.cmd中包含的~实用程序可用于此而无需自行编程:

NULL

null可缩短为yaml)将检查文件是否以及如何更改。如果确实发生了变化,它会显示unified diff。基于此,如果文件稳定,您可以决定保存文件:

yaml round-trip your_file.yml --verbose

round-trip的输出:

rt

将是:

yaml round-trip your_file.yml --save

当保存时看起来像:

example.yml

缩进级别默认为2,但可以使用--- foo: alice: female # verified bob: male bar: - node: 42 name: none - node: 43 name: none 选项进行设置。

答案 1 :(得分:2)

我刚刚找到了另一个解决方案,它需要替换几个正则表达式。

首先选择Yaml中不包含的单个字符。我用了“°”

在这里我想将4个空格重新缩进2个空格:

搜索正则表达式“ ^(( )*)( )”(大括号中的4个空格),并替换为“ $1°”,直到没有要替换的内容为止。

将所有“ °”替换为“ ”(2个空格)。 瞧!

发生的情况是最右边的缩进被占位符顺序替换。为了避免与已替换的内容不正确匹配,需要一个与搜索不匹配的占位符。

PS:我知道它不是完全自动化的,但是大多数编辑器都可以替换正则表达式,因此不需要额外的安装。