def __init__(self, indict, infile, stout):
        """This class creates a config object from a loaded config file - infile which should be passed in as a list of lines.
        It parses this file according to the config specification it is passed as infile - which specifies the keywords expected and the type of value expected.
        stout is an output object with a method stout.out('error line') for printing errors to.
        
        The value for each keyword in indict defines the type of value expected in the infile.
        Valid values are '' - a string value, '[]' multiple string values.
        
        Keyword and value can be escaped between ' ' or " " and be seperated by a space or = or : and other whitespace.
        A line can end with a trailing comment - which is marked by a # or a ;
        Lines of purely whitespace or only a comment will be ignored.

        The following are all valid entries :
        keyword1 value1         # trailing comment
        'keyword2' : 'value2'   ; and another trailing comment
        "keyword3" = [value3, 'value4',"value5"]

        The indict definition for the above config file would be a dictionary of the following construction :
        indict = { 'keyword1' : '', 'keyword2' :'', 'keyword3' : '[]' }
        There is a function called buildconfig in this distribution
        It builds a valid indict from a list of keywords - if a keyword ends in '[]' it makes it a multiple value.
        The valid construction then becomes :
        indict = buildconfig( ['keyword1', 'keyword2', 'keyword3[]'] ) 
    
        The parsed config is contained in object.config as a dictionary, keywords in indict that
        it failed to find valid entries for in infile will be set to None
        
        The config can be modified in place and then values written to a new file using the
        newfile = object.write() method or changed values written into an existing file using the
        newfile = object.update(infile) method.
        Both methods return the config files as a list of lines.
        
        A config file (infile) can have more parameters than the indict (extra ones will be ignored).
        This allows for multiple configs to be stored in a single file - keywords must be unique per file though.
        
        The created config dictionary object.config has the following values for each valid keyword :
        'value' and 'comment'. Comment is any trailing comment on the same line as the keyword.
        The comment is also written back by the write and update methods.
        
        If a keyword appears twice in a config file an error is written to stout - and the first value used.
        If an badly constructed or invalid line is found then an error is printed and the line ignored.
        
        If the value required is '[]' - multiple values - then the 'value' is returned as a list of strings.
        
        A single value cannot start with a '['.
        A value surrounded by '[ ]' will be assumed to be a multiple value entry. (even if there is none or only one value inside).

        The original parsed config is also preserved in object.defaultconfig so that the config produced can be amended and then reverted
        back to the original if desired (using a deepcopy operation).
        
        The indict (config spec) is also preserved as object.configspec
        This is so it can be used for writing out defaults by object.write() etc - when the original value is missing and
        ConfigObj has preserved a None.

        See the source code for the full specs for the write() method (which can take a writeorder
        paramater) and update(infile) method.
        There is also a useful getval(param) method which can be used as a handy shortcut for retrieving values.
        """

# make sure all the values are lowercase
        newdict = {}
        self.config = {}
        self.stout = stout # save the output object - this must be valid across the life of the config object
        for member in indict:
            newdict[member.lower()] = indict[member]
            self.config[member.lower()] = None       # values we fail to find later will be None.
        
        configkeys = newdict.keys()

        for line in infile:
            origline=line.strip()
            origparam = self.findparam(line)
            if not origparam:       # this line was whitespace or comment or invalid
                continue
            param=origparam.lower()
            if param not in newdict:    # a valid parameter - but not one we're looking for in this config object. Allows larger config files with multiple configs in - keywords must be unique per file though
                continue
            if param not in configkeys:
                stout.out('param defined twice : ')
                stout.out(origparam+'\n\n')
                continue
            #exception ? - parameter defined twice....... currently first parameter is used and second discarded.

            test = line.find(origparam)
            test += len(param)
            line = line[test+1:]
            value = self.findvalue(line)    # removes the dividing character between the parameter and value
            value, comment = self.removecomment(value, origline)
            if value == None:   # if the value was invalid
                continue
            value = self.matchvalue(value, newdict[param])  # this tests the value given is of the right type - and if a multipel value is required... returns it as a string.
            if value == None:      # is the value of the right type (single or multiple)
                stout.out('Wrong type, or invalid value in line : \n')
                stout.out(origline + '\n')
                stout.out('Expected value of type :\n')
                if newdict[param] == '[]':
                    paramtype = "['','',''] - Multiple Values"
                else:
                    paramtype = "'value' - Single Value" 
                stout.out(paramtype+'\n\n')
                continue                
            configkeys.remove(param)
            self.config[param] = {'value' : value, 'comment' : comment }
            
        self.defaultconfig = deepcopy(self.config)
        self.configspec = indict
This class creates a config object from a loaded config file - infile which should be passed in as a list of lines.
        It parses this file according to the config specification it is passed as infile - which specifies the keywords expected and the type of value expected.
        stout is an output object with a method stout.out('error line') for printing errors to.
        
        The value for each keyword in indict defines the type of value expected in the infile.
        Valid values are '' - a string value, '[]' multiple string values.
        
        Keyword and value can be escaped between ' ' or " " and be seperated by a space or = or : and other whitespace.
        A line can end with a trailing comment - which is marked by a # or a ;
        Lines of purely whitespace or only a comment will be ignored.

        The following are all valid entries :
        keyword1 value1         # trailing comment
        'keyword2' : 'value2'   ; and another trailing comment
        "keyword3" = [value3, 'value4',"value5"]

        The indict definition for the above config file would be a dictionary of the following construction :
        indict = { 'keyword1' : '', 'keyword2' :'', 'keyword3' : '[]' }
        There is a function called buildconfig in this distribution
        It builds a valid indict from a list of keywords - if a keyword ends in '[]' it makes it a multiple value.
        The valid construction then becomes :
        indict = buildconfig( ['keyword1', 'keyword2', 'keyword3[]'] ) 
    
        The parsed config is contained in object.config as a dictionary, keywords in indict that
        it failed to find valid entries for in infile will be set to None
        
        The config can be modified in place and then values written to a new file using the
        newfile = object.write() method or changed values written into an existing file using the
        newfile = object.update(infile) method.
        Both methods return the config files as a list of lines.
        
        A config file (infile) can have more parameters than the indict (extra ones will be ignored).
        This allows for multiple configs to be stored in a single file - keywords must be unique per file though.
        
        The created config dictionary object.config has the following values for each valid keyword :
        'value' and 'comment'. Comment is any trailing comment on the same line as the keyword.
        The comment is also written back by the write and update methods.
        
        If a keyword appears twice in a config file an error is written to stout - and the first value used.
        If an badly constructed or invalid line is found then an error is printed and the line ignored.
        
        If the value required is '[]' - multiple values - then the 'value' is returned as a list of strings.
        
        A single value cannot start with a '['.
        A value surrounded by '[ ]' will be assumed to be a multiple value entry. (even if there is none or only one value inside).

        The original parsed config is also preserved in object.defaultconfig so that the config produced can be amended and then reverted
        back to the original if desired (using a deepcopy operation).
        
        The indict (config spec) is also preserved as object.configspec
        This is so it can be used for writing out defaults by object.write() etc - when the original value is missing and
        ConfigObj has preserved a None.

        See the source code for the full specs for the write() method (which can take a writeorder
        paramater) and update(infile) method.
        There is also a useful getval(param) method which can be used as a handy shortcut for retrieving values.
        
recipes/Python/259175_ConfigObj__Lightweight_Config_File/recipe-259175.py