2016-02-19 19 views
6

設定に使用するいくつかの(おそらくもっと多くの)yamlファイルがあるシステムを設定しようとしています。私は別のファイルの中のあるファイルで物事を参照できるようにしたい。2つのyamlファイルを結合するPyYaml

私はYAMLがこれを許可していないことを知っています。

私の考えでは、2つのYAMLファイルを結合して1つのファイルとして扱うというのが私の考えです。 2つのファイルを一緒にcatでき、一時ファイルを作成し、YAMLとして読み込んだり、テキストとしてファイルを読み込んだり、連結したり、文字列を解析したりすることができます。

しかし、これを行うにはより良い方法があるはずです。ある?

答えて

0

YAMLで参照する唯一の方法は、&(アンカー)と*(エイリアス)を使用することです。これらが機能するには、同じYAML文書になければなりません。以下は(これはmerge key特徴に基づいているが、通常のオブジェクト参照は、同じ制限を持って)動作しません:

import ruamel.yaml 

yaml_str = """\ 
a: &BASE { x: 1, y: 2} 
--- 
b: 
    << : *BASE 
    z: 3 
""" 

for data in ruamel.yaml.load_all(yaml_str): 
    print(data) 

は、「BASE」が見つからなかったことを作曲エラーをスロー。ドキュメントセパレータ---を削除しても問題ありません。

原則として、2つのドキュメントを連結することができます。エイリアスを含むドキュメントを別々に読み込むことは、アンカーを含むドキュメントと連結しなければ実行できません。

さらに、すべてのドキュメントがトップレベルにマッピングまたはシーケンスを持つ必要があるという警告があります。マッピングに

- &BASE a 
- b 

:シーケンスを組み合わせたい場合

結果がロード可能ではありません。


示されているように、トップレベルのタイプがすべてのファイルで同じ場合、YAMLファイルをロードしてメモリに組み込むことはできません。私。

- &CENTER { x: 1, y: 2 } 
- &LEFT { x: 0, y: 2 } 
- &BIG { r: 10 } 
- &SMALL { r: 1 } 

2.yaml

# Explicit keys 
- 
    x: 1 
    y: 2 
    r: 10 
    label: center/big 

3.yaml

# Merge one map 
- 
    << : *CENTER 
    r: 10 
    label: center/big 

4.yaml

# Merge multiple maps 
- 
    << : [ *CENTER, *BIG ] 
    label: center/big  
012マージキードキュメント分割の例 1.yamlに与えられました

5.yaml

# Override 
- 
    << : [ *BIG, *LEFT, *SMALL ] 
    x: 1 
    label: center/big 

あなたは個別のYAMLファイルの使用load()ことはできませんし、それらを組み合わせた:

import ruamel.yaml 
import glob 

data = [] 
for file_name in sorted(glob.glob('*.yaml')): 
    data.append(ruamel.yaml.load(open(file_name))) 
print(ruamel.yaml.dump(data, allow_unicode=True)) 

(その上2.yaml場合などに働くだろう

class CombinedOpenForReading(object): 
    def __init__(self, file_names): 
     self._to_do = file_names[:] 
     self._fp = None 

    def __enter__(self): 
     return self 

    def __exit__(self, exception_type, exception_value, exception_traceback): 
     if self._fp: 
      self._fp.close() 

    def read(self, size=None): 
     res = '' 
     while True: 
      if self._fp is None: 
       if not self._to_do: 
        return res 
       else: 
        self._fp = open(self._to_do.pop(0)) 
      if size is None: 
       data = self._fp.read() 
      else: 
       data = self._fp.read(size) 
      if size is None or not data: 
       self._fp.close() 
       self._fp = None 
      res += data 
      if size is None: 
       continue 
      size -= len(data) 
      if size == 0: 
       break 
     return res 

行うには:

import ruamel.yaml 
import glob 

with CombinedOpenForReading(sorted(glob.glob('*.yaml'))) as fp: 
    data = ruamel.yaml.round_trip_load(fp) 
assert data[6]['r'] == 10 
print(ruamel.yaml.dump(data, Dumper=ruamel.yaml.RoundTripDumper)) 

あなたがプログラムの外のファイルを連結したくない場合は、 このクラスを使用することができます)

を別名を持っていませんでした取得する:

- &CENTER {x: 1, y: 2} 
- &LEFT {x: 0, y: 2} 
- &BIG {r: 10} 
- &SMALL {r: 1} 
# Explicit keys 
- x: 1 
    y: 2 
    r: 10 
    label: center/big 
# Merge one map 
- <<: *CENTER 
    r: 10 
    label: center/big 
# Merge multiple maps 
- <<: [*CENTER, *BIG] 
    label: center/big 
# Override 
- <<: [*BIG, *LEFT, *SMALL] 
    x: 1 
    label: center/big 

(正しい順序でファイルを手渡す必要があるため、ソート。ファイルの末尾に改行があることを確認してください。そうしないと予期しないエラーが発生する可能性があります)。

+0

あなたの注意点では、BASEと* BASEは同じ種類のもの(シーケンス対マッピング)でなければなりませんか?私はそれが問題だとは気付かなかったが、それは問題ではない。 1つのファイルで複数のドキュメントを使用する予定はありません。 1つのドキュメントで複数のファイルを使用する予定です。 –

+0

ああ、あなたがファイルをマージできるようにするには、それらのファイルのトップタイプが同じでなければならないということです。はい、それは問題ではありません。 –

+0

@BrianPostowはい、一番上のタイプは同じファイルで – Anthon

0

これは@ Anthon'sよりも簡単だと思います。それは完全ではないかもしれませんが、私が必要とするものだと思っています。

def merge(fList): 
    ''' 
    Takes a list of yaml files and loads them as a single yaml document. 
    Restrictions: 
    1) None of the files may have a yaml document marker (---) 
    2) All of the files must have the same top-level type (dictionary or list) 
    3) If any pointers cross between files, then the file in which they are defined (&) must be 
    earlier in the list than any uses (*). 
    ''' 

    if not fList: 
     #if flist is the empty list, return an empty list. This is arbitrary, if it turns out that 
     #an empty dictionary is better, we can do something about that. 
     return [] 

    sList = [] 
    for f in fList: 
     with open(f, 'r') as stream: 
      sList.append(stream.read()) 
    fString = '' 
    for s in sList: 
     fString = fString + '\n'+ s 

    y = yaml.load(fString) 

    return y 

コメント歓迎です。

+0

3つのファイルをマージして、マージする代わりに完全に置き換えます – holms

+0

どうしてですか?それらが辞書であり、キーがばらばらになっていない場合、確かに、それはおそらくYAMLエラーです... –

+0

私は心から考えていません。 merge-yaml npm cliツールを使用して終了しました。また、そこに問題があったので、1つのファイルを2番目の位置に移動しなければなりませんでした:Dこれはどのように可能かと思います。 yamlファイルの構文エラーをチェックしてフォーマットするyamlreaderもありますが、まだ助けにはなりませんでした – holms

関連する問題