r'''Return a list of dictionaries formated as a comma seperated values
        (CSV) list in a string.

        Each entry is on one line. By default, each value is seperated by
        commas and each entry is followed by a newline ('\n').

        By default, RFC4180 is followed. See:

           http://tools.ietf.org/html/rfc4180

        The RFC has considerable detail describing the format.

        Note regarding certain RFC section 2 sub 4: there is no default
        behavior specified if the last field on a line is empty and thus
        creating a 'trailing comma'. This routine handles it by quoting
        the final field. The first example below demonstrates this for the
        entry with 'name' of 'Larry' and a missing 'income' field. The
        missing field is shown as "" at the end of the line.
           
        Missing keys and/or values of None are simply blank.

        Example of use:

        >>> test = [
        ...    {"name": "Jim, Phd",         "age": 18  , "income": 93000, "order": 2},
        ...    {"name": "Larry",            "age": None,                  "order": 3},
        ...    {"name": "Joe",              "age": 20  , "income": 15000, "order": 1},
        ...    {"name": "B \"Zip\" O'Tool", "age": 19  , "income": 29000, "order": 4},
        ... ]
        >>> print PLOD(test).returnCSV()   # doctest: +NORMALIZE_WHITESPACE
        age,order,name,income
        18,2,"Jim, Phd",93000
        ,3,Larry,""
        20,1,Joe,15000
        19,4,"B ""Zip"" O'Tool",29000
        <BLANKLINE>
        >>> print PLOD(test).returnCSV(limit=3, omitHeaderLine=True, quoteChar="'", eolChars='\n')
        '18','2','Jim, Phd','93000'
        '','3','Larry',''
        '20','1','Joe','15000'
        <BLANKLINE>
        >>> print PLOD(test).returnCSV(keys=["name", "age"], quoteChar='"', eolChars='\n')
        "name","age"
        "Jim, Phd","18"
        "Larry",""
        "Joe","20"
        "B ""Zip"" O'Tool","19"
        <BLANKLINE>
        
        :param keys:
           If the 'keys' parameter is passed a list of keys, then only those
           keys are returned. The order of keys in the list is retained. If a
           key is not found in an entry (or in *any* entry), that is not an error
           condition. Those entries simply have an empty value for that position.

           NOTE: use this parameter if the order of the keys is critical. Order
           is not guaranteed otherwise.
        :param limit:
           A number limiting the quantity of entries to return. Defaults to
           False, which means that the full list is returned.
        :param omitHeaderLine:
           If set to True, the initial line of text listing the keys
           is not included. Defaults to False.
        :param quoteChar:
           If set to anything (including a single quote), then all fields will
           be surrounded by the quote character.
        :param eolChars:
           These are the characters inserted at the end of each line. By default
           they are CRLF ('\r\n') as specified in RFC4180. To be more pythonic
           you could change it to '\n'.
        :return:
           A string containing a formatted textual representation of the list
           of dictionaries.

def returnCSV(self, keys=None, limit=False, omitHeaderLine=False, quoteChar=None, eolChars='\r\n'):
        r'''Return a list of dictionaries formated as a comma seperated values
        (CSV) list in a string.

        Each entry is on one line. By default, each value is seperated by
        commas and each entry is followed by a newline ('\n').

        By default, RFC4180 is followed. See:

           http://tools.ietf.org/html/rfc4180

        The RFC has considerable detail describing the format.

        Note regarding certain RFC section 2 sub 4: there is no default
        behavior specified if the last field on a line is empty and thus
        creating a 'trailing comma'. This routine handles it by quoting
        the final field. The first example below demonstrates this for the
        entry with 'name' of 'Larry' and a missing 'income' field. The
        missing field is shown as "" at the end of the line.
           
        Missing keys and/or values of None are simply blank.

        Example of use:

        >>> test = [
        ...    {"name": "Jim, Phd",         "age": 18  , "income": 93000, "order": 2},
        ...    {"name": "Larry",            "age": None,                  "order": 3},
        ...    {"name": "Joe",              "age": 20  , "income": 15000, "order": 1},
        ...    {"name": "B \"Zip\" O'Tool", "age": 19  , "income": 29000, "order": 4},
        ... ]
        >>> print PLOD(test).returnCSV()   # doctest: +NORMALIZE_WHITESPACE
        age,order,name,income
        18,2,"Jim, Phd",93000
        ,3,Larry,""
        20,1,Joe,15000
        19,4,"B ""Zip"" O'Tool",29000
        <BLANKLINE>
        >>> print PLOD(test).returnCSV(limit=3, omitHeaderLine=True, quoteChar="'", eolChars='\n')
        '18','2','Jim, Phd','93000'
        '','3','Larry',''
        '20','1','Joe','15000'
        <BLANKLINE>
        >>> print PLOD(test).returnCSV(keys=["name", "age"], quoteChar='"', eolChars='\n')
        "name","age"
        "Jim, Phd","18"
        "Larry",""
        "Joe","20"
        "B ""Zip"" O'Tool","19"
        <BLANKLINE>
        
        :param keys:
           If the 'keys' parameter is passed a list of keys, then only those
           keys are returned. The order of keys in the list is retained. If a
           key is not found in an entry (or in *any* entry), that is not an error
           condition. Those entries simply have an empty value for that position.

           NOTE: use this parameter if the order of the keys is critical. Order
           is not guaranteed otherwise.
        :param limit:
           A number limiting the quantity of entries to return. Defaults to
           False, which means that the full list is returned.
        :param omitHeaderLine:
           If set to True, the initial line of text listing the keys
           is not included. Defaults to False.
        :param quoteChar:
           If set to anything (including a single quote), then all fields will
           be surrounded by the quote character.
        :param eolChars:
           These are the characters inserted at the end of each line. By default
           they are CRLF ('\r\n') as specified in RFC4180. To be more pythonic
           you could change it to '\n'.
        :return:
           A string containing a formatted textual representation of the list
           of dictionaries.
        '''
        result = ""
        if quoteChar:
            quoteAll=True
        else:
            quoteAll=False
        # we limit the table if needed
        used_table = self.table
        if limit:
            used_table = []
            for i in range(limit):
                if len(self.table)>i:
                    used_table.append(self.table[i])
        # we locate all of the attributes
        if keys:
            attr_list = keys
        else:
            attr_list = []
            for row in used_table:
                for key in row:
                    if not key in attr_list:
                        attr_list.append(key)
        # now we do the pretty print
        if not omitHeaderLine:
            if quoteAll:
                result += quoteChar
                temp = quoteChar+","+quoteChar
                result += temp.join(attr_list)
                result += quoteChar
            else:
                result += ",".join(attr_list)
            result += eolChars
        for row in used_table:
            ml = []
            for ctr, key in enumerate(attr_list):
                if key in row:
                    if row[key] is None:
                        value = ""
                    else:
                        value = str(row[key])
                    if quoteAll:
                        ml.append(internal.csv_quote(quoteChar,value))
                    else:
                        if ('"' in value) or (',' in value):
                            ml.append(internal.csv_quote('"', value))
                        else:
                            if ((ctr+1)==len(attr_list)) and (len(value)==0):
                                ml.append('""')
                            else:
                                ml.append(value)
                else:
                    if quoteAll:
                        ml.append(quoteChar+quoteChar)
                    else:
                        if (ctr+1)==len(attr_list):
                            ml.append('""')
                        else:
                            ml.append("")
            result += ",".join(ml)
            result += eolChars
        return result