From 0fb4fc20559c9a5de5a10c74c1247635a1523255 Mon Sep 17 00:00:00 2001 From: Wolfgang Müller Date: Sun, 14 Nov 2021 18:55:52 +0100 Subject: Initial commit --- ywalk/parser.py | 47 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 47 insertions(+) create mode 100644 ywalk/parser.py (limited to 'ywalk/parser.py') diff --git a/ywalk/parser.py b/ywalk/parser.py new file mode 100644 index 0000000..40d5f88 --- /dev/null +++ b/ywalk/parser.py @@ -0,0 +1,47 @@ +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 -- cgit v1.2.3-2-gb3c3