diff options
author | Wolfgang Müller | 2021-11-14 18:55:52 +0100 |
---|---|---|
committer | Wolfgang Müller | 2021-11-14 18:55:52 +0100 |
commit | 0fb4fc20559c9a5de5a10c74c1247635a1523255 (patch) | |
tree | dbd27ffd7e5d09af732e973455bea1e47e46609f /ywalk/parser.py | |
download | ywalk-0fb4fc20559c9a5de5a10c74c1247635a1523255.tar.gz |
Diffstat (limited to 'ywalk/parser.py')
-rw-r--r-- | ywalk/parser.py | 47 |
1 files changed, 47 insertions, 0 deletions
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 |