Online Shopping : Computers : Programming : Languages : Python : Snippets

+ Search
Add Entry AlertManage Folder Edit Entry Add page to http://del.icio.us/
Did You Find This Entry Useful?

5 of 8 people (63%) answered Yes
Recently 4 of 6 people (67%) answered Yes

Entry

Utilities for reading config files (was: Reval builtin)

Mar 24th, 2009 17:55
chat alarab, games games, Nathan Wallace, Hans Nowak, Snippet 352, Scott Cotton


"""
Packages: text
"""
"""
here's another config file utility.  i know that some
pythoners won't like this, but it uses shell-like
'$variable' expansion.  i personally like this interpolation
syntax in config files (but not in programming languages). 
enjoy,
scott
"""
"""
Utilities for reading config files.
functions:
  readcf(path) - return a dict containing the vars and vals
  readtxt(txt) - same as above but for text instead of file
  dict2cf(dict, templ=None) - see function doc string
"""
import string
class SyntaxErrorCF(StandardError):
    def __init__(self, *args):
        self.args = args
#
# "eval" for config files
# given the rhs of an assignment as plain text,
# figure out what it's python native value is.
#
def figger(rhs):
    rhs = string.strip(rhs)
    if rhs == "None":
        return None
    elif rhs == 'yes' or rhs == 'on': # boolean
        return 1
    elif rhs == 'no' or rhs == 'off': # more booleans
        return 0
    elif len(rhs) >= 6 and rhs[:3] in ('"""', "'''") \
         and rhs[:3] == rhs[-3:]:# TQS
        return rhs[3:-3]
    elif rhs[0] in ("'", '"') and rhs[0] == rhs[-1]: # plain string
        return rhs[1:-1]
    elif rhs[0] == '[' and rhs[-1] == ']': # list (of strings only)
        list = []
        inner = rhs[1:-1]
        for s in string.split(inner, ","): # items in the list
            s = string.strip(s)
            if not s:
                continue
            if s == '""' or s == "''": # empty string
                list.append("")
            elif len(s) >= 6 \
                 and s[:3] in ('"""', "'''") \
                 and s[-3:] == s[:3]: # triple quoted string
                list.append(s[3:-3])
            elif s[0] in ("'", '"') and s[0] == s[-1]: # regular string
                list.append(s[1:-1])
            # since lists can only be lists of strings, we allow bare 
words	
            else: 
                list.append(s) 
        return list
    else: # the only remaining value is an int.
        try:
            return string.atoi(rhs)
        except ValueError: # oh well, the var get's None.
            return rhs
#
# return a dict containing non variable substituted key-values
# from the text of a config file.  
#
def getassignments(text):
    linect = 0
    assgns = {}
    var = None 
    check_tqs = 0 # is it a triple quoted string?
    text = string.replace(text, "\\\n", "")
    for line in string.split(text, "\n"):
        stripped = string.strip(line)
        linect = linect + 1
        if check_tqs: # check for the end of a triple quoted string
            rhs = rhs + "\n" + line
            if len(stripped) >= 3 and stripped[-3:] == check_tqs:
                # the variable check_tqs contains either ''' or """ as
                # a string literal.
                assgns[var] = figger(rhs)
                var, rhs = None, None
                check_tqs = 0
        elif not stripped or stripped[0] == '#':
            # we try to wrap everything we know up from a previous 
assignment
            if var is not None and rhs is not None:
                if not string.strip(rhs):
                    raise SyntaxErrorCF(linect -1, "%s = " % (var))
                assgns[var] = figger(rhs)
            # reinitialize the variables.
            rhs = None
            var = None
            continue
        elif line[0] not in " \t":
            if var is not None and rhs is not None:
                if not string.strip(rhs):
                    raise SyntaxErrorCF(linect -1, "%s = " % (var))
                assgns[var] = figger(rhs)
            var, rhs = None, None
            if string.find(line, "=") == -1: # not an assignment
                raise SyntaxErrorCF(linect, line)
            lhs, rhs = string.split(line, "=", 1)
            var = string.strip(lhs)
            if len(string.strip(rhs)) >= 3  \
               and string.strip(rhs)[:3] in ('"""', "'''"):
                check_tqs = string.strip(rhs)[:3]
                var = string.strip(lhs)
        else:
            if var is None:
                raise SyntaxErrorCF(linect, line)
            rhs = rhs + " " + line
    # here we just wrap up what we found out iterating over the
    # lines in the text one last time.
    if var is not None and rhs is not None:
        if not string.strip(rhs):
            raise SyntaxErrorCF(linect -1, "%s = " % (var))
        assgns[var] = figger(rhs)
    return assgns
