# -*- coding: utf-8 -*-
#
# records.py
# csvdiff
#
import six
import csv
from . import error
[docs]class InvalidKeyError(Exception):
pass
[docs]def load(file_or_stream, sep=','):
istream = (open(file_or_stream)
if not hasattr(file_or_stream, 'read')
else file_or_stream)
# unicode delimiters are not ok in Python 2
if six.PY2 and isinstance(sep, six.text_type):
sep = sep.encode('utf8')
return SafeDictReader(istream, sep=sep)
[docs]class SafeDictReader:
"""
A CSV reader that streams records but gives nice errors if lines fail to parse.
"""
def __init__(self, istream, sep=None):
self.reader = csv.DictReader(istream, delimiter=sep)
def __iter__(self):
for lineno, r in enumerate(self.reader, 2):
if any(k is None for k in r):
error.abort('CSV parse error on line {}'.format(lineno))
yield r
@property
def fieldnames(self):
return self.reader._fieldnames
[docs]def index(record_seq, index_columns):
try:
obj = {
tuple(r[i] for i in index_columns): r
for r in record_seq
}
return obj
except KeyError as k:
raise InvalidKeyError('invalid column name {k} as key'.format(k=k))
[docs]def filter_ignored(sequence, ignore_columns):
for key in sequence:
for i in ignore_columns:
sequence[key].pop(i)
return sequence
[docs]def save(record_seq, fieldnames, ostream):
writer = csv.DictWriter(ostream, fieldnames)
writer.writeheader()
for r in record_seq:
writer.writerow(r)
[docs]def sort(recs):
return sorted(recs, key=_record_key)
def _record_key(r):
return sorted(r.items())