import csv from ywalk.types import Connection, Mode, Place FIELD_NAMES = ['Origin', 'Destination', 'Mode', 'Time'] class InputError(Exception): def __init__(self, message, file, line, context): super().__init__(message) self.message = message self.file = file self.line = line self.context = context def __str__(self): return f'{self.file}:{self.line}: {self.message}\n {self.context}' def parse_row(row): for field in FIELD_NAMES: if row[field] is None: raise ValueError(f'Empty field \'{field}\'') # This is an enum lookup, not a creation. The Enum class replaces a custom # __new__ after class creation with one that only does lookups. # pylint: disable=no-value-for-parameter mode = Mode(row['Mode']) time = int(row['Time']) if time < 0: raise ValueError('Negative travel time') return Connection(Place(row['Origin']), Place(row['Destination']), mode, time) def check_fieldnames(names): for name in FIELD_NAMES: if name not in names: raise ValueError(f'Missing field \'{name}\' in {names}') def parse_tsv(path): with open(path, encoding='utf-8', newline='') as file: reader = csv.DictReader(file, dialect=csv.excel_tab) check_fieldnames(reader.fieldnames) for line, row in enumerate(reader): try: yield parse_row(row) except ValueError as err: raise InputError(err, path, line + 1, row) from err