#
# globally replace variables with values (eg $prefix)
#
def global_replace(text, dict):
    for k, v in dict.items():
        text = string.replace(text, '$' + k, v)
    return text
#
# replace variables defined earlier in a config file
#
def internal_replace(dict, replacements):
    for var, rvars in replacements.items():
        for rvar in rvars:
            cval = dict[var]
            if type(cval) is type([]):
                l = []
                for s in cval:
                    l.append(string.replace(s, '$' + rvar, dict[rvar]))
                dict[var] = l
            elif type(cval) is type(""):
                dict[var] = string.replace(cval, '$' + rvar, dict
[rvar])
    return dict
#
# top level function
#
def readcf(file, global_repl=None, int_repl=None):
    """
    given a path to a file, and any replacements that need to
    be made, return a dictionary containing the variables
    in the config file as keys and the values as values.
    """
    text = open(file).read()
    text = string.replace(text, "\\\n", "") # get rid of \ cont'd lines
    if global_repl is not None:
        text = global_replace(text, global_repl)
    d = getassignments(text)
    if int_repl is not None:
        d = internal_replace(d, int_repl)
    return d
def readtxt(txt, global_repl=None, int_repl=None):
    """
    reads the text of a cf file into a dict and return the dict.
    """
    txt = string.replace(txt, "\\\n", "")
    if global_repl is not None:
        txt = global_replace(txt, global_repl)
    d = getassignments(text)
    if int_repl is not None:
        d = internal_replace(d, int_repl)
    return d
def dict2cf(dict, templ=None):
    """
    given a dictionary keyed by the configuration variables
    and whose values are a 3-tuple of (var-value, descr, vtype)
    where vtype is one of 'str', 'int', 'bool' or 'list', return
    text suitable for writing a config file.  If optional second arg
    'templ' is passed, then transpose the values to the template,
    which should be a string with 1 %(<varname>)s entry per variable
    """
    txtdict = {}
    for k, (val, vtype, descr) in dict.items():
        valstr = ""
        descr = string.rstrip(descr)
        entry = descr + "\n#\n" + k + " = "
        if vtype == "str":
            if string.find(val, "\n") != -1:
                valstr = '"""\\\n' + val + '"""'
            elif string.find(val, '"') != -1:
                valstr = "'" + val + "'"
            else:
                valstr = '"' + val + '"'
        elif vtype == "bool":
            if val:
                valstr = "yes"
            else:
                valstr = "no"
        elif vtype == "int":
            valstr = "%d" % (val)
        elif vtype == "list":
            if not val:
                valstr = "[]"
            else:
                spaces = " " * (len(k) + 4)
                valstr = "["
                for item in val:
                    if string.find(item, "\n") != -1:
                        valstr = "%s%s%s%s,\n%s" % (valstr,
                                                   '"""\\\n',
                                                   item,
                                                   '"""',
                                                   spaces)
                    elif string.find(item, '"') != -1:
                        valstr = "%s%s%s%s,\n%s" % (valstr,
                                                   "'",
                                                   item,
                                                   "'",
                                                   spaces)
                    else:
                        valstr = "%s%s%s%s,\n%s" % (valstr,
                                                   '"',
                                                   item,
                                                   '"',
                                                   spaces)
                valstr = valstr[:-2 - len(spaces)] + "]"
        else:
            print "weird:", k, val, descr, vtype
        valstr = valstr + "\n\n"
        txtdict[k] = entry + valstr
    if templ:
        return templ % txtdict
    else:
        vars = txtdict.keys()
        vars.sort()
        res = ""
        for v in vars:
            res = res + txtdict[v]
        return res
http://www.ksa-123.com
http://www.ksa-2000.com
http://www.chat-kuwait.com
http://www.vip-kuwait.com
http://www.chat-3rb.com
http://www.vip-3rb.com
http://www.3rb-chat.com
http://www.vipgulf.com
http://www.chat-gulf.com
http://www.vip-gulf.com