Wednesday, May 14, 2014

A python CSV writer decorator

A CSV writer decorator for functions returning a list (row)

I was looking for a clean way to implement a repeatable pattern where I could have a function return a value list and for this value list to be output to a CSV. The hope is that it should be adaptable to any other target format as well.

After a bit of research I came across:

And the decorator mentioned in it seemed like an excellent idea.

The additional requirement for this was to render the header (or field names) for the csv as a one time output for the top line of the CSV file.

The following assumptions are made:
  • The 'out' file handle is a global and is setup prior to calling the function
  • The 'attrNameList' is a global and is the header that should be the first line of the CSV file.
  • The function passed to the decorator returns a compatible list-type value such that decorator can render it using the csv.writer.
I am guessing this is not quite rigorously pythonic due to the expectations from the passed-function - whereas a true decorator might expect to be completely agnostic of the function it decorates. But I think it is still reasonable and reasonable is good enough for me in many cases.

For the header, a single boolean flag is injected into the function to indicate whether to write the header or not.

So here is the decorator code:

def writeCSV(func):
    def wrapcsv(*args,**kwargs):
        global out
        global attrNameList
        cw=csv.writer(out, delimiter=",",lineterminator='\n')
        if wrapcsv.header==False:
            cw.writerow(attrNameList)
        wrapcsv.header=True
        funcval= func(*args,**kwargs)
        cw.writerow(funcval)
        
        return funcval
    
    wrapcsv.header=False
    return wrapcsv


And here is a function it could decorate (I was parsing an LDIF file and extracting specified attributes to a CSV file):

@writeCSV    
def reportAttrs(dn,ent):
    global attrNameList
    global out
    vallist=[]
    for attrName in attrNameList: 
        if ent.has_key(attrName.lower()):
            vallist.append(ent[attrName.lower()][0])
        else:
            vallist.append("")
        if attrName.lower() == "dn":
            vallist.append(dn)
    return vallist

This can be adapted to writing XML, or JSON or whatever you need

No comments:

Post a Comment