在PyYaml事件结构中通过主键不必要地引用子键和迭代

时间:2018-03-26 11:32:20

标签: python-3.x yaml pyyaml

我有下一个代码:

import gnupg
import re
import textwrap
from pprint import pprint
import yaml
from yaml.events import *

class AppendableEvents:
  def __init__(self, path, events):
    self.path = path
    self.events = events

  def correct_position(self, levels):
    if len(self.path) != len(levels):
      return False
    for index, expected in enumerate(self.path):
      if expected != levels[index].cur_id:
        return False
    return True

class Level:
  def __init__(self, mode):
    self.mode = mode
    self.cur_id = -1 if mode == "item" else ""

def append_to_yaml(yamlFile, targetFile, items):
  events = []
  levels = []
  with open(yamlFile, 'r') as handle:
    for event in yaml.parse(handle):
      if isinstance(event, StreamStartEvent) or \
         isinstance(event, StreamEndEvent) or \
         isinstance(event, DocumentStartEvent) or \
         isinstance(event, DocumentEndEvent):
        pass
      elif isinstance(event, CollectionStartEvent):
        if len(levels) > 0:
          if levels[-1].mode == "key":
            # we can only handle scalar keys
            raise ValueError("encountered complex key!")
          else:
            if levels[-1].mode == "value":
              levels[-1].mode = "key"
        if isinstance(event, MappingStartEvent):
          levels.append(Level("key"))
        else: # SequenceStartEvent
          levels.append(Level("item"))
      elif isinstance(event, ScalarEvent):
        if len(levels) > 0:
          if levels[-1].mode == "item":
            levels[-1].cur_id += 1
          elif levels[-1].mode == "key":
            levels[-1].cur_id = event.value
            levels[-1].mode = "value"
          else: # mode == "value"
            levels[-1].mode = "key"
      elif isinstance(event, CollectionEndEvent):
        # here we check whether we want to append anything
        levels.pop()
        for item in items:
          if item.correct_position(levels):
            for additional_event in item.events:
              events.append(additional_event)
      events.append(event)
  with open(targetFile, mode="w") as handle:
    yaml.emit(events, handle,line_break=True)

def key(name):
  return ScalarEvent(None, None, (True, True), name)

def literal_value(content):
  return ScalarEvent(None, None, (False, True), content, style="|")

def map(*scalarValues):
  return [MappingStartEvent(None, None, True)] + \
    [ScalarEvent(None, None, (False, True), v, style="|") for v in scalarValues] + \
    [MappingEndEvent()]


gpg = gnupg.GPG(gnupghome='~/.gnupg')
gpg.encoding = 'utf-8'

def ExistingFile():
    with open(creds) as sensitive_data:
        for line in sensitive_data:
            encrypted_value = gpg.encrypt(
                re.sub(r'^( +?|[A-Za-z0-9]|[A-Za]|[0-9])+( +)?' + '=' + '( +)?', '', line, 1),
                recipients="test", always_trust=True)
            if not encrypted_value.ok:
                print(encrypted_value.status, '\n', encrypted_value.stderr)
                break
            line = re.sub(r'^( +)?|( +)?' + '=' + '.*', '', line.rstrip('\n'))
            append_to_yaml(f1, f1, [
              AppendableEvents(["global","app1"], [
                key("app2")] + map(line, encrypted_value.data.decode()))])
                #key(line), literal_value(encrypted_value.data.decode())])])

ExistingFile()

creds文件的内容是:

1=1
sadsa=ars

f1文件的内容是:

global:
    app1:
      test: |
        -----BEGIN PGP MESSAGE-----
        Version: GnuPG v1
        -----END PGP MESSAGE----- 

在此代码中,必要的键,子键和值将附加到yaml文件中的相应块。

但问题是我在密钥方法中传递的值正在迭代,因此根据creds文件中的内容追加两次或更多次:

global:
    app1:
      test: |
        -----BEGIN PGP MESSAGE-----
        Version: GnuPG v1
        -----END PGP MESSAGE-----     
    app2:
      "1": |
        -----BEGIN PGP MESSAGE-----
        Version: GnuPG v1
        -----END PGP MESSAGE-----
    app2:
      "sadsa": |
        -----BEGIN PGP MESSAGE-----
        Version: GnuPG v1
        -----END PGP MESSAGE-----

也是子键" 1"和" sadsa"附加双引号。

我想摆脱双引号,当我将它与map方法结合时,我在key方法中传递的值只会被附加到文件一次。

所以最终结果将是例如:

global:
    app1:
      test: |
        -----BEGIN PGP MESSAGE-----
        Version: GnuPG v1
        -----END PGP MESSAGE-----     
    app2:
      1: |
        -----BEGIN PGP MESSAGE-----
        Version: GnuPG v1
        -----END PGP MESSAGE-----
      sadsa: |
        -----BEGIN PGP MESSAGE-----
        Version: GnuPG v1
        -----END PGP MESSAGE-----

注意 :使用literal_value方法的注释行非常好,所以我不打算破解它的逻辑,使用map方法对case进行更改。

1 个答案:

答案 0 :(得分:0)

首先,请在提问时提供 minimal 示例。我从您的代码中删除了gnupg内容,因为它与您的问题完全无关。您有责任仅提供实际代码的相关部分,以便尝试帮助您的人不需要挖掘不相关的行。

现在,要解决您的问题:您只需构建适当输出的事件。这是一个解决方案(没有gnupg的东西):

def ExistingFile():
  with open(creds) as sensitive_data:
    additions = [key("app2"), MappingStartEvent(None, None, True)]
    for line in sensitive_data:
      encrypted_value = re.sub(r'^( +?|[A-Za-z0-9]|[A-Za]|[0-9])+( +)?' + '=' + '( +)?', '', line, 1)
      line = re.sub(r'^( +)?|( +)?' + '=' + '.*', '', line.rstrip('\n'))
      additions.extend(
        [ScalarEvent(None, None, (True, False), line),
         ScalarEvent(None, None, (False, True), encrypted_value, style='|')])
    additions.append(MappingEndEvent())
    append_to_yaml(f1, f1_mod, [AppendableEvents(["global", "app1"], additions)])

正如您所看到的,我为键构造标量事件的方式与值的不同,因此值显示为文字标量,键为普通标量(不带引号)。

我也只拨打append_to_yaml一次,以便app2只创建一次。