#! /usr/bin/env python3 '''\ Create top level variables based on a higher level config $Id$ ''' from __future__ import print_function import sys import io import argparse import package_info import re from feedback import die, warn from configobj import Section from expconfig import ConfigObj # # Helper routines # # Get base config keys that contain globals global_matcher = re.compile(r'\$(\w+)') def get_globals(config, result): for key in config.sections: get_globals_section(config[key], key, {s: '' for s in config.scalars}, result) def get_globals_section(section, path, global_keys, result): all_keys = global_keys.copy() all_keys.update({s: path for s in section.scalars}) for key in section.scalars: value = section[key] if not isinstance(value, (list, tuple)): match = global_matcher.match(value) if match: global_key = match.group(1) if True: ### global_key in global_keys: result[path+'/'+key] = (global_key, all_keys.get(global_key, '')) for key in section.sections: get_globals_section(section[key], path+'/'+key, all_keys, result) def eval_globals(config, global_dict): for key in config.sections: eval_globals_section(config[key], key, global_dict) def eval_globals_section(section, path, global_dict): for key in section.scalars: value = section[key] if not isinstance(value, (list, tuple)): global_key = path+'/'+key if global_key in global_dict: global_var, global_path = global_dict[global_key] global_section = section.main if global_path: for name in global_path.split('/'): global_section = global_section[name] if global_var in global_section: if global_section[global_var] != value: warn("global '%s' at '%s' already set to '%s', " "keeping '%s' at '%s'", global_var, global_path, global_section[global_var], global_key, value) else: section[key] = '$' + global_var else: global_section[global_var] = value section[key] = '$' + global_var for key in section.sections: eval_globals_section(section[key], path+'/'+key, global_dict) # # Main routine # # Check command line command_line = argparse.ArgumentParser(description=__doc__.split('\n', 1)[0]) # TODO: print differences option, ... command_line.add_argument('--indent-string', default=' ', help='set indent string [%(default)s]') command_line.add_argument('--inline-comments' , '-c', action='store_true', help='compact white space before inline comments' ' (BETA)') command_line.add_argument('--trailing-space' , '-t', action='store_true', help='remove white space at end of lines') command_line.add_argument('base_config', help='name of high level config') command_line.add_argument('config', help='name of low level/expanded config') command_line.add_argument('-V', '--version', action='version', version=package_info.version) args = command_line.parse_args() # File handling try: base_config = ConfigObj(args.base_config, file_error=True) config = ConfigObj(args.config, file_error=True) except IOError as error: die(error.args[0]) global_dict = {} get_globals(base_config, global_dict) ### for k, v in global_dict.items(): ### print(k+':', v) ### exit() eval_globals(config, global_dict) ### exit() # Ready to roll out lines = io.BytesIO() config.write(lines) lines.seek(0) for line in io.TextIOWrapper(lines): if args.inline_comments: line = re.sub(r' = (.*?) #', r' = \1 #', line) if args.trailing_space: print(line.rstrip()) else: print(line, end='')