Files
zbiorki_app/zbiorka_app/errors.py
Mateusz Gruszczyński 29b07f6431 error handling
2026-03-20 10:54:01 +01:00

114 lines
3.0 KiB
Python

from http import HTTPStatus
from flask import jsonify, render_template, request
from jinja2 import TemplateNotFound
from sqlalchemy.exc import OperationalError
from werkzeug.exceptions import HTTPException
from .extensions import db
def _safe_rollback() -> None:
try:
db.session.rollback()
except Exception:
pass
def _wants_json_response() -> bool:
if request.path.startswith("/api/"):
return True
best = request.accept_mimetypes.best_match(["application/json", "text/html"])
if not best:
return False
return (
best == "application/json"
and request.accept_mimetypes["application/json"]
>= request.accept_mimetypes["text/html"]
)
def _get_status_phrase(status_code: int) -> str:
try:
return HTTPStatus(status_code).phrase
except ValueError:
return "Blad"
def _get_status_description(status_code: int) -> str:
try:
return HTTPStatus(status_code).description
except ValueError:
return "Wystapil blad podczas przetwarzania zadania."
def _error_headers(status_code: int) -> dict[str, str]:
headers = {}
if status_code >= 500:
headers.update(
{
"Cache-Control": "no-store, no-cache, must-revalidate, max-age=0, private",
"Pragma": "no-cache",
"Expires": "0",
"Surrogate-Control": "no-store",
}
)
return headers
def _render_error(status_code: int, message: str | None = None):
phrase = _get_status_phrase(status_code)
description = message or _get_status_description(status_code)
headers = _error_headers(status_code)
payload = {
"status": status_code,
"error": phrase,
"message": description,
}
if _wants_json_response():
return jsonify(payload), status_code, headers
try:
return (
render_template(
"error.html",
error_code=status_code,
error_name=phrase,
error_message=description,
),
status_code,
headers,
)
except TemplateNotFound:
return (
f"{status_code} {phrase}: {description}",
status_code,
headers,
)
def register_error_handlers(app):
@app.errorhandler(HTTPException)
def handle_http_exception(exc):
return _render_error(exc.code or 500, exc.description)
@app.errorhandler(OperationalError)
def handle_operational_error(exc):
_safe_rollback()
app.logger.exception("Blad polaczenia z baza danych: %s", exc)
return _render_error(
503,
"Baza danych jest chwilowo niedostepna. Sprobuj ponownie za chwile.",
)
@app.errorhandler(Exception)
def handle_unexpected_error(exc):
_safe_rollback()
app.logger.exception("Nieobsluzony wyjatek: %s", exc)
return _render_error(500, "Wystapil nieoczekiwany blad serwera.")