Created
July 27, 2021 18:42
-
-
Save mzpqnxow/1e5ef0ae64d651173ea3e5b0bbd85b7d to your computer and use it in GitHub Desktop.
Python module variables to YaML
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
"""Enumerate and export a bunch of structured or scalar objects from a Python module to YaML | |
This enumerates everything in the module in the globals namespace automatically, skipping | |
a few things (like things starting with `__`) | |
Useful when you have a ton of constants (including some that are lists, tuples, dicts, etc) in a | |
Python module and you want to put them into YaML to maintain separately from the Python code. It's | |
not a difficult task, but it becomes painful when there are 10-20 (or more) different objects. This | |
just automates it | |
By default, each object gets its own file | |
If monolithic=True, each gets its own file | |
Some of the export logic was stolen from the Internet | |
Requires ruamel as PyYaML is not flexible enough to emit the type of YaML format I prefer | |
- AG | |
""" | |
from os.path import join as join_path | |
from collections import defaultdict | |
from sys import stderr | |
from typing import Any, Optional | |
import ruamel.yaml as yaml | |
def export_globals(monolithic: bool = False, deflate_int: bool = True): | |
"""To write to a single file, use monolithic=True""" | |
ignore_globals = ('argv', ) | |
ignore_class_name = ('module', ) | |
accept_types = (set, tuple, list, dict, OrderedDict) | |
out_map = defaultdict(dict) | |
for k, v in globals().items(): | |
if k.startswith('_') or v.__class__.__name__ in ignore_class_name or k in ignore_globals: | |
stderr.write(f'Skipping global {k}') | |
continue | |
if not isinstance(v, accept_types): | |
continue | |
if isinstance(v, (set, tuple)): | |
# YaML can't serialize set or tuple | |
v = list(v) | |
if isinstance(v, list) and any(map(lambda t: isinstance(t, (IPv4Address, IPv4Network)), v)): | |
# If it's a list, it may have ipaddress.ip_address or ipaddress.ip_network values | |
# Convert to strings | |
v = [o.compressed for o in v] | |
print(f'Converted {k} to string IPv4 addresses') | |
if deflate_int is True: | |
print('Deflate') | |
if isinstance(v, list): | |
if all(map(lambda x: isinstance(x, int), v)): | |
print(f'deflating: {v}') | |
# One element list, so it can be added to as lines if desired | |
v = deflate_int_list_to_string_range(v, as_list=True) | |
print(v) | |
input('deflated?') | |
if monolithic is True: | |
out_map['root'][k] = v | |
else: | |
to_yaml(v, k, section=k) | |
# print(f'Wrote {k} ...') | |
if monolithic is True: | |
to_yaml(out_map, 'monolithic.yml') | |
def to_yaml(obj: Any, filename: str, section: Optional[str] = None, outdir: Optional[str] = None) -> None: | |
if not filename.endswith('.yml') and not filename.endswith('.yaml'): | |
filename += '.yml' | |
if outdir is not None: | |
filename = join_path(outdir, filename) | |
if isinstance(obj, defaultdict): | |
obj = dict(obj) | |
with open(filename, mode='w') as outfd: | |
if section is not None: | |
obj = {section: obj} | |
yaml_obj = yaml.YAML() | |
yaml_obj.indent(sequence=4, offset=2) | |
yaml_obj.dump(obj, outfd) | |
SOME_GLOBAL_STUFF = { | |
'a': 1, | |
'b': 2, | |
'c': 3, | |
} | |
OTHER_GLOBAL_STUFF = (1, 2, 3, 4, 5) | |
def main(): | |
export_globals(monolithic=True) | |
if __name__ == '__main__': | |
main() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment