Exercise 7.2 - Solution

(a) Adding logging to a module

# fileparse.py
import csv

# Get a logger on which to issue diagnostics.  The __name__ variable
# contains the module name--so in this case the logger should have
# the name 'fileparse'

import logging
log = logging.getLogger(__name__)

def parse_csv(filename, select=None, types=None, has_headers=True, delimiter=',', ignore_errors=False):
    '''
    Parse a CSV file into a list of records with type conversion.
    '''
    if select and not has_headers:
        raise RuntimeError("select requires column headers")

    f = open(filename)
    f_csv = csv.reader(f, delimiter=delimiter)

    # Read the file headers (if any)
    headers = next(f_csv) if has_headers else []

    # If specific columns have been selected, make indices for filtering and set output columns
    if select:
        indices = [ headers.index(colname) for colname in select ]
        output_columns = select
    else:
        indices = []
        output_columns = headers

    records = []
    for rowno, row in enumerate(f_csv, 1):
        if not row:     # Skip rows with no data
            continue

        # If specific column indices are selected, pick them out
        if indices:
            row = [ row[index] for index in indices]

        # Apply type conversion to the row
        if types:
            try:
                row = [func(val) for func, val in zip(types, row)]
            except ValueError as e:
                if not ignore_errors:
                    log.warning("Row %d: Couldn't convert %s", rowno, row)
                    log.debug("Row %d: Reason %s", rowno, e)
                continue

        # Make a dictionary or a tuple
        if output_columns:
            record = dict(zip(output_columns, row))
        else:
            record = tuple(row)
        records.append(record)

    f.close()
    return records

====(b) Adding Logging to an Application

You simply need to add some logging configuration to your program in the main program. Due to earlier exercises and practicum projects, your solution might look quite different than this. However, the key part is the call to logging.basicConfig().

# report.py

...
if __name__ == '__main__':
    import sys
    import logging

    if len(sys.argv) != 3:
        raise SystemExit('Usage: %s portfile pricefile' % sys.argv[0])
    portfile = sys.argv[1]
    pricefile = sys.argv[2]

    # Config logging
    logging.basicConfig(
        filename='report.log',
        filemode='w',
        level=logging.WARNING
        )

    portfolio_report(portfile, pricefile)

[ Back ]