From 8ae8877e3e2588db76285e7e3ddfb8c7b9948a96 Mon Sep 17 00:00:00 2001 From: Christian Heller Date: Wed, 27 Mar 2024 01:34:22 +0100 Subject: [PATCH] Use different exceptions throwing different HTTP codes for different cases. --- plomtask/days.py | 6 +++--- plomtask/db.py | 2 +- plomtask/exceptions.py | 19 +++++++++++++++++++ plomtask/http.py | 12 ++++++------ plomtask/misc.py | 8 -------- plomtask/processes.py | 4 ++-- run.py | 2 +- tests/days.py | 16 ++++++++-------- tests/processes.py | 16 ++++++++-------- 9 files changed, 48 insertions(+), 37 deletions(-) create mode 100644 plomtask/exceptions.py delete mode 100644 plomtask/misc.py diff --git a/plomtask/days.py b/plomtask/days.py index 3b81a7f..afdea33 100644 --- a/plomtask/days.py +++ b/plomtask/days.py @@ -2,7 +2,7 @@ from __future__ import annotations from datetime import datetime, timedelta from sqlite3 import Row -from plomtask.misc import HandledException +from plomtask.exceptions import BadFormatException, NotFoundException from plomtask.db import DatabaseConnection DATE_FORMAT = '%Y-%m-%d' @@ -16,7 +16,7 @@ def valid_date(date_str: str) -> str: dt = datetime.strptime(date_str, DATE_FORMAT) except (ValueError, TypeError) as e: msg = f'Given date of wrong format: {date_str}' - raise HandledException(msg) from e + raise BadFormatException(msg) from e return dt.strftime(DATE_FORMAT) @@ -76,7 +76,7 @@ class Day: for row in db_conn.exec('SELECT * FROM days WHERE date = ?', (date,)): return cls.from_table_row(row) if not create: - raise HandledException(f'Day not found for date: {date}') + raise NotFoundException(f'Day not found for date: {date}') return cls(date) @property diff --git a/plomtask/db.py b/plomtask/db.py index e0a5d4f..929a733 100644 --- a/plomtask/db.py +++ b/plomtask/db.py @@ -3,7 +3,7 @@ from os.path import isfile from difflib import Differ from sqlite3 import connect as sql_connect, Cursor from typing import Any -from plomtask.misc import HandledException +from plomtask.exceptions import HandledException PATH_DB_SCHEMA = 'scripts/init.sql' diff --git a/plomtask/exceptions.py b/plomtask/exceptions.py new file mode 100644 index 0000000..379ae9f --- /dev/null +++ b/plomtask/exceptions.py @@ -0,0 +1,19 @@ +""" +Whatever fits nowhere else, and/or is too small/trivial +to merit its own module at this point. +""" + + +class HandledException(Exception): + """To identify Exceptions based on expected (if faulty) user behavior.""" + http_code = 400 + + +class BadFormatException(HandledException): + """To identify Exceptions on malformed inputs.""" + http_code = 401 + + +class NotFoundException(HandledException): + """To identify Exceptions on unsuccessful queries.""" + http_code = 404 diff --git a/plomtask/http.py b/plomtask/http.py index ddea087..af1a60c 100644 --- a/plomtask/http.py +++ b/plomtask/http.py @@ -6,7 +6,7 @@ from urllib.parse import urlparse, parse_qs from os.path import split as path_split from jinja2 import Environment as JinjaEnv, FileSystemLoader as JinjaFSLoader from plomtask.days import Day, todays_date -from plomtask.misc import HandledException +from plomtask.exceptions import HandledException, BadFormatException from plomtask.db import DatabaseConnection, DatabaseFile from plomtask.processes import Process @@ -43,7 +43,7 @@ class TaskHandler(BaseHTTPRequestHandler): try: id__ = int(id_) if id_ else None except ValueError as e: - raise HandledException(f'Bad ?id= value: {id_}') from e + raise BadFormatException(f'Bad ?id= value: {id_}') from e html = self.do_GET_process(conn, id__) elif 'processes' == site: html = self.do_GET_processes(conn) @@ -53,7 +53,7 @@ class TaskHandler(BaseHTTPRequestHandler): conn.close() self._send_html(html) except HandledException as error: - self._send_msg(error) + self._send_msg(error, code=error.http_code) def do_GET_calendar(self, conn: DatabaseConnection, start: str, end: str) -> str: @@ -92,13 +92,13 @@ class TaskHandler(BaseHTTPRequestHandler): try: id__ = int(id_) if id_ else None except ValueError as e: - raise HandledException(f'Bad ?id= value: {id_}') from e + raise BadFormatException(f'Bad ?id= value: {id_}') from e self.do_POST_process(conn, id__, postvars) conn.commit() conn.close() self._redirect('/') except HandledException as error: - self._send_msg(error) + self._send_msg(error, code=error.http_code) def do_POST_day(self, conn: DatabaseConnection, date: str, postvars: dict[str, list[str]]) -> None: @@ -117,7 +117,7 @@ class TaskHandler(BaseHTTPRequestHandler): try: process.effort.set(float(effort)) except ValueError as e: - raise HandledException(f'Bad effort value: {effort}') from e + raise BadFormatException(f'Bad effort value: {effort}') from e process.save(conn) def _init_handling(self) -> \ diff --git a/plomtask/misc.py b/plomtask/misc.py deleted file mode 100644 index 1b780e2..0000000 --- a/plomtask/misc.py +++ /dev/null @@ -1,8 +0,0 @@ -""" -Whatever fits nowhere else, and/or is too small/trivial -to merit its own module at this point. -""" - - -class HandledException(Exception): - """To identify Exceptions based on expected (if faulty) user behavior.""" diff --git a/plomtask/processes.py b/plomtask/processes.py index 4867227..8b5ff42 100644 --- a/plomtask/processes.py +++ b/plomtask/processes.py @@ -3,7 +3,7 @@ from __future__ import annotations from sqlite3 import Row from datetime import datetime from plomtask.db import DatabaseConnection -from plomtask.misc import HandledException +from plomtask.exceptions import NotFoundException class Process: @@ -46,7 +46,7 @@ class Process: break if not process: if not create: - raise HandledException(f'Process not found of id: {id_}') + raise NotFoundException(f'Process not found of id: {id_}') process = Process(id_) if process: for row in db_conn.exec('SELECT * FROM process_titles ' diff --git a/run.py b/run.py index 31d11ce..e1bbe5d 100755 --- a/run.py +++ b/run.py @@ -2,7 +2,7 @@ """Call this to start the application.""" from sys import exit as sys_exit from os import environ -from plomtask.misc import HandledException +from plomtask.exceptions import HandledException from plomtask.http import TaskHandler, TaskServer from plomtask.db import DatabaseFile diff --git a/tests/days.py b/tests/days.py index f3ed082..61a27ed 100644 --- a/tests/days.py +++ b/tests/days.py @@ -3,7 +3,7 @@ from unittest import TestCase from datetime import datetime from tests.utils import TestCaseWithDB, TestCaseWithServer from plomtask.days import Day, todays_date -from plomtask.misc import HandledException +from plomtask.exceptions import BadFormatException, NotFoundException class TestsSansDB(TestCase): @@ -11,11 +11,11 @@ class TestsSansDB(TestCase): def test_Day_dates(self) -> None: """Test Day's date format.""" - with self.assertRaises(HandledException): + with self.assertRaises(BadFormatException): Day('foo') - with self.assertRaises(HandledException): + with self.assertRaises(BadFormatException): Day('2024-02-30') - with self.assertRaises(HandledException): + with self.assertRaises(BadFormatException): Day('2024-02-01 23:00:00') self.assertEqual(datetime(2024, 1, 1), Day('2024-01-01').datetime) @@ -37,12 +37,12 @@ class TestsWithDB(TestCaseWithDB): def test_Day_by_date(self) -> None: """Test Day.by_date().""" - with self.assertRaises(HandledException): + with self.assertRaises(NotFoundException): Day.by_date(self.db_conn, '2024-01-01') Day('2024-01-01').save(self.db_conn) self.assertEqual(Day('2024-01-01'), Day.by_date(self.db_conn, '2024-01-01')) - with self.assertRaises(HandledException): + with self.assertRaises(NotFoundException): Day.by_date(self.db_conn, '2024-01-02') self.assertEqual(Day('2024-01-02'), Day.by_date(self.db_conn, '2024-01-02', create=True)) @@ -97,7 +97,7 @@ class TestsWithServer(TestCaseWithServer): self.conn.request('GET', '/day?date=3000-01-01') self.assertEqual(self.conn.getresponse().status, 200) self.conn.request('GET', '/day?date=FOO') - self.assertEqual(self.conn.getresponse().status, 400) + self.assertEqual(self.conn.getresponse().status, 401) self.conn.request('GET', '/calendar') self.assertEqual(self.conn.getresponse().status, 200) self.conn.request('GET', '/calendar?start=&end=') @@ -107,4 +107,4 @@ class TestsWithServer(TestCaseWithServer): self.conn.request('GET', '/calendar?start=2024-01-01&end=2025-01-01') self.assertEqual(self.conn.getresponse().status, 200) self.conn.request('GET', '/calendar?start=foo') - self.assertEqual(self.conn.getresponse().status, 400) + self.assertEqual(self.conn.getresponse().status, 401) diff --git a/tests/processes.py b/tests/processes.py index 17af14e..399eb9d 100644 --- a/tests/processes.py +++ b/tests/processes.py @@ -3,7 +3,7 @@ from unittest import TestCase from urllib.parse import urlencode from tests.utils import TestCaseWithDB, TestCaseWithServer from plomtask.processes import Process -from plomtask.misc import HandledException +from plomtask.exceptions import NotFoundException class TestsSansDB(TestCase): @@ -42,11 +42,11 @@ class TestsWithDB(TestCaseWithDB): def test_Process_by_id(self) -> None: """Test Process.by_id().""" - with self.assertRaises(HandledException): + with self.assertRaises(NotFoundException): Process.by_id(self.db_conn, None, create=False) - with self.assertRaises(HandledException): + with self.assertRaises(NotFoundException): Process.by_id(self.db_conn, 0, create=False) - with self.assertRaises(HandledException): + with self.assertRaises(NotFoundException): Process.by_id(self.db_conn, 1, create=False) self.assertNotEqual(Process(1).id_, Process.by_id(self.db_conn, None, create=True).id_) @@ -81,11 +81,11 @@ class TestsWithServer(TestCaseWithServer): body=encoded_form_data, headers=headers) self.assertEqual(self.conn.getresponse().status, expect) form_data = {'title': 'foo', 'description': 'foo', 'effort': 1.0} - post_data_to_expect(form_data, '/process?id=FOO', 400) + post_data_to_expect(form_data, '/process?id=FOO', 401) form_data['effort'] = 'foo' - post_data_to_expect(form_data, '/process?id=', 400) + post_data_to_expect(form_data, '/process?id=', 401) form_data['effort'] = None - post_data_to_expect(form_data, '/process?id=', 400) + post_data_to_expect(form_data, '/process?id=', 401) form_data = {'title': None, 'description': 1, 'effort': 1.0} post_data_to_expect(form_data, '/process?id=', 302) retrieved = Process.by_id(self.db_conn, 1) @@ -102,6 +102,6 @@ class TestsWithServer(TestCaseWithServer): self.conn.request('GET', '/process?id=0') self.assertEqual(self.conn.getresponse().status, 200) self.conn.request('GET', '/process?id=FOO') - self.assertEqual(self.conn.getresponse().status, 400) + self.assertEqual(self.conn.getresponse().status, 401) self.conn.request('GET', '/processes') self.assertEqual(self.conn.getresponse().status, 200) -- 2.30.2