summaryrefslogtreecommitdiff
path: root/qutebrowser
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 /qutebrowser
parentf54b76b0a49337b03978434808e438cb2cb6cb7b (diff)
parentfca59c1a27dd54e820b0ba69ce240276d6d22c7a (diff)
downloadqutebrowser-01b57a33beeb00cf77f7ee28c19273da52f7aea1.tar.gz
qutebrowser-01b57a33beeb00cf77f7ee28c19273da52f7aea1.zip
Merge remote-tracking branch 'origin/pr/6234'
Diffstat (limited to 'qutebrowser')
-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
11 files changed, 168 insertions, 141 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