Last active
September 10, 2020 16:49
-
-
Save taikedz/a425825e8dba91a3f8b42038e2bf1fa0 to your computer and use it in GitHub Desktop.
Load a YAML file with references to other YAML files
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import yaml | |
import os | |
class RecursiveYamlLoad(Exception): | |
pass | |
def mingw_path(path): | |
#TODO not yet finished | |
""" Running Python in MinGW (incl Git Bash) causes abspath() to mangle the path | |
If a MinGW absolute path "/c/Program Files/..." is passed to abspath() it returns "C:\\c\\Progam Files\\..." | |
If we are in MinGW and we get a regular Windows path, it needs converting | |
""" | |
if ( # Test for abspath in mingw having mangled the path between Windows and LFH | |
path[1:2] == ':' and path[0:1] != '/' and # C:... | |
path[2:3].upper() == path[0:1] # C:\c\... | |
): | |
path = path[2:] | |
#if path | |
def path_of(base_path, file_path=None): | |
path = os.path.abspath(base_path) | |
if file_path: | |
path = os.path.pathsep.join(path, file_path) | |
path = os.path.abspath(path) # Smash relative paths from file_path | |
#path = mingw_path(path) | |
return path | |
def load_yaml(filepath, recurse=False, starter="@:"): | |
""" Load a YAML file | |
If recurse is True, then look for strings starting with `starter` substring | |
`starter` substring indicates the identifier of an external reference | |
""" | |
yaml_data = __load_yaml(filepath, recurse, starter, os.path.dirname(filepath), []) # FIXME path duplication here | |
return yaml_data | |
def __load_yaml(filepath, recurse, starter, base_dir, load_chain): | |
""" Internalised, to separate from the management of circular references | |
base_dir | |
""" | |
filepath = path_of(base_dir, filepath) | |
if filepath in load_chain: | |
raise RecursiveYamlLoad(f"Loop on {filename} from : {' > '.join(load_chain)}") | |
load_chain.append(filename) | |
with open(filepath, 'r') as fh: | |
my_yaml = yaml.full_load(fh) | |
if recurse: | |
__drilldown_yaml(my_yaml, starter, os.path.dirname(filepath), load_chain) | |
return my_yaml | |
def __drilldown_yaml(yaml_data, starter, base_dir, load_chain): | |
""" In-place loader of YAML external references | |
""" | |
starter_length = len(starter) | |
for k,v in __pairs(yaml_data): | |
if type(v) is str and v.startswith(starter): | |
filename = v[starter_length:] # Needs to be relative to originating YAML file | |
external_data = __load_yaml(filename, recurse=True, starter=starter, load_chain=load_chain) | |
yaml_data[k] = external_data | |
elif type(v) in [list, dict]: | |
__drilldown_yaml(v, starter, base_dir, load_chain) | |
def __pairs(thing): | |
""" Return the appropriate iterator for dict or list | |
""" | |
if type(thing) is dict: | |
return thing.items() | |
elif type(thing) is list: | |
return enumerate(thing) | |
else: | |
raise ValueError(f"Not a list or dict {type(thing)}") | |
def main(yamlfile): | |
yaml_data = load_yaml(yamlfile, recurse=True) | |
pp = pprint.PrettyPrinter() | |
pp.pprint(yaml_data) | |
if __name__ == "__main__": | |
import sys | |
import pprint | |
main(sys.argv[1]) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment