Support recursive config inclusions (#3783)

This commit is contained in:
Lewis Juggins 2016-10-12 11:05:41 +01:00 committed by Johann Kellerman
parent 1d7169403b
commit 10feac11d9
2 changed files with 116 additions and 12 deletions

View file

@ -1,8 +1,8 @@
"""YAML utility functions.""" """YAML utility functions."""
import glob
import logging import logging
import os import os
import sys import sys
import fnmatch
from collections import OrderedDict from collections import OrderedDict
from typing import Union, List, Dict from typing import Union, List, Dict
@ -61,23 +61,32 @@ def _include_yaml(loader: SafeLineLoader,
return load_yaml(fname) return load_yaml(fname)
def _find_files(directory, pattern):
"""Recursively load files in a directory."""
for root, _dirs, files in os.walk(directory):
for basename in files:
if fnmatch.fnmatch(basename, pattern):
filename = os.path.join(root, basename)
yield filename
def _include_dir_named_yaml(loader: SafeLineLoader, def _include_dir_named_yaml(loader: SafeLineLoader,
node: yaml.nodes.Node): node: yaml.nodes.Node) -> OrderedDict:
"""Load multiple files from directory as a dictionary.""" """Load multiple files from directory as a dictionary."""
mapping = OrderedDict() # type: OrderedDict mapping = OrderedDict() # type: OrderedDict
files = os.path.join(os.path.dirname(loader.name), node.value, '*.yaml') loc = os.path.join(os.path.dirname(loader.name), node.value)
for fname in glob.glob(files): for fname in _find_files(loc, '*.yaml'):
filename = os.path.splitext(os.path.basename(fname))[0] filename = os.path.splitext(os.path.basename(fname))[0]
mapping[filename] = load_yaml(fname) mapping[filename] = load_yaml(fname)
return mapping return mapping
def _include_dir_merge_named_yaml(loader: SafeLineLoader, def _include_dir_merge_named_yaml(loader: SafeLineLoader,
node: yaml.nodes.Node): node: yaml.nodes.Node) -> OrderedDict:
"""Load multiple files from directory as a merged dictionary.""" """Load multiple files from directory as a merged dictionary."""
mapping = OrderedDict() # type: OrderedDict mapping = OrderedDict() # type: OrderedDict
files = os.path.join(os.path.dirname(loader.name), node.value, '*.yaml') loc = os.path.join(os.path.dirname(loader.name), node.value)
for fname in glob.glob(files): for fname in _find_files(loc, '*.yaml'):
if os.path.basename(fname) == _SECRET_YAML: if os.path.basename(fname) == _SECRET_YAML:
continue continue
loaded_yaml = load_yaml(fname) loaded_yaml = load_yaml(fname)
@ -89,18 +98,18 @@ def _include_dir_merge_named_yaml(loader: SafeLineLoader,
def _include_dir_list_yaml(loader: SafeLineLoader, def _include_dir_list_yaml(loader: SafeLineLoader,
node: yaml.nodes.Node): node: yaml.nodes.Node):
"""Load multiple files from directory as a list.""" """Load multiple files from directory as a list."""
files = os.path.join(os.path.dirname(loader.name), node.value, '*.yaml') loc = os.path.join(os.path.dirname(loader.name), node.value)
return [load_yaml(f) for f in glob.glob(files) return [load_yaml(f) for f in _find_files(loc, '*.yaml')
if os.path.basename(f) != _SECRET_YAML] if os.path.basename(f) != _SECRET_YAML]
def _include_dir_merge_list_yaml(loader: SafeLineLoader, def _include_dir_merge_list_yaml(loader: SafeLineLoader,
node: yaml.nodes.Node): node: yaml.nodes.Node):
"""Load multiple files from directory as a merged list.""" """Load multiple files from directory as a merged list."""
files = os.path.join(os.path.dirname(loader.name), loc = os.path.join(os.path.dirname(loader.name),
node.value, '*.yaml') # type: str node.value) # type: str
merged_list = [] # type: List merged_list = [] # type: List
for fname in glob.glob(files): for fname in _find_files(loc, '*.yaml'):
if os.path.basename(fname) == _SECRET_YAML: if os.path.basename(fname) == _SECRET_YAML:
continue continue
loaded_yaml = load_yaml(fname) loaded_yaml = load_yaml(fname)

View file

@ -92,6 +92,27 @@ class TestYaml(unittest.TestCase):
doc = yaml.yaml.safe_load(file) doc = yaml.yaml.safe_load(file)
assert sorted(doc["key"]) == sorted(["one", "two"]) assert sorted(doc["key"]) == sorted(["one", "two"])
def test_include_dir_list_recursive(self):
"""Test include dir recursive list yaml."""
with tempfile.TemporaryDirectory() as include_dir:
file_0 = tempfile.NamedTemporaryFile(dir=include_dir,
suffix=".yaml", delete=False)
file_0.write(b"zero")
file_0.close()
temp_dir = tempfile.TemporaryDirectory(dir=include_dir)
file_1 = tempfile.NamedTemporaryFile(dir=temp_dir.name,
suffix=".yaml", delete=False)
file_1.write(b"one")
file_1.close()
file_2 = tempfile.NamedTemporaryFile(dir=temp_dir.name,
suffix=".yaml", delete=False)
file_2.write(b"two")
file_2.close()
conf = "key: !include_dir_list {}".format(include_dir)
with io.StringIO(conf) as file:
doc = yaml.yaml.safe_load(file)
assert sorted(doc["key"]) == sorted(["zero", "one", "two"])
def test_include_dir_named(self): def test_include_dir_named(self):
"""Test include dir named yaml.""" """Test include dir named yaml."""
with tempfile.TemporaryDirectory() as include_dir: with tempfile.TemporaryDirectory() as include_dir:
@ -111,6 +132,32 @@ class TestYaml(unittest.TestCase):
doc = yaml.yaml.safe_load(file) doc = yaml.yaml.safe_load(file)
assert doc["key"] == correct assert doc["key"] == correct
def test_include_dir_named_recursive(self):
"""Test include dir named yaml."""
with tempfile.TemporaryDirectory() as include_dir:
file_0 = tempfile.NamedTemporaryFile(dir=include_dir,
suffix=".yaml", delete=False)
file_0.write(b"zero")
file_0.close()
temp_dir = tempfile.TemporaryDirectory(dir=include_dir)
file_1 = tempfile.NamedTemporaryFile(dir=temp_dir.name,
suffix=".yaml", delete=False)
file_1.write(b"one")
file_1.close()
file_2 = tempfile.NamedTemporaryFile(dir=temp_dir.name,
suffix=".yaml", delete=False)
file_2.write(b"two")
file_2.close()
conf = "key: !include_dir_named {}".format(include_dir)
correct = {}
correct[os.path.splitext(
os.path.basename(file_0.name))[0]] = "zero"
correct[os.path.splitext(os.path.basename(file_1.name))[0]] = "one"
correct[os.path.splitext(os.path.basename(file_2.name))[0]] = "two"
with io.StringIO(conf) as file:
doc = yaml.yaml.safe_load(file)
assert doc["key"] == correct
def test_include_dir_merge_list(self): def test_include_dir_merge_list(self):
"""Test include dir merge list yaml.""" """Test include dir merge list yaml."""
with tempfile.TemporaryDirectory() as include_dir: with tempfile.TemporaryDirectory() as include_dir:
@ -127,6 +174,28 @@ class TestYaml(unittest.TestCase):
doc = yaml.yaml.safe_load(file) doc = yaml.yaml.safe_load(file)
assert sorted(doc["key"]) == sorted(["one", "two", "three"]) assert sorted(doc["key"]) == sorted(["one", "two", "three"])
def test_include_dir_merge_list_recursive(self):
"""Test include dir merge list yaml."""
with tempfile.TemporaryDirectory() as include_dir:
file_0 = tempfile.NamedTemporaryFile(dir=include_dir,
suffix=".yaml", delete=False)
file_0.write(b"- zero")
file_0.close()
temp_dir = tempfile.TemporaryDirectory(dir=include_dir)
file_1 = tempfile.NamedTemporaryFile(dir=temp_dir.name,
suffix=".yaml", delete=False)
file_1.write(b"- one")
file_1.close()
file_2 = tempfile.NamedTemporaryFile(dir=temp_dir.name,
suffix=".yaml", delete=False)
file_2.write(b"- two\n- three")
file_2.close()
conf = "key: !include_dir_merge_list {}".format(include_dir)
with io.StringIO(conf) as file:
doc = yaml.yaml.safe_load(file)
assert sorted(doc["key"]) == sorted(["zero", "one", "two",
"three"])
def test_include_dir_merge_named(self): def test_include_dir_merge_named(self):
"""Test include dir merge named yaml.""" """Test include dir merge named yaml."""
with tempfile.TemporaryDirectory() as include_dir: with tempfile.TemporaryDirectory() as include_dir:
@ -147,6 +216,32 @@ class TestYaml(unittest.TestCase):
"key3": "three" "key3": "three"
} }
def test_include_dir_merge_named_recursive(self):
"""Test include dir merge named yaml."""
with tempfile.TemporaryDirectory() as include_dir:
file_0 = tempfile.NamedTemporaryFile(dir=include_dir,
suffix=".yaml", delete=False)
file_0.write(b"key0: zero")
file_0.close()
temp_dir = tempfile.TemporaryDirectory(dir=include_dir)
file_1 = tempfile.NamedTemporaryFile(dir=temp_dir.name,
suffix=".yaml", delete=False)
file_1.write(b"key1: one")
file_1.close()
file_2 = tempfile.NamedTemporaryFile(dir=temp_dir.name,
suffix=".yaml", delete=False)
file_2.write(b"key2: two\nkey3: three")
file_2.close()
conf = "key: !include_dir_merge_named {}".format(include_dir)
with io.StringIO(conf) as file:
doc = yaml.yaml.safe_load(file)
assert doc["key"] == {
"key0": "zero",
"key1": "one",
"key2": "two",
"key3": "three"
}
FILES = {} FILES = {}