diff --git a/lookup.py b/lookup.py index fc3a2f6..62fde8c 100755 --- a/lookup.py +++ b/lookup.py @@ -1,11 +1,15 @@ #!/usr/bin/python3 """Reverse geocode: convert lat/lon to Wikidata item & Wikimedia Commons category.""" +import inspect import random import socket +import sys +import traceback import typing import sqlalchemy.exc +import werkzeug.debug.tbtools from flask import Flask, jsonify, redirect, render_template, request, url_for from sqlalchemy.orm.query import Query from werkzeug.wrappers import Response @@ -24,6 +28,33 @@ Tags = typing.Mapping[str, str] logging_enabled = True +@app.errorhandler(werkzeug.exceptions.InternalServerError) +def exception_handler(e: werkzeug.exceptions.InternalServerError) -> tuple[str, int]: + """Handle exception.""" + exec_type, exc_value, current_traceback = sys.exc_info() + assert exc_value + tb = werkzeug.debug.tbtools.DebugTraceback(exc_value) + + summary = tb.render_traceback_html(include_title=False) + exc_lines = "".join(tb._te.format_exception_only()) + + last_frame = list(traceback.walk_tb(current_traceback))[-1][0] + last_frame_args = inspect.getargs(last_frame.f_code) + + return ( + render_template( + "show_error.html", + plaintext=tb.render_traceback_text(), + exception=exc_lines, + exception_type=tb._te.exc_type.__name__, + summary=summary, + last_frame=last_frame, + last_frame_args=last_frame_args, + ), + 500, + ) + + def get_random_lat_lon() -> tuple[float, float]: """Select random lat/lon within the UK.""" south, east = 50.8520, 0.3536 diff --git a/static/css/exception.css b/static/css/exception.css new file mode 100644 index 0000000..1f141c5 --- /dev/null +++ b/static/css/exception.css @@ -0,0 +1,78 @@ +div.debugger { text-align: left; padding: 12px; margin: auto; + background-color: white; } +div.detail { cursor: pointer; } +div.detail p { margin: 0 0 8px 13px; font-size: 14px; white-space: pre-wrap; + font-family: monospace; } +div.explanation { margin: 20px 13px; font-size: 15px; color: #555; } +div.footer { font-size: 13px; text-align: right; margin: 30px 0; + color: #86989B; } + +h2 { font-size: 16px; margin: 1.3em 0 0.0 0; padding: 9px; + background-color: #11557C; color: white; } +h2 em, h3 em { font-style: normal; color: #A5D6D9; font-weight: normal; } + +div.traceback, div.plain { border: 1px solid #ddd; margin: 0 0 1em 0; padding: 10px; } +div.plain p { margin: 0; } +div.plain textarea, +div.plain pre { margin: 10px 0 0 0; padding: 4px; + background-color: #E8EFF0; border: 1px solid #D3E7E9; } +div.plain textarea { width: 99%; height: 300px; } +div.traceback h3 { font-size: 1em; margin: 0 0 0.8em 0; } +div.traceback ul { list-style: none; margin: 0; padding: 0 0 0 1em; } +div.traceback h4 { font-size: 13px; font-weight: normal; margin: 0.7em 0 0.1em 0; } +div.traceback pre { margin: 0; padding: 5px 0 3px 15px; + background-color: #E8EFF0; border: 1px solid #D3E7E9; } +div.traceback .library .current { background: white; color: #555; } +div.traceback .expanded .current { background: #E8EFF0; color: black; } +div.traceback pre:hover { background-color: #DDECEE; color: black; cursor: pointer; } +div.traceback div.source.expanded pre + pre { border-top: none; } + +div.traceback span.ws { display: none; } +div.traceback pre.before, div.traceback pre.after { display: none; background: white; } +div.traceback div.source.expanded pre.before, +div.traceback div.source.expanded pre.after { + display: block; +} + +div.traceback div.source.expanded span.ws { + display: inline; +} + +div.traceback blockquote { margin: 1em 0 0 0; padding: 0; white-space: pre-line; } +div.traceback img { float: right; padding: 2px; margin: -3px 2px 0 0; display: none; } +div.traceback img:hover { background-color: #ddd; cursor: pointer; + border-color: #BFDDE0; } +div.traceback pre:hover img { display: block; } +div.traceback cite.filename { font-style: normal; color: #3B666B; } + +pre.console { border: 1px solid #ccc; background: white!important; + color: black; padding: 5px!important; + margin: 3px 0 0 0!important; cursor: default!important; + max-height: 400px; overflow: auto; } +pre.console form { color: #555; } +pre.console input { background-color: transparent; color: #555; + width: 90%; font-family: 'Consolas', 'Deja Vu Sans Mono', + 'Bitstream Vera Sans Mono', monospace; font-size: 14px; + border: none!important; } + +span.string { color: #30799B; } +span.number { color: #9C1A1C; } +span.help { color: #3A7734; } +span.object { color: #485F6E; } +span.extended { opacity: 0.5; } +span.extended:hover { opacity: 1; } +a.toggle { text-decoration: none; background-repeat: no-repeat; + background-position: center center; + background-image: url(?__debugger__=yes&cmd=resource&f=more.png); } +a.toggle:hover { background-color: #444; } +a.open { background-image: url(?__debugger__=yes&cmd=resource&f=less.png); } + +div.traceback pre, div.console pre { + white-space: pre-wrap; /* css-3 should we be so lucky... */ + white-space: -moz-pre-wrap; /* Mozilla, since 1999 */ + white-space: -pre-wrap; /* Opera 4-6 ?? */ + white-space: -o-pre-wrap; /* Opera 7 ?? */ + word-wrap: break-word; /* Internet Explorer 5.5+ */ + _white-space: pre; /* IE only hack to re-specify in + addition to word-wrap */ +} diff --git a/templates/show_error.html b/templates/show_error.html new file mode 100644 index 0000000..dd79c3c --- /dev/null +++ b/templates/show_error.html @@ -0,0 +1,23 @@ +{% extends "base.html" %} + +{% block style %} + +{% endblock %} + +{% block content %} +
{{ exception }}+
Error in function "{{ last_frame.f_code.co_name }}": {{ last_frame_args | pprint }}
+{{ last_frame.f_locals | pprint }}+ +