BrickTracker/bricktracker/views/error.py

190 lines
4.7 KiB
Python
Raw Normal View History

2025-01-17 11:03:00 +01:00
import logging
from sqlite3 import Error, OperationalError
import traceback
2025-01-29 22:44:08 +01:00
from typing import Any, Tuple
2025-01-17 11:03:00 +01:00
from flask import jsonify, redirect, request, render_template, url_for
from werkzeug.wrappers.response import Response
from ..exceptions import DatabaseException, ErrorException, NotFoundException
logger = logging.getLogger(__name__)
# Get the cleaned exception
def cleaned_exception(e: Exception, /) -> str:
trace = traceback.TracebackException.from_exception(e)
cleaned: list[str] = []
# Hacky: stripped from the call to the decorator wrapper() or outer()
for line in trace.format():
if 'in wrapper' not in line and 'in outer' not in line:
cleaned.append(line)
return ''.join(cleaned)
# Generic error
def error(
error: Exception | None,
file: str,
/,
2025-01-23 08:58:57 +01:00
*,
2025-01-17 11:03:00 +01:00
json: bool = False,
post_redirect: str | None = None,
2025-01-29 22:44:08 +01:00
error_name: str = 'error',
2025-01-17 11:03:00 +01:00
**kwargs,
) -> str | Tuple[str | Response, int] | Response:
# Back to the index if no error (not sure if this can happen)
if error is None:
if json:
2025-01-29 22:44:08 +01:00
return json_error(
'error() called without an error',
error_name=error_name
)
2025-01-17 11:03:00 +01:00
else:
return redirect(url_for('index.index'))
# Convert SQLite errors
if isinstance(error, (Error, OperationalError)):
error = DatabaseException(error)
# Clear redirect if not POST or json
if json or request.method != 'POST':
post_redirect = None
# Not found
if isinstance(error, NotFoundException):
return error_404(
error,
json=json,
post_redirect=post_redirect,
2025-01-29 22:44:08 +01:00
error_name=error_name,
2025-01-17 11:03:00 +01:00
**kwargs
)
# Common error
elif isinstance(error, ErrorException):
# Error
logger.error('{title}: {error}'.format(
title=error.title,
error=str(error),
))
# Debug
logger.debug(cleaned_exception(error))
if json:
2025-01-29 22:44:08 +01:00
return json_error(
str(error),
error_name=error_name
)
2025-01-17 11:03:00 +01:00
elif post_redirect is not None:
2025-01-29 22:44:08 +01:00
return redirect_error(
2025-01-17 11:03:00 +01:00
post_redirect,
error=str(error),
2025-01-29 22:44:08 +01:00
error_name=error_name,
2025-01-17 11:03:00 +01:00
**kwargs,
2025-01-29 22:44:08 +01:00
)
2025-01-17 11:03:00 +01:00
else:
return render_template(
'error.html',
title=error.title,
error=str(error)
)
# Exception
else:
# Error
logger.error(cleaned_exception(error))
if error.__traceback__ is not None:
line = error.__traceback__.tb_lineno
else:
line = None
if json:
2025-01-29 22:44:08 +01:00
return json_error(
'Exception: {error}'.format(error=str(error)),
error_name=error_name,
name=type(error).__name__,
line=line,
file=file
), 500
2025-01-17 11:03:00 +01:00
elif post_redirect is not None:
2025-01-29 22:44:08 +01:00
return redirect_error(
2025-01-17 11:03:00 +01:00
post_redirect,
error=str(error),
2025-01-29 22:44:08 +01:00
error_name=error_name,
2025-01-17 11:03:00 +01:00
**kwargs,
2025-01-29 22:44:08 +01:00
)
2025-01-17 11:03:00 +01:00
else:
return render_template(
'exception.html',
error=str(error),
name=type(error).__name__,
line=line,
file=file,
)
# Error 404
def error_404(
error: Exception,
/,
2025-01-23 08:58:57 +01:00
*,
2025-01-17 11:03:00 +01:00
json: bool = False,
post_redirect: str | None = None,
2025-01-29 22:44:08 +01:00
error_name: str = 'error',
2025-01-17 11:03:00 +01:00
**kwargs,
) -> Response | Tuple[str | Response, int]:
2025-01-17 11:03:00 +01:00
# Warning
logger.warning('Not found: {error} (path: {path})'.format(
2025-01-17 11:03:00 +01:00
error=str(error),
path=request.path,
2025-01-17 11:03:00 +01:00
))
if json:
2025-01-29 22:44:08 +01:00
return json_error(
'Not found: {error}'.format(error=str(error)),
error_name=error_name
), 404
2025-01-17 11:03:00 +01:00
elif post_redirect is not None:
2025-01-29 22:44:08 +01:00
return redirect_error(
2025-01-17 11:03:00 +01:00
post_redirect,
error=str(error),
2025-01-29 22:44:08 +01:00
error_name=error_name,
**kwargs,
)
2025-01-17 11:03:00 +01:00
else:
return render_template('404.html', error=str(error)), 404
2025-01-29 22:44:08 +01:00
# JSON error with parametric error name
def json_error(
error: str,
error_name: str = 'error',
**parameters: Any
) -> Response:
parameters[error_name] = error
return jsonify(parameters)
# Redirect error with parametric error name
def redirect_error(
url: str,
error: str,
error_name: str = 'error',
**kwargs
) -> Response:
error_parameter: dict[str, str] = {}
error_parameter[error_name] = str(error)
return redirect(url_for(
url,
**error_parameter,
**kwargs
))