Skip to content

Instantly share code, notes, and snippets.

@mzpqnxow
Created July 27, 2021 18:42
Show Gist options
  • Save mzpqnxow/1e5ef0ae64d651173ea3e5b0bbd85b7d to your computer and use it in GitHub Desktop.
Save mzpqnxow/1e5ef0ae64d651173ea3e5b0bbd85b7d to your computer and use it in GitHub Desktop.
Python module variables to YaML
"""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