Show more useful error message for SQL interrupted, closes #142

custom-router
Simon Willison 2018-05-28 14:24:19 -07:00
rodzic 98c8f0e728
commit b0a95da963
Nie znaleziono w bazie danych klucza dla tego podpisu
ID klucza GPG: 17E2DEA2588B7F52
3 zmienionych plików z 25 dodań i 3 usunięć

Wyświetl plik

@ -12,6 +12,7 @@ import urllib.parse
from concurrent import futures from concurrent import futures
from pathlib import Path from pathlib import Path
from markupsafe import Markup
import pluggy import pluggy
from jinja2 import ChoiceLoader, Environment, FileSystemLoader, PrefixLoader from jinja2 import ChoiceLoader, Environment, FileSystemLoader, PrefixLoader
from sanic import Sanic, response from sanic import Sanic, response
@ -461,6 +462,7 @@ class Datasette:
@app.exception(Exception) @app.exception(Exception)
def on_exception(request, exception): def on_exception(request, exception):
title = None title = None
help = None
if isinstance(exception, NotFound): if isinstance(exception, NotFound):
status = 404 status = 404
info = {} info = {}
@ -473,6 +475,8 @@ class Datasette:
status = exception.status status = exception.status
info = exception.error_dict info = exception.error_dict
message = exception.message message = exception.message
if exception.messagge_is_html:
message = Markup(message)
title = exception.title title = exception.title
else: else:
status = 500 status = 500

Wyświetl plik

@ -27,11 +27,12 @@ HASH_LENGTH = 7
class DatasetteError(Exception): class DatasetteError(Exception):
def __init__(self, message, title=None, error_dict=None, status=500, template=None): def __init__(self, message, title=None, error_dict=None, status=500, template=None, messagge_is_html=False):
self.message = message self.message = message
self.title = title self.title = title
self.error_dict = error_dict or {} self.error_dict = error_dict or {}
self.status = status self.status = status
self.messagge_is_html = messagge_is_html
class RenderMixin(HTTPMethodView): class RenderMixin(HTTPMethodView):
@ -154,7 +155,11 @@ class BaseView(RenderMixin):
else: else:
data, extra_template_data, templates = response_or_template_contexts data, extra_template_data, templates = response_or_template_contexts
except InterruptedError as e: except InterruptedError as e:
raise DatasetteError(str(e), title="SQL Interrupted", status=400) raise DatasetteError("""
SQL query took too long. The time limit is controlled by the
<a href="https://datasette.readthedocs.io/en/stable/config.html#sql-time-limit-ms">sql_time_limit_ms</a>
configuration option.
""", title="SQL Interrupted", status=400, messagge_is_html=True)
except (sqlite3.OperationalError, InvalidSql) as e: except (sqlite3.OperationalError, InvalidSql) as e:
raise DatasetteError(str(e), title="Invalid SQL", status=400) raise DatasetteError(str(e), title="Invalid SQL", status=400)

Wyświetl plik

@ -1,10 +1,11 @@
from bs4 import BeautifulSoup as Soup from bs4 import BeautifulSoup as Soup
from .fixtures import app_client from .fixtures import app_client, app_client_shorter_time_limit
import pytest import pytest
import re import re
import urllib.parse import urllib.parse
pytest.fixture(scope='session')(app_client) pytest.fixture(scope='session')(app_client)
pytest.fixture(scope='session')(app_client_shorter_time_limit)
def test_homepage(app_client): def test_homepage(app_client):
@ -29,6 +30,18 @@ def test_invalid_custom_sql(app_client):
assert 'Statement must be a SELECT' in response.text assert 'Statement must be a SELECT' in response.text
def test_sql_time_limit(app_client_shorter_time_limit):
response = app_client_shorter_time_limit.get(
'/test_tables?sql=select+sleep(0.5)',
gather_request=False
)
assert 400 == response.status
expected_html_fragment = """
<a href="https://datasette.readthedocs.io/en/stable/config.html#sql-time-limit-ms">sql_time_limit_ms</a>
""".strip()
assert expected_html_fragment in response.text
def test_view(app_client): def test_view(app_client):
response = app_client.get('/test_tables/simple_view', gather_request=False) response = app_client.get('/test_tables/simple_view', gather_request=False)
assert response.status == 200 assert response.status == 200