Source code for ResearchNotes.error

# -*- coding: utf-8 -*-
"""
Module for all error handling.

All error handling is defined here as well as
made sure, we log them into the app.logger facility.
"""
import typing

from flask import Blueprint, render_template, current_app, request, make_response

from werkzeug.exceptions import HTTPException
from werkzeug.wrappers.response import Response

from sqlalchemy.exc import SQLAlchemyError, DBAPIError

from ResearchNotes.database import db

bp = Blueprint("errors", __name__)


[docs]@bp.app_errorhandler(400) def badrequest_error(error: typing.Union[str, HTTPException]) -> typing.Tuple[str, int] | Response: """ Error handler for bad request. Provide an error handler for code 400 HTTPExceptions (Bad request) Parameters ---------- error : TYPE DESCRIPTION. Returns ------- str View to error page int HTTP error code. """ current_app.logger.warning(f"{str(error)}") if "HX-Request" in request.headers: current_app.logger.info("Error came from Htmx request") response = make_response("<h1>Sorry, bad HTTP request.</h1>", 400) # response.headers["HX-Reswap"] = "" response.headers["HX-Retarget"] = "#main" return response return render_template("400.html"), 400
[docs]@bp.app_errorhandler(403) def forbidden_error(error: typing.Union[str, HTTPException]) -> typing.Tuple[str, int] | Response: """ Error for not authorized for certain action. Parameters ---------- error : str Error message from function calling the handler. Returns ------- str View to error page int HTTP error code. """ current_app.logger.warning(f"{str(error)}") if "HX-Request" in request.headers: current_app.logger.info("Error came from Htmx request") response = make_response( "<h1>Sorry, you do not have the right to access this resource.</h1>" + "This might either come from your user role or the group you are in.", 403, ) # response.headers["HX-Reswap"] = "" response.headers["HX-Retarget"] = "#main" return response return render_template("403.html"), 403
[docs]@bp.app_errorhandler(404) def not_found_error(error: typing.Union[str, HTTPException]) -> typing.Tuple[str, int] | Response: """ Errorhandler for page not found. Parameters ---------- error : str Error message from function calling the handler. Returns ------- str View to error page int HTTP error code. """ current_app.logger.warning(f"{str(error)}") if "HX-Request" in request.headers: current_app.logger.info("Error came from Htmx request") response = make_response( "<h1>Sorry, the resource your were looking for was not found.</h1>", 404 ) # response.headers["HX-Reswap"] = "" response.headers["HX-Retarget"] = "#main" return response return render_template("404.html"), 404
[docs]@bp.app_errorhandler(413) def too_large(error: typing.Union[str, HTTPException]) -> typing.Tuple[str, int]: """ Error handler for file to large (does not work well with dropzone.js). Parameters ---------- error : str Error message from function calling the handler. Returns ------- str View to error page int HTTP error code. """ current_app.logger.warning(f"{str(error)}") return "File is too large", 413
[docs]@bp.app_errorhandler(418) def i_am_a_teapot(error: typing.Union[str, HTTPException]) -> typing.Tuple[str, int] | Response: """ Errorhandler for "I am a teapot" status. Parameters ---------- error : str Error message from function calling the handler. Returns ------- str View to error page int HTTP error code. """ current_app.logger.warning(f"{str(error)}") if "HX-Request" in request.headers: current_app.logger.info("Error came from Htmx request") response = make_response("<h1>Sorry, I am a terapot</h1>", 418) # response.headers["HX-Reswap"] = "" response.headers["HX-Retarget"] = "#main" return response return render_template("418.html"), 418
[docs]@bp.app_errorhandler(500) def internal_error(error: typing.Union[str, HTTPException]) -> typing.Tuple[str, int] | Response: """ Errorhandler for internal errors. Tries to rollback database, if possible. Parameters ---------- error : str Error message from function calling the handler. Returns ------- str View to error page int HTTP error code. """ current_app.logger.error("Real 500 error") current_app.logger.error(f"{str(error)}") try: db.session.rollback() except (SQLAlchemyError, DBAPIError) as dberror: current_app.logger.error(f"Database error : {str(dberror)}") if "HX-Request" in request.headers: current_app.logger.info("Error came from Htmx request") response = make_response( "<h1> Upsi, An unexpected error has occurred</h1>\nThis should not" + " happen. Something went wrong. Sorry.\n<br>\nPlease, tell the admin " + "that we can fix it.", 500, ) # response.headers["HX-Reswap"] = "" response.headers["HX-Retarget"] = "#main" return response return render_template("500.html"), 500
[docs]@bp.app_errorhandler(Exception) def handle_exception( error: typing.Union[str, Exception] ) -> typing.Union[HTTPException, typing.Tuple[str, int]] | Response: """ Errorhandler for all non HTTPExceptions. Parameters ---------- error : Exception All exceptions go here and if they are not of the type HTTPExceptions will be handed here. Returns ------- View Returns view to our internal error page. """ # pass through HTTP errors if isinstance(error, HTTPException): return error # now you're handling non-HTTP exceptions only current_app.logger.error("Something bad happen. Exception raised") current_app.logger.exception(error) debug = None if current_app.debug: debug = error if "HX-Request" in request.headers: current_app.logger.info("Error came from Htmx request") response = make_response( "<h1> Upsi, An unexpected error has occurred</h1>\nThis should not" + " happen. Something went wrong. Sorry.\n<br>\nPlease, tell the admin " + f"that we can fix it. <p> {debug} </p>", 500, ) # response.headers["HX-Reswap"] = "" response.headers["HX-Retarget"] = "#main" return response return render_template("500.html", debug=debug), 500