aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/ywalk/parser.py
diff options
context:
space:
mode:
Diffstat (limited to 'ywalk/parser.py')
-rw-r--r--ywalk/parser.py47
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