codes_configurator.py 4.43 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47
#!/usr/bin/python2.7

import re
import sys
import os
import imp
import argparse
from collections import Sequence

def main():
    args = parse_args()
    
    # load the template file
    tstr = open(args.template).read()

    # load the module
    mod = import_from(args.substitute_py)
    
    # checks - make sure cfields is set and is the correct type 
    if "cfields" not in mod.__dict__:
        raise TypeError("Expected cfields to be defined in " + args.substitute_py)
    elif not \
            (isinstance(mod.cfields, Sequence) and \
            isinstance(mod.cfields[0][0], str) and \
            isinstance(mod.cfields[0][1], Sequence)):
        raise TypeError("cfields in incorrect format, see usage")

    # get list of iterators, replacement input, and output labels
    labels      = [ k[0] for k in mod.cfields]
    iterables   = [ k[1].__iter__() for k in mod.cfields]
    # use two dicts to maintain type information when printing (don't want
    #  quotes to appear in log file)
    replace_map =     { k[0] : None for k in mod.cfields }

    # print the header to the log
    if args.log != None:
        flog = open(args.log, "w")
    else:
        flog = open(os.devnull, "w")
    flog.write("# format:\n# <config index>")
    for l in labels:
        flog.write(" <" + l + ">")
    flog.write("\n")

    # generate initial configuration 
    for i,k in enumerate(labels):
        v = iterables[i].next()
48
        replace_map[k] = v
49 50 51

    # main loop 
    ct = 0
52
    while True:
53 54 55 56 57 58 59 60 61 62
        # generate new configuration
        new_config = replace_many(tstr, replace_map)
        fname = template+"."+str(ct) if args.output_prefix==None else \
            args.output_prefix+"."+str(ct)
        with open(fname, "w") as fout:
            fout.write(new_config)

        # print the configuration to the log
        flog.write(str(ct))
        for i,k in enumerate(labels):
63 64
            if isinstance(replace_map[k], str):
                flog.write(' "' + replace_map[k] + '"')
65
            else:
66
                flog.write(" " + str(replace_map[k]))
67 68 69 70 71 72
        else:
            flog.write('\n')

        # generate the next config
        for i,k in enumerate(labels):
            try:
73
                # update current iterable and finish
74
                v = iterables[i].next()
75
                replace_map[k] = v
76 77 78
                ct += 1
                break
            except StopIteration:
79
                # reset the current iterable and set to first element
80 81
                iterables[i] = mod.cfields[i][1].__iter__()
                v = iterables[i].next()
82
                replace_map[k] = v
83
        else:
84 85
            # last iterable has finished, have generated full set
            break
86 87 88 89 90 91 92 93 94 95 96 97 98 99

# import a python file (assumes there is a .py suffix!!!)
def import_from(filename):
    path,name = os.path.split(filename)
    name,ext  = os.path.splitext(name) 

    fmod, fname, data = imp.find_module(name, [path])
    return imp.load_module(name,fmod,fname,data)

# given a string and a set of substitution pairs, 
#   perform a single pass replace 
# st is the string to perform substitution on
# kv_pairs is a dict or a sequence of sequences.
# kv_pairs examples:
100 101 102 103
# { a:b, c:d }
# [[a,b],[c,d]]
# [(a,b),(c,d)]
# ((a,b),(c,d))
104
def replace_many(st, kv_pairs):
105 106
    rep_dict = {}
    # dict-ify and string-ify the input
107
    if isinstance(kv_pairs, dict):
108 109
        for k in kv_pairs:
            rep_dict[k] = str(kv_pairs[k])
110
    elif isinstance(kv_pairs, Sequence) and isinstance(kv_pairs[0], Sequence):
111 112
        for k in kv_pairs:
            rep_dict[k[0]] = str(kv_pairs[k[1]])
113 114
    else:
        raise TypeError("Expected dict or sequence of sequences types")
115 116 117 118

    pat = re.compile("|".join([re.escape(k) for k in rep_dict]))

    return pat.sub(lambda match: rep_dict[match.group(0)], st)
119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136

def parse_args():
    parser = argparse.ArgumentParser()
    parser.add_argument("template",\
            help="template file with replace tokens")
    parser.add_argument("substitute_py",\
            help='python file defining "cfields" variable consisting of'\
                 ' elements of the form ( replacement_token, [replacements...])')
    parser.add_argument("-l", "--log",\
            help="log file to write parameterizations to") 
    parser.add_argument("-o", "--output_prefix",\
            help="prefix to output generated configuration files to "
                 "(default: the configuration index appended to the template name)")
    return parser.parse_args()


if __name__ == "__main__":
    main()