Skip to content

Instantly share code, notes, and snippets.

@taikedz
Last active September 10, 2020 16:49
Show Gist options
  • Save taikedz/a425825e8dba91a3f8b42038e2bf1fa0 to your computer and use it in GitHub Desktop.
Save taikedz/a425825e8dba91a3f8b42038e2bf1fa0 to your computer and use it in GitHub Desktop.
Load a YAML file with references to other YAML files
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