summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorFlorian Bruhin <me@the-compiler.org>2021-03-11 17:23:51 +0100
committerFlorian Bruhin <me@the-compiler.org>2021-03-11 17:23:51 +0100
commit01b57a33beeb00cf77f7ee28c19273da52f7aea1 (patch)
tree73b13330d4c28b3fdfa9b3128e2ebfa22c8b1385
parentf54b76b0a49337b03978434808e438cb2cb6cb7b (diff)
parentfca59c1a27dd54e820b0ba69ce240276d6d22c7a (diff)
downloadqutebrowser-01b57a33beeb00cf77f7ee28c19273da52f7aea1.tar.gz
qutebrowser-01b57a33beeb00cf77f7ee28c19273da52f7aea1.zip
Merge remote-tracking branch 'origin/pr/6234'
-rw-r--r--qutebrowser/app.py7
-rw-r--r--qutebrowser/browser/network/pac.py4
-rw-r--r--qutebrowser/browser/pdfjs.py4
-rw-r--r--qutebrowser/browser/qutescheme.py14
-rw-r--r--qutebrowser/browser/webengine/webenginetab.py12
-rw-r--r--qutebrowser/browser/webkit/webkittab.py5
-rw-r--r--qutebrowser/config/configdata.py4
-rw-r--r--qutebrowser/utils/jinja.py8
-rw-r--r--qutebrowser/utils/resources.py133
-rw-r--r--qutebrowser/utils/utils.py114
-rw-r--r--qutebrowser/utils/version.py4
-rw-r--r--scripts/dev/check_coverage.py2
-rw-r--r--tests/unit/browser/test_pdfjs.py2
-rw-r--r--tests/unit/browser/test_qutescheme.py13
-rw-r--r--tests/unit/utils/test_jinja.py6
-rw-r--r--tests/unit/utils/test_resources.py146
-rw-r--r--tests/unit/utils/test_utils.py110
-rw-r--r--tests/unit/utils/test_version.py6
18 files changed, 330 insertions, 264 deletions
diff --git a/qutebrowser/app.py b/qutebrowser/app.py
index 5a9c956b0..1a18881b5 100644
--- a/qutebrowser/app.py
+++ b/qutebrowser/app.py
@@ -66,7 +66,8 @@ from qutebrowser.misc import (ipc, savemanager, sessions, crashsignal,
earlyinit, sql, cmdhistory, backendproblem,
objects, quitter)
from qutebrowser.utils import (log, version, message, utils, urlutils, objreg,
- usertypes, standarddir, error, qtutils, debug)
+ resources, usertypes, standarddir,
+ error, qtutils, debug)
# pylint: disable=unused-import
# We import those to run the cmdutils.register decorators.
from qutebrowser.mainwindow.statusbar import command
@@ -86,7 +87,7 @@ def run(args):
log.init.debug("Initializing directories...")
standarddir.init(args)
- utils.preload_resources()
+ resources.preload()
log.init.debug("Initializing config...")
configinit.early_init(args)
@@ -395,7 +396,7 @@ def _open_special_pages(args):
return
try:
- changelog = utils.read_file('html/doc/changelog.html')
+ changelog = resources.read_file('html/doc/changelog.html')
except OSError as e:
log.init.warning(f"Not showing changelog due to {e}")
return
diff --git a/qutebrowser/browser/network/pac.py b/qutebrowser/browser/network/pac.py
index 4a4768dde..5ade5d4ac 100644
--- a/qutebrowser/browser/network/pac.py
+++ b/qutebrowser/browser/network/pac.py
@@ -29,7 +29,7 @@ from PyQt5.QtNetwork import (QNetworkProxy, QNetworkRequest, QHostInfo,
QHostAddress)
from PyQt5.QtQml import QJSEngine, QJSValue
-from qutebrowser.utils import log, utils, qtutils
+from qutebrowser.utils import log, utils, qtutils, resources
class ParseProxyError(Exception):
@@ -190,7 +190,7 @@ class PACResolver:
self._engine.globalObject().setProperty(
"PAC", self._engine.newQObject(self._ctx))
self._evaluate(_PACContext.JS_DEFINITIONS, "pac_js_definitions")
- self._evaluate(utils.read_file("javascript/pac_utils.js"), "pac_utils")
+ self._evaluate(resources.read_file("javascript/pac_utils.js"), "pac_utils")
proxy_config = self._engine.newObject()
proxy_config.setProperty("bindings", self._engine.newObject())
self._engine.globalObject().setProperty("ProxyConfig", proxy_config)
diff --git a/qutebrowser/browser/pdfjs.py b/qutebrowser/browser/pdfjs.py
index 97074767b..c180c55f8 100644
--- a/qutebrowser/browser/pdfjs.py
+++ b/qutebrowser/browser/pdfjs.py
@@ -24,7 +24,7 @@ import os
from PyQt5.QtCore import QUrl, QUrlQuery
-from qutebrowser.utils import utils, javascript, jinja, standarddir, log
+from qutebrowser.utils import resources, javascript, jinja, standarddir, log
from qutebrowser.config import config
@@ -149,7 +149,7 @@ def get_pdfjs_res_and_path(path):
if content is None:
res_path = '3rdparty/pdfjs/{}'.format(path)
try:
- content = utils.read_file_binary(res_path)
+ content = resources.read_file_binary(res_path)
except FileNotFoundError:
raise PDFJSNotFound(path) from None
except OSError as e:
diff --git a/qutebrowser/browser/qutescheme.py b/qutebrowser/browser/qutescheme.py
index 169c92325..cb04586ff 100644
--- a/qutebrowser/browser/qutescheme.py
+++ b/qutebrowser/browser/qutescheme.py
@@ -40,7 +40,7 @@ import qutebrowser
from qutebrowser.browser import pdfjs, downloads, history
from qutebrowser.config import config, configdata, configexc
from qutebrowser.utils import (version, utils, jinja, log, message, docutils,
- objreg, standarddir)
+ resources, objreg, standarddir)
from qutebrowser.qt import sip
@@ -271,7 +271,7 @@ def qute_javascript(url: QUrl) -> _HandlerRet:
path = url.path()
if path:
path = "javascript" + os.sep.join(path.split('/'))
- return 'text/html', utils.read_file(path)
+ return 'text/html', resources.read_file(path)
else:
raise UrlInvalidError("No file specified")
@@ -345,14 +345,14 @@ def qute_log(url: QUrl) -> _HandlerRet:
@add_handler('gpl')
def qute_gpl(_url: QUrl) -> _HandlerRet:
"""Handler for qute://gpl. Return HTML content as string."""
- return 'text/html', utils.read_file('html/license.html')
+ return 'text/html', resources.read_file('html/license.html')
def _asciidoc_fallback_path(html_path: str) -> Optional[str]:
"""Fall back to plaintext asciidoc if the HTML is unavailable."""
path = html_path.replace('.html', '.asciidoc')
try:
- return utils.read_file(path)
+ return resources.read_file(path)
except OSError:
return None
@@ -372,14 +372,14 @@ def qute_help(url: QUrl) -> _HandlerRet:
path = 'html/doc/{}'.format(urlpath)
if not urlpath.endswith('.html'):
try:
- bdata = utils.read_file_binary(path)
+ bdata = resources.read_file_binary(path)
except OSError as e:
raise SchemeOSError(e)
mimetype = utils.guess_mimetype(urlpath)
return mimetype, bdata
try:
- data = utils.read_file(path)
+ data = resources.read_file(path)
except OSError:
asciidoc = _asciidoc_fallback_path(path)
@@ -575,7 +575,7 @@ def qute_resource(url: QUrl) -> _HandlerRet:
path = url.path().lstrip('/')
mimetype = utils.guess_mimetype(path, fallback=True)
try:
- data = utils.read_file_binary(path)
+ data = resources.read_file_binary(path)
except FileNotFoundError as e:
raise NotFoundError(str(e))
return mimetype, data
diff --git a/qutebrowser/browser/webengine/webenginetab.py b/qutebrowser/browser/webengine/webenginetab.py
index 450d68751..69ddbe6e1 100644
--- a/qutebrowser/browser/webengine/webenginetab.py
+++ b/qutebrowser/browser/webengine/webenginetab.py
@@ -38,7 +38,7 @@ from qutebrowser.browser.webengine import (webview, webengineelem, tabhistory,
webengineinspector)
from qutebrowser.utils import (usertypes, qtutils, log, javascript, utils,
- message, jinja, debug, version)
+ resources, message, jinja, debug, version)
from qutebrowser.qt import sip
from qutebrowser.misc import objects, miscwidgets
@@ -1038,9 +1038,9 @@ class _WebEngineScripts(QObject):
"""Initialize global qutebrowser JavaScript."""
js_code = javascript.wrap_global(
'scripts',
- utils.read_file('javascript/scroll.js'),
- utils.read_file('javascript/webelem.js'),
- utils.read_file('javascript/caret.js'),
+ resources.read_file('javascript/scroll.js'),
+ resources.read_file('javascript/webelem.js'),
+ resources.read_file('javascript/caret.js'),
)
# FIXME:qtwebengine what about subframes=True?
self._inject_js('js', js_code, subframes=True)
@@ -1061,7 +1061,7 @@ class _WebEngineScripts(QObject):
css = shared.get_user_stylesheet()
js_code = javascript.wrap_global(
'stylesheet',
- utils.read_file('javascript/stylesheet.js'),
+ resources.read_file('javascript/stylesheet.js'),
javascript.assemble('stylesheet', 'set_css', css),
)
self._inject_js('stylesheet', js_code, subframes=True)
@@ -1169,7 +1169,7 @@ class _WebEngineScripts(QObject):
for quirk in quirks:
if not quirk.predicate:
continue
- src = utils.read_file(f'javascript/quirks/{quirk.filename}.user.js')
+ src = resources.read_file(f'javascript/quirks/{quirk.filename}.user.js')
self._inject_js(
f'quirk_{quirk.filename}',
src,
diff --git a/qutebrowser/browser/webkit/webkittab.py b/qutebrowser/browser/webkit/webkittab.py
index f910cf676..df3491ec2 100644
--- a/qutebrowser/browser/webkit/webkittab.py
+++ b/qutebrowser/browser/webkit/webkittab.py
@@ -34,8 +34,7 @@ from PyQt5.QtPrintSupport import QPrinter
from qutebrowser.browser import browsertab, shared
from qutebrowser.browser.webkit import (webview, tabhistory, webkitelem,
webkitsettings, webkitinspector)
-
-from qutebrowser.utils import qtutils, usertypes, utils, log, debug
+from qutebrowser.utils import qtutils, usertypes, utils, log, debug, resources
from qutebrowser.keyinput import modeman
from qutebrowser.qt import sip
@@ -228,7 +227,7 @@ class WebKitCaret(browsertab.AbstractCaret):
# true in caret mode.
if self._selection_state is browsertab.SelectionState.none:
self._widget.page().currentFrame().evaluateJavaScript(
- utils.read_file('javascript/position_caret.js'))
+ resources.read_file('javascript/position_caret.js'))
@pyqtSlot(usertypes.KeyMode)
def _on_mode_left(self, _mode):
diff --git a/qutebrowser/config/configdata.py b/qutebrowser/config/configdata.py
index 6cead0732..ec4efc375 100644
--- a/qutebrowser/config/configdata.py
+++ b/qutebrowser/config/configdata.py
@@ -30,7 +30,7 @@ import functools
import dataclasses
from qutebrowser.config import configtypes
-from qutebrowser.utils import usertypes, qtutils, utils
+from qutebrowser.utils import usertypes, qtutils, utils, resources
from qutebrowser.misc import debugcachestats
DATA = cast(Mapping[str, 'Option'], None)
@@ -272,4 +272,4 @@ def is_valid_prefix(prefix: str) -> bool:
def init() -> None:
"""Initialize configdata from the YAML file."""
global DATA, MIGRATIONS
- DATA, MIGRATIONS = _read_yaml(utils.read_file('config/configdata.yml'))
+ DATA, MIGRATIONS = _read_yaml(resources.read_file('config/configdata.yml'))
diff --git a/qutebrowser/utils/jinja.py b/qutebrowser/utils/jinja.py
index e5cd853aa..61d8ccdad 100644
--- a/qutebrowser/utils/jinja.py
+++ b/qutebrowser/utils/jinja.py
@@ -31,7 +31,7 @@ import jinja2
import jinja2.nodes
from PyQt5.QtCore import QUrl
-from qutebrowser.utils import utils, urlutils, log, qtutils
+from qutebrowser.utils import utils, urlutils, log, qtutils, resources
from qutebrowser.misc import debugcachestats
@@ -56,7 +56,7 @@ html_fallback = """
class Loader(jinja2.BaseLoader):
- """Jinja loader which uses utils.read_file to load templates.
+ """Jinja loader which uses resources.read_file to load templates.
Attributes:
_subdir: The subdirectory to find templates in.
@@ -72,7 +72,7 @@ class Loader(jinja2.BaseLoader):
) -> Tuple[str, str, Callable[[], bool]]:
path = os.path.join(self._subdir, template)
try:
- source = utils.read_file(path)
+ source = resources.read_file(path)
except OSError as e:
source = html_fallback.replace("%ERROR%", html.escape(str(e)))
source = source.replace("%FILE%", html.escape(template))
@@ -119,7 +119,7 @@ class Environment(jinja2.Environment):
def _data_url(self, path: str) -> str:
"""Get a data: url for the broken qutebrowser logo."""
- data = utils.read_file_binary(path)
+ data = resources.read_file_binary(path)
mimetype = utils.guess_mimetype(path)
return urlutils.data_url(mimetype, data).toString()
diff --git a/qutebrowser/utils/resources.py b/qutebrowser/utils/resources.py
new file mode 100644
index 000000000..e812ece99
--- /dev/null
+++ b/qutebrowser/utils/resources.py
@@ -0,0 +1,133 @@
+# vim: ft=python fileencoding=utf-8 sts=4 sw=4 et:
+
+# Copyright 2014-2021 Florian Bruhin (The Compiler) <mail@qutebrowser.org>
+#
+# This file is part of qutebrowser.
+#
+# qutebrowser is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# qutebrowser is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with qutebrowser. If not, see <https://www.gnu.org/licenses/>.
+
+"""Resources related utilities."""
+
+import os.path
+import sys
+import contextlib
+import posixpath
+import pathlib
+from typing import (Iterator, Iterable)
+
+
+# We cannot use the stdlib version on 3.7-3.8 because we need the files() API.
+if sys.version_info >= (3, 9):
+ import importlib.resources as importlib_resources
+else: # pragma: no cover
+ import importlib_resources
+
+import qutebrowser
+cache = {}
+
+def path(filename: str) -> pathlib.Path:
+ """Get a pathlib.Path object for a resource."""
+ assert not posixpath.isabs(filename), filename
+ assert os.path.pardir not in filename.split(posixpath.sep), filename
+
+ if hasattr(sys, 'frozen'):
+ # For PyInstaller, where we can't store resource files in a qutebrowser/ folder
+ # because the executable is already named "qutebrowser" (at least on macOS).
+ return pathlib.Path(sys.executable).parent / filename
+
+ return importlib_resources.files(qutebrowser) / filename
+
+@contextlib.contextmanager
+def keyerror_workaround() -> Iterator[None]:
+ """Re-raise KeyErrors as FileNotFoundErrors.
+
+ WORKAROUND for zipfile.Path resources raising KeyError when a file was notfound:
+ https://bugs.python.org/issue43063
+
+ Only needed for Python 3.8 and 3.9.
+ """
+ try:
+ yield
+ except KeyError as e:
+ raise FileNotFoundError(str(e))
+
+
+def _glob(
+ resource_path: pathlib.Path,
+ subdir: str,
+ ext: str,
+) -> Iterable[str]:
+ """Find resources with the given extension.
+
+ Yields a resource name like "html/log.html" (as string).
+ """
+ assert '*' not in ext, ext
+ assert ext.startswith('.'), ext
+ glob_path = resource_path / subdir
+
+ if isinstance(resource_path, pathlib.Path):
+ for full_path in glob_path.glob(f'*{ext}'): # . is contained in ext
+ yield full_path.relative_to(resource_path).as_posix()
+ else: # zipfile.Path or importlib_resources compat object
+ # Unfortunately, we can't tell mypy about resource_path being of type
+ # Union[pathlib.Path, zipfile.Path] because we set "python_version = 3.6" in
+ # .mypy.ini, but the zipfiel stubs (correctly) only declare zipfile.Path with
+ # Python 3.8...
+ assert glob_path.is_dir(), path # type: ignore[unreachable]
+ for subpath in glob_path.iterdir():
+ if subpath.name.endswith(ext):
+ yield posixpath.join(subdir, subpath.name)
+
+
+def preload() -> None:
+ """Load resource files into the cache."""
+ resource_path = path('')
+ for subdir, ext in [
+ ('html', '.html'),
+ ('javascript', '.js'),
+ ('javascript/quirks', '.js'),
+ ]:
+ for name in _glob(resource_path, subdir, ext):
+ cache[name] = read_file(name)
+
+
+def read_file(filename: str) -> str:
+ """Get the contents of a file contained with qutebrowser.
+
+ Args:
+ filename: The filename to open as string.
+
+ Return:
+ The file contents as string.
+ """
+ if filename in cache:
+ return cache[filename]
+
+ file_path = path(filename)
+ with keyerror_workaround():
+ return file_path.read_text(encoding='utf-8')
+
+
+def read_file_binary(filename: str) -> bytes:
+ """Get the contents of a binary file contained with qutebrowser.
+
+ Args:
+ filename: The filename to open as string.
+
+ Return:
+ The file contents as a bytes object.
+ """
+ file_binary_path = path(filename)
+ with keyerror_workaround():
+ return file_binary_path.read_bytes()
diff --git a/qutebrowser/utils/utils.py b/qutebrowser/utils/utils.py
index af9d5fad7..03a3c7842 100644
--- a/qutebrowser/utils/utils.py
+++ b/qutebrowser/utils/utils.py
@@ -30,14 +30,13 @@ import datetime
import traceback
import functools
import contextlib
-import posixpath
import shlex
import mimetypes
-import pathlib
import ctypes
import ctypes.util
-from typing import (Any, Callable, IO, Iterator, Optional, Sequence, Tuple, Type, Union,
- Iterable, TypeVar, TYPE_CHECKING)
+from typing import (Any, Callable, IO, Iterator,
+ Optional, Sequence, Tuple, Type, Union,
+ TypeVar, TYPE_CHECKING)
try:
# Protocol was added in Python 3.8
from typing import Protocol
@@ -50,11 +49,7 @@ except ImportError: # pragma: no cover
from PyQt5.QtCore import QUrl, QVersionNumber, QRect
from PyQt5.QtGui import QClipboard, QDesktopServices
from PyQt5.QtWidgets import QApplication
-# We cannot use the stdlib version on 3.7-3.8 because we need the files() API.
-if sys.version_info >= (3, 9):
- import importlib.resources as importlib_resources
-else: # pragma: no cover
- import importlib_resources
+
import yaml
try:
from yaml import (CSafeLoader as YamlLoader,
@@ -65,13 +60,10 @@ except ImportError: # pragma: no cover
SafeDumper as YamlDumper)
YAML_C_EXT = False
-import qutebrowser
from qutebrowser.utils import log
-
fake_clipboard = None
log_clipboard = False
-_resource_cache = {}
is_mac = sys.platform.startswith('darwin')
is_linux = sys.platform.startswith('linux')
@@ -244,104 +236,6 @@ def compact_text(text: str, elidelength: int = None) -> str:
return out
-def _resource_path(filename: str) -> pathlib.Path:
- """Get a pathlib.Path object for a resource."""
- assert not posixpath.isabs(filename), filename
- assert os.path.pardir not in filename.split(posixpath.sep), filename
-
- if hasattr(sys, 'frozen'):
- # For PyInstaller, where we can't store resource files in a qutebrowser/ folder
- # because the executable is already named "qutebrowser" (at least on macOS).
- return pathlib.Path(sys.executable).parent / filename
-
- return importlib_resources.files(qutebrowser) / filename
-
-
-@contextlib.contextmanager
-def _resource_keyerror_workaround() -> Iterator[None]:
- """Re-raise KeyErrors as FileNotFoundErrors.
-
- WORKAROUND for zipfile.Path resources raising KeyError when a file was notfound:
- https://bugs.python.org/issue43063
-
- Only needed for Python 3.8 and 3.9.
- """
- try:
- yield
- except KeyError as e:
- raise FileNotFoundError(str(e))
-
-
-def _glob_resources(
- resource_path: pathlib.Path,
- subdir: str,
- ext: str,
-) -> Iterable[str]:
- """Find resources with the given extension.
-
- Yields a resource name like "html/log.html" (as string).
- """
- assert '*' not in ext, ext
- assert ext.startswith('.'), ext
- path = resource_path / subdir
-
- if isinstance(resource_path, pathlib.Path):
- for full_path in path.glob(f'*{ext}'): # . is contained in ext
- yield full_path.relative_to(resource_path).as_posix()
- else: # zipfile.Path or importlib_resources compat object
- # Unfortunately, we can't tell mypy about resource_path being of type
- # Union[pathlib.Path, zipfile.Path] because we set "python_version = 3.6" in
- # .mypy.ini, but the zipfiel stubs (correctly) only declare zipfile.Path with
- # Python 3.8...
- assert path.is_dir(), path # type: ignore[unreachable]
- for subpath in path.iterdir():
- if subpath.name.endswith(ext):
- yield posixpath.join(subdir, subpath.name)
-
-
-def preload_resources() -> None:
- """Load resource files into the cache."""
- resource_path = _resource_path('')
- for subdir, ext in [
- ('html', '.html'),
- ('javascript', '.js'),
- ('javascript/quirks', '.js'),
- ]:
- for name in _glob_resources(resource_path, subdir, ext):
- _resource_cache[name] = read_file(name)
-
-
-def read_file(filename: str) -> str:
- """Get the contents of a file contained with qutebrowser.
-
- Args:
- filename: The filename to open as string.
-
- Return:
- The file contents as string.
- """
- if filename in _resource_cache:
- return _resource_cache[filename]
-
- path = _resource_path(filename)
- with _resource_keyerror_workaround():
- return path.read_text(encoding='utf-8')
-
-
-def read_file_binary(filename: str) -> bytes:
- """Get the contents of a binary file contained with qutebrowser.
-
- Args:
- filename: The filename to open as string.
-
- Return:
- The file contents as a bytes object.
- """
- path = _resource_path(filename)
- with _resource_keyerror_workaround():
- return path.read_bytes()
-
-
def format_seconds(total_seconds: int) -> str:
"""Format a count of seconds to get a [H:]M:SS string."""
prefix = '-' if total_seconds < 0 else ''
diff --git a/qutebrowser/utils/version.py b/qutebrowser/utils/version.py
index 4a4455c99..46916c516 100644
--- a/qutebrowser/utils/version.py
+++ b/qutebrowser/utils/version.py
@@ -53,7 +53,7 @@ except ImportError: # pragma: no cover
import qutebrowser
-from qutebrowser.utils import log, utils, standarddir, usertypes, message
+from qutebrowser.utils import log, utils, standarddir, usertypes, message, resources
from qutebrowser.misc import objects, earlyinit, sql, httpclient, pastebin, elf
from qutebrowser.browser import pdfjs
from qutebrowser.config import config, websettings
@@ -218,7 +218,7 @@ def _git_str() -> Optional[str]:
return commit
# If that fails, check the git-commit-id file.
try:
- return utils.read_file('git-commit-id')
+ return resources.read_file('git-commit-id')
except (OSError, ImportError):
return None
diff --git a/scripts/dev/check_coverage.py b/scripts/dev/check_coverage.py
index bc1894e43..c66cb3e8d 100644
--- a/scripts/dev/check_coverage.py
+++ b/scripts/dev/check_coverage.py
@@ -187,6 +187,8 @@ PERFECT_FILES = [
'qutebrowser/utils/usertypes.py'),
('tests/unit/utils/test_utils.py',
'qutebrowser/utils/utils.py'),
+ ('tests/unit/utils/test_resources.py',
+ 'qutebrowser/utils/resources.py'),
('tests/unit/utils/test_version.py',
'qutebrowser/utils/version.py'),
('tests/unit/utils/test_debug.py',
diff --git a/tests/unit/browser/test_pdfjs.py b/tests/unit/browser/test_pdfjs.py
index 788209d6f..86b875be5 100644
--- a/tests/unit/browser/test_pdfjs.py
+++ b/tests/unit/browser/test_pdfjs.py
@@ -77,7 +77,7 @@ class TestResources:
@pytest.fixture
def read_file_mock(self, mocker):
- return mocker.patch.object(pdfjs.utils, 'read_file_binary', autospec=True)
+ return mocker.patch.object(pdfjs.resources, 'read_file_binary', autospec=True)
def test_get_pdfjs_res_system(self, read_system_mock):
read_system_mock.return_value = (b'content', 'path')
diff --git a/tests/unit/browser/test_qutescheme.py b/tests/unit/browser/test_qutescheme.py
index 213df4e0c..2ae939596 100644
--- a/tests/unit/browser/test_qutescheme.py
+++ b/tests/unit/browser/test_qutescheme.py
@@ -28,7 +28,7 @@ from PyQt5.QtCore import QUrl, QUrlQuery
import pytest
from qutebrowser.browser import qutescheme, pdfjs, downloads
-from qutebrowser.utils import utils
+from qutebrowser.utils import resources
class TestJavascriptHandler:
@@ -43,15 +43,15 @@ class TestJavascriptHandler:
@pytest.fixture(autouse=True)
def patch_read_file(self, monkeypatch):
- """Patch utils.read_file to return few fake JS files."""
+ """Patch resources.read_file to return few fake JS files."""
def _read_file(path):
- """Faked utils.read_file."""
+ """Faked resources.read_file."""
for filename, content in self.js_files:
if path == os.path.join('javascript', filename):
return content
raise OSError("File not found {}!".format(path))
- monkeypatch.setattr(utils, 'read_file', _read_file)
+ monkeypatch.setattr(resources, 'read_file', _read_file)
@pytest.mark.parametrize("filename, content", js_files)
def test_qutejavascript(self, filename, content):
@@ -165,8 +165,9 @@ class TestHelpHandler:
assert path == name
return data
- monkeypatch.setattr(qutescheme.utils, 'read_file', _read_file)
- monkeypatch.setattr(qutescheme.utils, 'read_file_binary', _read_file_binary)
+ monkeypatch.setattr(qutescheme.resources, 'read_file', _read_file)
+ monkeypatch.setattr(qutescheme.resources,
+ 'read_file_binary', _read_file_binary)
return _patch
def test_unknown_file_type(self, data_patcher):
diff --git a/tests/unit/utils/test_jinja.py b/tests/unit/utils/test_jinja.py
index 5555560bf..0ef03725c 100644
--- a/tests/unit/utils/test_jinja.py
+++ b/tests/unit/utils/test_jinja.py
@@ -33,7 +33,7 @@ from qutebrowser.config import configexc
@pytest.fixture(autouse=True)
def patch_read_file(monkeypatch):
- """pytest fixture to patch utils.read_file."""
+ """pytest fixture to patch resources.read_file."""
def _read_file(path):
"""A read_file which returns a simple template if the path is right."""
if path == os.path.join('html', 'test.html'):
@@ -55,8 +55,8 @@ def patch_read_file(monkeypatch):
else:
raise OSError("Invalid path {}!".format(path))
- monkeypatch.setattr(jinja.utils, 'read_file', _read_file)
- monkeypatch.setattr(jinja.utils, 'read_file_binary', _read_file_binary)
+ monkeypatch.setattr(jinja.resources, 'read_file', _read_file)
+ monkeypatch.setattr(jinja.resources, 'read_file_binary', _read_file_binary)
def test_simple_template():
diff --git a/tests/unit/utils/test_resources.py b/tests/unit/utils/test_resources.py
new file mode 100644
index 000000000..d8af64cb9
--- /dev/null
+++ b/tests/unit/utils/test_resources.py
@@ -0,0 +1,146 @@
+# vim: ft=python fileencoding=utf-8 sts=4 sw=4 et:
+
+# Copyright 2014-2021 Florian Bruhin (The Compiler) <mail@qutebrowser.org>
+#
+# This file is part of qutebrowser.
+#
+# qutebrowser is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# qutebrowser is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with qutebrowser. If not, see <https://www.gnu.org/licenses/>.
+
+"""Tests for qutebrowser.utils.resources."""
+
+import sys
+import os.path
+import zipfile
+import pytest
+import qutebrowser
+from qutebrowser.utils import utils, resources
+
+
+@pytest.fixture(params=[True, False])
+def freezer(request, monkeypatch):
+ if request.param and not getattr(sys, 'frozen', False):
+ monkeypatch.setattr(sys, 'frozen', True, raising=False)
+ monkeypatch.setattr(sys, 'executable', qutebrowser.__file__)
+ elif not request.param and getattr(sys, 'frozen', False):
+ # Want to test unfrozen tests, but we are frozen
+ pytest.skip("Can't run with sys.frozen = True!")
+
+
+@pytest.mark.usefixtures('freezer')
+class TestReadFile:
+
+ @pytest.fixture
+ def package_path(self, tmp_path):
+ return tmp_path / 'qutebrowser'
+
+ @pytest.fixture
+ def html_path(self, package_path):
+ path = package_path / 'html'
+ path.mkdir(parents=True)
+
+ for filename in ['test1.html', 'test2.html', 'README', 'unrelatedhtml']:
+ (path / filename).touch()
+
+ subdir = path / 'subdir'
+ subdir.mkdir()
+ (subdir / 'subdir-file.html').touch()
+
+ return path
+
+ @pytest.fixture
+ def html_zip(self, tmp_path, html_path):
+ if not hasattr(zipfile, 'Path'):
+ pytest.skip("Needs zipfile.Path")
+
+ zip_path = tmp_path / 'qutebrowser.zip'
+ with zipfile.ZipFile(zip_path, 'w') as zf:
+ for path in html_path.rglob('*'):
+ zf.write(path, path.relative_to(tmp_path))
+
+ assert sorted(zf.namelist()) == [
+ 'qutebrowser/html/README',
+ 'qutebrowser/html/subdir/',
+ 'qutebrowser/html/subdir/subdir-file.html',
+ 'qutebrowser/html/test1.html',
+ 'qutebrowser/html/test2.html',
+ 'qutebrowser/html/unrelatedhtml',
+ ]
+
+ yield zipfile.Path(zip_path) / 'qutebrowser'
+
+ @pytest.fixture(params=['pathlib', 'zipfile'])
+ def resource_root(self, request):
+ """Resource files packaged either directly or via a zip."""
+ if request.param == 'pathlib':
+ request.getfixturevalue('html_path')
+ return request.getfixturevalue('package_path')
+ elif request.param == 'zipfile':
+ return request.getfixturevalue('html_zip')
+ raise utils.Unreachable(request.param)
+
+ def test_glob_resources(self, resource_root):
+ files = sorted(resources._glob(resource_root, 'html', '.html'))
+ assert files == ['html/test1.html', 'html/test2.html']
+
+ def test_glob_resources_subdir(self, resource_root):
+ files = sorted(resources._glob(resource_root, 'html/subdir', '.html'))
+ assert files == ['html/subdir/subdir-file.html']
+
+ def test_readfile(self):
+ """Read a test file."""
+ content = resources.read_file(os.path.join('utils', 'testfile'))
+ assert content.splitlines()[0] == "Hello World!"
+
+ @pytest.mark.parametrize('filename', ['javascript/scroll.js',
+ 'html/error.html'])
+ def test_read_cached_file(self, mocker, filename):
+ resources.preload()
+ m = mocker.patch('qutebrowser.utils.resources.importlib_resources.files')
+ resources.read_file(filename)
+ m.assert_not_called()
+
+ def test_readfile_binary(self):
+ """Read a test file in binary mode."""
+ content = resources.read_file_binary(os.path.join('utils', 'testfile'))
+ assert content.splitlines()[0] == b"Hello World!"
+
+ @pytest.mark.parametrize('name', ['read_file', 'read_file_binary'])
+ @pytest.mark.parametrize('fake_exception', [KeyError, FileNotFoundError, None])
+ def test_not_found(self, name, fake_exception, monkeypatch):
+ """Test behavior when a resources file wasn't found.
+
+ With fake_exception, we emulate the rather odd error handling of certain Python
+ versions: https://bugs.python.org/issue43063
+ """
+ class BrokenFileFake:
+
+ def __init__(self, exc):
+ self.exc = exc
+
+ def read_bytes(self):
+ raise self.exc("File does not exist")
+
+ def read_text(self, encoding):
+ raise self.exc("File does not exist")
+
+ def __truediv__(self, _other):
+ return self
+
+ if fake_exception is not None:
+ monkeypatch.setattr(resources.importlib_resources, 'files',
+ lambda _pkg: BrokenFileFake(fake_exception))
+
+ meth = getattr(resources, name)
+ with pytest.raises(FileNotFoundError):
+ meth('doesnotexist')
diff --git a/tests/unit/utils/test_utils.py b/tests/unit/utils/test_utils.py
index 85e95f75f..b43638cb3 100644
--- a/tests/unit/utils/test_utils.py
+++ b/tests/unit/utils/test_utils.py
@@ -28,7 +28,6 @@ import functools
import re
import shlex
import math
-import zipfile
import operator
from PyQt5.QtCore import QUrl, QRect
@@ -228,115 +227,6 @@ def freezer(request, monkeypatch):
pytest.skip("Can't run with sys.frozen = True!")
-@pytest.mark.usefixtures('freezer')
-class TestReadFile:
-
- @pytest.fixture
- def package_path(self, tmp_path):
- return tmp_path / 'qutebrowser'
-
- @pytest.fixture
- def html_path(self, package_path):
- path = package_path / 'html'
- path.mkdir(parents=True)
-
- for filename in ['test1.html', 'test2.html', 'README', 'unrelatedhtml']:
- (path / filename).touch()
-
- subdir = path / 'subdir'
- subdir.mkdir()
- (subdir / 'subdir-file.html').touch()
-
- return path
-
- @pytest.fixture
- def html_zip(self, tmp_path, html_path):
- if not hasattr(zipfile, 'Path'):
- pytest.skip("Needs zipfile.Path")
-
- zip_path = tmp_path / 'qutebrowser.zip'
- with zipfile.ZipFile(zip_path, 'w') as zf:
- for path in html_path.rglob('*'):
- zf.write(path, path.relative_to(tmp_path))
-
- assert sorted(zf.namelist()) == [
- 'qutebrowser/html/README',
- 'qutebrowser/html/subdir/',
- 'qutebrowser/html/subdir/subdir-file.html',
- 'qutebrowser/html/test1.html',
- 'qutebrowser/html/test2.html',
- 'qutebrowser/html/unrelatedhtml',
- ]
-
- yield zipfile.Path(zip_path) / 'qutebrowser'
-
- @pytest.fixture(params=['pathlib', 'zipfile'])
- def resource_root(self, request):
- """Resource files packaged either directly or via a zip."""
- if request.param == 'pathlib':
- request.getfixturevalue('html_path')
- return request.getfixturevalue('package_path')
- elif request.param == 'zipfile':
- return request.getfixturevalue('html_zip')
- raise utils.Unreachable(request.param)
-
- def test_glob_resources(self, resource_root):
- files = sorted(utils._glob_resources(resource_root, 'html', '.html'))
- assert files == ['html/test1.html', 'html/test2.html']
-
- def test_glob_resources_subdir(self, resource_root):
- files = sorted(utils._glob_resources(resource_root, 'html/subdir', '.html'))
- assert files == ['html/subdir/subdir-file.html']
-
- def test_readfile(self):
- """Read a test file."""
- content = utils.read_file(os.path.join('utils', 'testfile'))
- 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('qutebrowser.utils.utils.importlib_resources.files')
- utils.read_file(filename)
- m.assert_not_called()
-
- def test_readfile_binary(self):
- """Read a test file in binary mode."""
- content = utils.read_file_binary(os.path.join('utils', 'testfile'))
- assert content.splitlines()[0] == b"Hello World!"
-
- @pytest.mark.parametrize('name', ['read_file', 'read_file_binary'])
- @pytest.mark.parametrize('fake_exception', [KeyError, FileNotFoundError, None])
- def test_not_found(self, name, fake_exception, monkeypatch):
- """Test behavior when a resources file wasn't found.
-
- With fake_exception, we emulate the rather odd error handling of certain Python
- versions: https://bugs.python.org/issue43063
- """
- class BrokenFileFake:
-
- def __init__(self, exc):
- self.exc = exc
-
- def read_bytes(self):
- raise self.exc("File does not exist")
-
- def read_text(self, encoding):
- raise self.exc("File does not exist")
-
- def __truediv__(self, _other):
- return self
-
- if fake_exception is not None:
- monkeypatch.setattr(utils.importlib_resources, 'files',
- lambda _pkg: BrokenFileFake(fake_exception))
-
- meth = getattr(utils, name)
- with pytest.raises(FileNotFoundError):
- meth('doesnotexist')
-
-
@pytest.mark.parametrize('seconds, out', [
(-1, '-0:01'),
(0, '0:00'),
diff --git a/tests/unit/utils/test_version.py b/tests/unit/utils/test_version.py
index 39b898d59..879f84a1f 100644
--- a/tests/unit/utils/test_version.py
+++ b/tests/unit/utils/test_version.py
@@ -357,14 +357,14 @@ class TestGitStr:
@pytest.fixture
def commit_file_mock(self, mocker):
- """Fixture providing a mock for utils.read_file for git-commit-id.
+ """Fixture providing a mock for resources.read_file for git-commit-id.
On fixture teardown, it makes sure it got called with git-commit-id as
argument.
"""
mocker.patch('qutebrowser.utils.version.subprocess',
side_effect=AssertionError)
- m = mocker.patch('qutebrowser.utils.version.utils.read_file')
+ m = mocker.patch('qutebrowser.utils.version.resources.read_file')
yield m
m.assert_called_with('git-commit-id')
@@ -413,7 +413,7 @@ class TestGitStr:
"""Test with things raising OSError."""
m = mocker.patch('qutebrowser.utils.version.os')
m.path.join.side_effect = OSError
- mocker.patch('qutebrowser.utils.version.utils.read_file',
+ mocker.patch('qutebrowser.utils.version.resources.read_file',
side_effect=OSError)
with caplog.at_level(logging.ERROR, 'misc'):
assert version._git_str() is None