Handle jinja's UndefinedError in jinja.render
We can get UndefinedError when a new function got added to the jinja env (and gets called from a template) and the user did update the on-disk templates but not restart qutebrowser yet. In this case, let's show a special error page to the user and tell them to do :report in the unlikely case it's actually a bug. Fixes #1362. See #1360.
This commit is contained in:
parent
ebfe476319
commit
9edc5a665e
22
qutebrowser/html/undef_error.html
Normal file
22
qutebrowser/html/undef_error.html
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<!--
|
||||||
|
vim: ft=html fileencoding=utf-8 sts=4 sw=4 et:
|
||||||
|
-->
|
||||||
|
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<title>Error while rendering HTML</title>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<h1>Error while rendering internal qutebrowser page</h1>
|
||||||
|
<p>There was an error while rendering {pagename}.</p>
|
||||||
|
|
||||||
|
<p>This most likely happened because you updated qutebrowser but didn't restart yet.</p>
|
||||||
|
|
||||||
|
<p>If you believe this isn't the case and this is a bug, please do :report.<p>
|
||||||
|
|
||||||
|
<h2>Traceback</h2>
|
||||||
|
<pre>{traceback}</pre>
|
||||||
|
</body>
|
||||||
|
</html>
|
@ -21,10 +21,12 @@
|
|||||||
|
|
||||||
import os
|
import os
|
||||||
import os.path
|
import os.path
|
||||||
|
import traceback
|
||||||
|
|
||||||
import jinja2
|
import jinja2
|
||||||
|
import jinja2.exceptions
|
||||||
|
|
||||||
from qutebrowser.utils import utils
|
from qutebrowser.utils import utils, log
|
||||||
|
|
||||||
from PyQt5.QtCore import QUrl
|
from PyQt5.QtCore import QUrl
|
||||||
|
|
||||||
@ -74,8 +76,13 @@ def resource_url(path):
|
|||||||
|
|
||||||
def render(template, **kwargs):
|
def render(template, **kwargs):
|
||||||
"""Render the given template and pass the given arguments to it."""
|
"""Render the given template and pass the given arguments to it."""
|
||||||
return _env.get_template(template).render(**kwargs)
|
try:
|
||||||
|
return _env.get_template(template).render(**kwargs)
|
||||||
|
except jinja2.exceptions.UndefinedError:
|
||||||
|
log.misc.exception("UndefinedError while rendering " + template)
|
||||||
|
err_template = utils.read_file(os.path.join('html', 'undef_error.html'))
|
||||||
|
tb = traceback.format_exc()
|
||||||
|
return err_template.format(pagename=template, traceback=tb)
|
||||||
|
|
||||||
_env = jinja2.Environment(loader=Loader('html'), autoescape=_guess_autoescape)
|
_env = jinja2.Environment(loader=Loader('html'), autoescape=_guess_autoescape)
|
||||||
_env.globals['resource_url'] = resource_url
|
_env.globals['resource_url'] = resource_url
|
||||||
|
@ -23,21 +23,27 @@ import os
|
|||||||
import os.path
|
import os.path
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
import logging
|
||||||
import jinja2
|
import jinja2
|
||||||
from PyQt5.QtCore import QUrl
|
from PyQt5.QtCore import QUrl
|
||||||
|
|
||||||
from qutebrowser.utils import jinja
|
from qutebrowser.utils import utils, jinja
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture(autouse=True)
|
@pytest.fixture(autouse=True)
|
||||||
def patch_read_file(monkeypatch):
|
def patch_read_file(monkeypatch):
|
||||||
"""pytest fixture to patch utils.read_file."""
|
"""pytest fixture to patch utils.read_file."""
|
||||||
|
real_read_file = utils.read_file
|
||||||
def _read_file(path):
|
def _read_file(path):
|
||||||
"""A read_file which returns a simple template if the path is right."""
|
"""A read_file which returns a simple template if the path is right."""
|
||||||
if path == os.path.join('html', 'test.html'):
|
if path == os.path.join('html', 'test.html'):
|
||||||
return """Hello {{var}}"""
|
return """Hello {{var}}"""
|
||||||
elif path == os.path.join('html', 'test2.html'):
|
elif path == os.path.join('html', 'test2.html'):
|
||||||
return """{{ resource_url('utils/testfile') }}"""
|
return """{{ resource_url('utils/testfile') }}"""
|
||||||
|
elif path == os.path.join('html', 'undef.html'):
|
||||||
|
return """{{ does_not_exist() }}"""
|
||||||
|
elif path == os.path.join('html', 'undef_error.html'):
|
||||||
|
return real_read_file(path)
|
||||||
else:
|
else:
|
||||||
raise IOError("Invalid path {}!".format(path))
|
raise IOError("Invalid path {}!".format(path))
|
||||||
|
|
||||||
@ -87,6 +93,18 @@ def test_utf8():
|
|||||||
assert data == "Hello \u2603"
|
assert data == "Hello \u2603"
|
||||||
|
|
||||||
|
|
||||||
|
def test_undefined_function(caplog):
|
||||||
|
"""Make sure we don't crash if an undefined function is called."""
|
||||||
|
with caplog.at_level(logging.ERROR):
|
||||||
|
data = jinja.render('undef.html')
|
||||||
|
assert 'There was an error while rendering undef.html' in data
|
||||||
|
assert "'does_not_exist' is undefined" in data
|
||||||
|
assert data.startswith('<!DOCTYPE html>')
|
||||||
|
|
||||||
|
assert len(caplog.records) == 1
|
||||||
|
assert caplog.records[0].msg == "UndefinedError while rendering undef.html"
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize('name, expected', [
|
@pytest.mark.parametrize('name, expected', [
|
||||||
(None, False),
|
(None, False),
|
||||||
('foo', False),
|
('foo', False),
|
||||||
|
Loading…
Reference in New Issue
Block a user