Cache HTML/JS resource files when starting
This mostly reverts 9edc5a665e
(see #1362).
Fixes #1943
This commit is contained in:
parent
78623f4ec8
commit
b4a2352833
@ -117,6 +117,8 @@ Fixed
|
|||||||
- Crash when opening an invalid URL from an application on macOS.
|
- Crash when opening an invalid URL from an application on macOS.
|
||||||
- Crash with an empty `completion.timestamp_format`.
|
- Crash with an empty `completion.timestamp_format`.
|
||||||
- Crash when `completion.min_chars` is set in some cases.
|
- Crash when `completion.min_chars` is set in some cases.
|
||||||
|
- HTML/JS resource files are now read into RAM on start to avoid crashes when
|
||||||
|
changing qutebrowser versions while it's open.
|
||||||
|
|
||||||
Removed
|
Removed
|
||||||
~~~~~~~
|
~~~~~~~
|
||||||
|
@ -95,6 +95,7 @@ def run(args):
|
|||||||
|
|
||||||
log.init.debug("Initializing directories...")
|
log.init.debug("Initializing directories...")
|
||||||
standarddir.init(args)
|
standarddir.init(args)
|
||||||
|
utils.preload_resources()
|
||||||
|
|
||||||
log.init.debug("Initializing config...")
|
log.init.debug("Initializing config...")
|
||||||
configinit.early_init(args)
|
configinit.early_init(args)
|
||||||
|
@ -1,22 +0,0 @@
|
|||||||
<!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>
|
|
@ -22,12 +22,10 @@
|
|||||||
import os
|
import os
|
||||||
import os.path
|
import os.path
|
||||||
import contextlib
|
import contextlib
|
||||||
import traceback
|
|
||||||
import mimetypes
|
import mimetypes
|
||||||
import html
|
import html
|
||||||
|
|
||||||
import jinja2
|
import jinja2
|
||||||
import jinja2.exceptions
|
|
||||||
from PyQt5.QtCore import QUrl
|
from PyQt5.QtCore import QUrl
|
||||||
|
|
||||||
from qutebrowser.utils import utils, urlutils, log
|
from qutebrowser.utils import utils, urlutils, log
|
||||||
@ -125,14 +123,7 @@ class Environment(jinja2.Environment):
|
|||||||
|
|
||||||
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."""
|
||||||
try:
|
return environment.get_template(template).render(**kwargs)
|
||||||
return environment.get_template(template).render(**kwargs)
|
|
||||||
except jinja2.exceptions.UndefinedError:
|
|
||||||
log.misc.exception("UndefinedError while rendering " + template)
|
|
||||||
err_path = os.path.join('html', 'undef_error.html')
|
|
||||||
err_template = utils.read_file(err_path)
|
|
||||||
tb = traceback.format_exc()
|
|
||||||
return err_template.format(pagename=template, traceback=tb)
|
|
||||||
|
|
||||||
|
|
||||||
environment = Environment()
|
environment = Environment()
|
||||||
|
@ -32,6 +32,7 @@ import functools
|
|||||||
import contextlib
|
import contextlib
|
||||||
import socket
|
import socket
|
||||||
import shlex
|
import shlex
|
||||||
|
import glob
|
||||||
|
|
||||||
from PyQt5.QtCore import QUrl
|
from PyQt5.QtCore import QUrl
|
||||||
from PyQt5.QtGui import QColor, QClipboard, QDesktopServices
|
from PyQt5.QtGui import QColor, QClipboard, QDesktopServices
|
||||||
@ -51,6 +52,7 @@ from qutebrowser.utils import qtutils, log
|
|||||||
|
|
||||||
fake_clipboard = None
|
fake_clipboard = None
|
||||||
log_clipboard = False
|
log_clipboard = False
|
||||||
|
_resource_cache = {}
|
||||||
|
|
||||||
is_mac = sys.platform.startswith('darwin')
|
is_mac = sys.platform.startswith('darwin')
|
||||||
is_linux = sys.platform.startswith('linux')
|
is_linux = sys.platform.startswith('linux')
|
||||||
@ -140,6 +142,15 @@ def compact_text(text, elidelength=None):
|
|||||||
return out
|
return out
|
||||||
|
|
||||||
|
|
||||||
|
def preload_resources():
|
||||||
|
"""Load resource files into the cache."""
|
||||||
|
for subdir, pattern in [('html', '*.html'), ('javascript', '*.js')]:
|
||||||
|
path = resource_filename(subdir)
|
||||||
|
for full_path in glob.glob(os.path.join(path, pattern)):
|
||||||
|
sub_path = os.path.join(subdir, os.path.basename(full_path))
|
||||||
|
_resource_cache[sub_path] = read_file(sub_path)
|
||||||
|
|
||||||
|
|
||||||
def read_file(filename, binary=False):
|
def read_file(filename, binary=False):
|
||||||
"""Get the contents of a file contained with qutebrowser.
|
"""Get the contents of a file contained with qutebrowser.
|
||||||
|
|
||||||
@ -151,6 +162,9 @@ def read_file(filename, binary=False):
|
|||||||
Return:
|
Return:
|
||||||
The file contents as string.
|
The file contents as string.
|
||||||
"""
|
"""
|
||||||
|
if not binary and filename in _resource_cache:
|
||||||
|
return _resource_cache[filename]
|
||||||
|
|
||||||
if hasattr(sys, 'frozen'):
|
if hasattr(sys, 'frozen'):
|
||||||
# PyInstaller doesn't support pkg_resources :(
|
# PyInstaller doesn't support pkg_resources :(
|
||||||
# https://github.com/pyinstaller/pyinstaller/wiki/FAQ#misc
|
# https://github.com/pyinstaller/pyinstaller/wiki/FAQ#misc
|
||||||
|
@ -23,6 +23,7 @@ import os
|
|||||||
import os.path
|
import os.path
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
|
import jinja2.exceptions
|
||||||
import pytest
|
import pytest
|
||||||
from PyQt5.QtCore import QUrl
|
from PyQt5.QtCore import QUrl
|
||||||
|
|
||||||
@ -32,7 +33,6 @@ 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
|
|
||||||
real_resource_filename = utils.resource_filename
|
real_resource_filename = utils.resource_filename
|
||||||
|
|
||||||
def _read_file(path, binary=False):
|
def _read_file(path, binary=False):
|
||||||
@ -52,9 +52,6 @@ def patch_read_file(monkeypatch):
|
|||||||
elif path == os.path.join('html', 'undef.html'):
|
elif path == os.path.join('html', 'undef.html'):
|
||||||
assert not binary
|
assert not binary
|
||||||
return """{{ does_not_exist() }}"""
|
return """{{ does_not_exist() }}"""
|
||||||
elif path == os.path.join('html', 'undef_error.html'):
|
|
||||||
assert not binary
|
|
||||||
return real_read_file(path)
|
|
||||||
elif path == os.path.join('html', 'attributeerror.html'):
|
elif path == os.path.join('html', 'attributeerror.html'):
|
||||||
assert not binary
|
assert not binary
|
||||||
return """{{ obj.foobar }}"""
|
return """{{ obj.foobar }}"""
|
||||||
@ -129,15 +126,9 @@ def test_utf8():
|
|||||||
|
|
||||||
|
|
||||||
def test_undefined_function(caplog):
|
def test_undefined_function(caplog):
|
||||||
"""Make sure we don't crash if an undefined function is called."""
|
"""Make sure undefined attributes crash since we preload resources.."""
|
||||||
with caplog.at_level(logging.ERROR):
|
with pytest.raises(jinja2.exceptions.UndefinedError):
|
||||||
data = jinja.render('undef.html')
|
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"
|
|
||||||
|
|
||||||
|
|
||||||
def test_attribute_error():
|
def test_attribute_error():
|
||||||
|
@ -133,6 +133,14 @@ class TestReadFile:
|
|||||||
content = utils.read_file(os.path.join('utils', 'testfile'))
|
content = utils.read_file(os.path.join('utils', 'testfile'))
|
||||||
assert content.splitlines()[0] == "Hello World!"
|
assert content.splitlines()[0] == "Hello World!"
|
||||||
|
|
||||||
|
@pytest.mark.parametrize('filename', ['javascript/scroll.js',
|
||||||
|
'html/error.html'])
|
||||||
|
def test_read_cached_file(self, mocker, filename):
|
||||||
|
utils.preload_resources()
|
||||||
|
m = mocker.patch('pkg_resources.resource_string')
|
||||||
|
utils.read_file(filename)
|
||||||
|
m.assert_not_called()
|
||||||
|
|
||||||
def test_readfile_binary(self):
|
def test_readfile_binary(self):
|
||||||
"""Read a test file in binary mode."""
|
"""Read a test file in binary mode."""
|
||||||
content = utils.read_file(os.path.join('utils', 'testfile'),
|
content = utils.read_file(os.path.join('utils', 'testfile'),
|
||||||
|
Loading…
Reference in New Issue
Block a user