From 39e51e9b5082652f5ad0ada46bb3b76824a75f8e Mon Sep 17 00:00:00 2001 From: Lembrun Date: Tue, 2 Mar 2021 22:54:51 +0100 Subject: Changing tmpdir fixtures to the pathlib equivalent --- tests/unit/config/test_configcommands.py | 34 +++++++++--------- tests/unit/config/test_configfiles.py | 62 ++++++++++++++++---------------- 2 files changed, 48 insertions(+), 48 deletions(-) diff --git a/tests/unit/config/test_configcommands.py b/tests/unit/config/test_configcommands.py index a15a6a334..f744f69ff 100644 --- a/tests/unit/config/test_configcommands.py +++ b/tests/unit/config/test_configcommands.py @@ -480,7 +480,7 @@ class TestSource: @pytest.mark.parametrize('location', ['default', 'absolute', 'relative']) @pytest.mark.parametrize('clear', [True, False]) - def test_config_source(self, tmpdir, commands, config_stub, config_tmpdir, + def test_config_source(self, tmp_path, commands, config_stub, config_tmpdir, location, clear): assert config_stub.val.content.javascript.enabled config_stub.val.search.ignore_case = 'always' @@ -489,7 +489,7 @@ class TestSource: pyfile = config_tmpdir / 'config.py' arg = None elif location == 'absolute': - pyfile = tmpdir / 'sourced.py' + pyfile = tmp_path / 'sourced.py' arg = str(pyfile) elif location == 'relative': pyfile = config_tmpdir / 'sourced.py' @@ -607,8 +607,8 @@ class TestWritePy: """Tests for :config-write-py.""" - def test_custom(self, commands, config_stub, key_config_stub, tmpdir): - confpy = tmpdir / 'config.py' + def test_custom(self, commands, config_stub, key_config_stub, tmp_path): + confpy = tmp_path / 'config.py' config_stub.val.content.javascript.enabled = True key_config_stub.bind(keyseq(',x'), 'message-info foo', mode='normal') @@ -618,8 +618,8 @@ class TestWritePy: assert "c.content.javascript.enabled = True" in lines assert "config.bind(',x', 'message-info foo')" in lines - def test_defaults(self, commands, tmpdir): - confpy = tmpdir / 'config.py' + def test_defaults(self, commands, tmp_path): + confpy = tmp_path / 'config.py' commands.config_write_py(str(confpy), defaults=True) lines = confpy.read_text('utf-8').splitlines() @@ -639,10 +639,10 @@ class TestWritePy: assert '# Autogenerated config.py' in lines @pytest.mark.posix - def test_expanduser(self, commands, monkeypatch, tmpdir): + def test_expanduser(self, commands, monkeypatch, tmp_path): """Make sure that using a path with ~/... works correctly.""" - home = tmpdir / 'home' - home.ensure(dir=True) + home = tmp_path / 'home' + home.mkdir(exist_ok=True) monkeypatch.setenv('HOME', str(home)) commands.config_write_py('~/config.py') @@ -651,9 +651,9 @@ class TestWritePy: lines = confpy.read_text('utf-8').splitlines() assert '# Autogenerated config.py' in lines - def test_existing_file(self, commands, tmpdir): - confpy = tmpdir / 'config.py' - confpy.ensure() + def test_existing_file(self, commands, tmp_path): + confpy = tmp_path / 'config.py' + confpy.touch(exist_ok=True) with pytest.raises(cmdutils.CommandError) as excinfo: commands.config_write_py(str(confpy)) @@ -661,19 +661,19 @@ class TestWritePy: expected = " already exists - use --force to overwrite!" assert str(excinfo.value).endswith(expected) - def test_existing_file_force(self, commands, tmpdir): - confpy = tmpdir / 'config.py' - confpy.ensure() + def test_existing_file_force(self, commands, tmp_path): + confpy = tmp_path / 'config.py' + confpy.touch(exist_ok=True) commands.config_write_py(str(confpy), force=True) lines = confpy.read_text('utf-8').splitlines() assert '# Autogenerated config.py' in lines - def test_oserror(self, commands, tmpdir): + def test_oserror(self, commands, tmp_path): """Test writing to a directory which does not exist.""" with pytest.raises(cmdutils.CommandError): - commands.config_write_py(str(tmpdir / 'foo' / 'config.py')) + commands.config_write_py(str(tmp_path / 'foo' / 'config.py')) def test_config_py_arg(self, commands, config_py_arg): config_py_arg.ensure() diff --git a/tests/unit/config/test_configfiles.py b/tests/unit/config/test_configfiles.py index 255ea8acc..b011d79d2 100644 --- a/tests/unit/config/test_configfiles.py +++ b/tests/unit/config/test_configfiles.py @@ -744,13 +744,13 @@ class ConfPy: """Helper class to get a confpy fixture.""" - def __init__(self, tmpdir, filename: str = "config.py"): - self._file = tmpdir / filename + def __init__(self, tmp_path, filename: str = "config.py"): + self._file = tmp_path / filename self.filename = str(self._file) def write(self, *lines): text = '\n'.join(lines) - self._file.write_text(text, 'utf-8', ensure=True) + self._file.write_text(text, 'utf-8') def read(self, error=False, warn_autoconfig=False): """Read the config.py via configfiles and check for errors.""" @@ -777,8 +777,8 @@ class ConfPy: @pytest.fixture -def confpy(tmpdir, config_tmpdir, data_tmpdir, config_stub, key_config_stub): - return ConfPy(tmpdir) +def confpy(tmp_path, config_tmpdir, data_tmpdir, config_stub, key_config_stub): + return ConfPy(tmp_path) class TestConfigPyModules: @@ -786,8 +786,8 @@ class TestConfigPyModules: pytestmark = pytest.mark.usefixtures('config_stub', 'key_config_stub') @pytest.fixture - def qbmodulepy(self, tmpdir): - return ConfPy(tmpdir, filename="qbmodule.py") + def qbmodulepy(self, tmp_path): + return ConfPy(tmp_path, filename="qbmodule.py") @pytest.fixture(autouse=True) def restore_sys_path(self): @@ -795,7 +795,7 @@ class TestConfigPyModules: yield sys.path = old_path - def test_bind_in_module(self, confpy, qbmodulepy, tmpdir): + def test_bind_in_module(self, confpy, qbmodulepy, tmp_path): qbmodulepy.write( 'def run(config):', ' config.bind(",a", "message-info foo", mode="normal")') @@ -804,9 +804,9 @@ class TestConfigPyModules: expected = {'normal': {',a': 'message-info foo'}} assert config.instance.get_obj('bindings.commands') == expected assert "qbmodule" not in sys.modules.keys() - assert tmpdir not in sys.path + assert tmp_path not in sys.path - def test_restore_sys_on_err(self, confpy, qbmodulepy, tmpdir): + def test_restore_sys_on_err(self, confpy, qbmodulepy, tmp_path): confpy.write_qbmodule() qbmodulepy.write('def run(config):', ' 1/0') @@ -815,9 +815,9 @@ class TestConfigPyModules: assert error.text == "Unhandled exception" assert isinstance(error.exception, ZeroDivisionError) assert "qbmodule" not in sys.modules.keys() - assert tmpdir not in sys.path + assert tmp_path not in sys.path - def test_fail_on_nonexistent_module(self, confpy, qbmodulepy, tmpdir): + def test_fail_on_nonexistent_module(self, confpy, qbmodulepy, tmp_path): qbmodulepy.write('def run(config):', ' pass') confpy.write('import foobar', @@ -832,13 +832,13 @@ class TestConfigPyModules: assert tblines[0] == "Traceback (most recent call last):" assert tblines[-1].endswith("Error: No module named 'foobar'") - def test_no_double_if_path_exists(self, confpy, qbmodulepy, tmpdir): - sys.path.insert(0, tmpdir) + def test_no_double_if_path_exists(self, confpy, qbmodulepy, tmp_path): + sys.path.insert(0, tmp_path) confpy.write('import sys', 'if sys.path[0] in sys.path[1:]:', ' raise Exception("Path not expected")') confpy.read() - assert sys.path.count(tmpdir) == 1 + assert sys.path.count(tmp_path) == 1 class TestConfigPy: @@ -1004,9 +1004,9 @@ class TestConfigPy: confpy.read() assert config.instance.get_obj(option)[-1] == value - def test_oserror(self, tmpdir, data_tmpdir, config_tmpdir): + def test_oserror(self, tmp_path, data_tmpdir, config_tmpdir): with pytest.raises(configexc.ConfigFileErrors) as excinfo: - configfiles.read_config_py(str(tmpdir / 'foo')) + configfiles.read_config_py(str(tmp_path / 'foo')) assert len(excinfo.value.errors) == 1 error = excinfo.value.errors[0] @@ -1154,12 +1154,12 @@ class TestConfigPy: assert error.traceback is not None @pytest.mark.parametrize('location', ['abs', 'rel']) - def test_source(self, tmpdir, confpy, location): + def test_source(self, tmp_path, confpy, location): if location == 'abs': - subfile = tmpdir / 'subfile.py' + subfile = tmp_path / 'subfile.py' arg = str(subfile) else: - subfile = tmpdir / 'config' / 'subfile.py' + subfile = tmp_path / 'config' / 'subfile.py' arg = 'subfile.py' subfile.write_text("c.content.javascript.enabled = False", @@ -1169,11 +1169,11 @@ class TestConfigPy: assert not config.instance.get_obj('content.javascript.enabled') - def test_source_configpy_arg(self, tmpdir, data_tmpdir, monkeypatch): + def test_source_configpy_arg(self, tmp_path, data_tmpdir, monkeypatch): alt_filename = 'alt-config.py' - alt_confpy_dir = tmpdir / 'alt-confpy-dir' - alt_confpy_dir.ensure(dir=True) + alt_confpy_dir = tmp_path / 'alt-confpy-dir' + alt_confpy_dir.mkdir(exist_ok=True) monkeypatch.setattr(standarddir, 'config_py', lambda: str(alt_confpy_dir / alt_filename)) @@ -1187,8 +1187,8 @@ class TestConfigPy: assert not config.instance.get_obj('content.javascript.enabled') - def test_source_errors(self, tmpdir, confpy): - subfile = tmpdir / 'config' / 'subfile.py' + def test_source_errors(self, tmp_path, confpy): + subfile = tmp_path / 'config' / 'subfile.py' subfile.write_text("c.foo = 42", encoding='utf-8') confpy.write("config.source('subfile.py')") error = confpy.read(error=True) @@ -1196,8 +1196,8 @@ class TestConfigPy: assert error.text == "While setting 'foo'" assert isinstance(error.exception, configexc.NoOptionError) - def test_source_multiple_errors(self, tmpdir, confpy): - subfile = tmpdir / 'config' / 'subfile.py' + def test_source_multiple_errors(self, tmp_path, confpy): + subfile = tmp_path / 'config' / 'subfile.py' subfile.write_text("c.foo = 42", encoding='utf-8') confpy.write("config.source('subfile.py')", "c.bar = 23") @@ -1218,8 +1218,8 @@ class TestConfigPy: assert isinstance(error.exception, FileNotFoundError) @pytest.mark.parametrize('reverse', [True, False]) - def test_source_warn_autoconfig(self, tmpdir, confpy, reverse): - subfile = tmpdir / 'config' / 'subfile.py' + def test_source_warn_autoconfig(self, tmp_path, confpy, reverse): + subfile = tmp_path / 'config' / 'subfile.py' subfile.write_text("c.content.javascript.enabled = False", encoding='utf-8') lines = [ @@ -1383,8 +1383,8 @@ class TestConfigPyWriter: expected = "config.set('opt', 'ask', 'https://www.example.com/')" assert expected in text - def test_write(self, tmpdir): - pyfile = tmpdir / 'config.py' + def test_write(self, tmp_path): + pyfile = tmp_path / 'config.py' writer = configfiles.ConfigPyWriter(options=[], bindings={}, commented=False) writer.write(str(pyfile)) -- cgit v1.2.3-54-g00ecf From b57dfaf663f66de890e984dd814bf86a9c7a08a5 Mon Sep 17 00:00:00 2001 From: Lembrun Date: Wed, 3 Mar 2021 12:12:34 +0100 Subject: Replaced py.path objects by their pathlib equivalents --- tests/unit/commands/test_userscripts.py | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/tests/unit/commands/test_userscripts.py b/tests/unit/commands/test_userscripts.py index 48bc31c32..436e9e2a7 100644 --- a/tests/unit/commands/test_userscripts.py +++ b/tests/unit/commands/test_userscripts.py @@ -18,6 +18,7 @@ # along with qutebrowser. If not, see . import os +import pathlib import json import time import logging @@ -34,8 +35,8 @@ from qutebrowser.utils import utils class TestQtFIFOReader: @pytest.fixture - def reader(self, tmpdir, qapp): - fifo_path = str(tmpdir / 'fifo') + def reader(self, tmp_path, qapp): + fifo_path = str(tmp_path / 'fifo') os.mkfifo(fifo_path) # pylint: disable=no-member,useless-suppression reader = userscripts._QtFIFOReader(fifo_path) yield reader @@ -142,8 +143,8 @@ def test_source(qtbot, py_proc, runner): assert parsed['text'] == 'This is text' assert parsed['html'] == 'This is HTML' - assert not os.path.exists(parsed['text_file']) - assert not os.path.exists(parsed['html_file']) + assert not pathlib.Path(parsed['text_file']).exists() + assert not pathlib.Path(parsed['html_file']).exists() def test_command_with_error(qtbot, py_proc, runner, caplog): @@ -165,13 +166,13 @@ def test_command_with_error(qtbot, py_proc, runner, caplog): runner.store_html('') data = json.loads(blocker.args[0]) - assert not os.path.exists(data) + assert not pathlib.Path(data).exists() -def test_killed_command(qtbot, tmpdir, py_proc, runner, caplog): - data_file = tmpdir / 'data' +def test_killed_command(qtbot, tmp_path, py_proc, runner, caplog): + data_file = tmp_path / 'data' watcher = QFileSystemWatcher() - watcher.addPath(str(tmpdir)) + watcher.addPath(str(tmp_path)) cmd, args = py_proc(r""" import os @@ -203,13 +204,14 @@ def test_killed_command(qtbot, tmpdir, py_proc, runner, caplog): # Make sure the PID was written to the file, not just the file created time.sleep(0.5) - data = json.load(data_file) + with data_file.open() as f: + data = json.load(f) with caplog.at_level(logging.ERROR): with qtbot.wait_signal(runner.finished): os.kill(int(data['pid']), signal.SIGTERM) - assert not os.path.exists(data['text_file']) + assert not pathlib.Path(data['text_file']).exists() def test_temporary_files_failed_cleanup(caplog, qtbot, py_proc, runner): -- cgit v1.2.3-54-g00ecf From 38311a69a898dd44ea76fd80e271f10a6b235114 Mon Sep 17 00:00:00 2001 From: Lembrun Date: Wed, 3 Mar 2021 12:59:17 +0100 Subject: Replaced os.path by the pathlib equivalent --- tests/unit/completion/test_models.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/tests/unit/completion/test_models.py b/tests/unit/completion/test_models.py index 8a6b24557..15ebe7110 100644 --- a/tests/unit/completion/test_models.py +++ b/tests/unit/completion/test_models.py @@ -21,6 +21,7 @@ import collections import os +import pathlib import random import string import time @@ -426,11 +427,11 @@ def test_filesystem_completion(qtmodeltester, config_stub, info, homedir = str(local_files_path) monkeypatch.setenv('HOME', homedir) # POSIX monkeypatch.setenv('USERPROFILE', homedir) # Windows - assert os.path.expanduser('~') == homedir + assert str(pathlib.Path('~').expanduser()) == homedir base = '~' - expected_1 = os.path.join('~', 'file1.txt') - expected_2 = os.path.join('~', 'file2.txt') + expected_1 = str(pathlib.Path('~').joinpath('file1.txt')) + expected_2 = str(pathlib.Path('~').joinpath('file2.txt')) config_stub.val.completion.open_categories = ['filesystem'] model = urlmodel.url(info=info) -- cgit v1.2.3-54-g00ecf From 423e7e25bdd7de78c7a75d70bfc92171fcf8ccaf Mon Sep 17 00:00:00 2001 From: Florian Bruhin Date: Fri, 5 Mar 2021 19:20:27 +0100 Subject: Remove blank line in cheatsheet --- doc/img/cheatsheet-big.png | Bin 779344 -> 781120 bytes doc/img/cheatsheet-small.png | Bin 30208 -> 30252 bytes misc/cheatsheet.svg | 14 ++++++-------- 3 files changed, 6 insertions(+), 8 deletions(-) diff --git a/doc/img/cheatsheet-big.png b/doc/img/cheatsheet-big.png index ecd52c14e..75e2abb89 100644 Binary files a/doc/img/cheatsheet-big.png and b/doc/img/cheatsheet-big.png differ diff --git a/doc/img/cheatsheet-small.png b/doc/img/cheatsheet-small.png index 0dc01e8b4..e97d63367 100644 Binary files a/doc/img/cheatsheet-small.png and b/doc/img/cheatsheet-small.png differ diff --git a/misc/cheatsheet.svg b/misc/cheatsheet.svg index 7e8a7b381..e908f9496 100644 --- a/misc/cheatsheet.svg +++ b/misc/cheatsheet.svg @@ -11,7 +11,7 @@ height="682.66669" id="svg2" sodipodi:version="0.32" - inkscape:version="1.0.1 (3bc2e813f5, 2020-09-07, custom)" + inkscape:version="1.0.2 (e86c870879, 2021-01-15)" version="1.0" sodipodi:docname="cheatsheet.svg" inkscape:output_extension="org.inkscape.output.svg.inkscape" @@ -30,16 +30,16 @@ objecttolerance="10" inkscape:pageopacity="0.0" inkscape:pageshadow="2" - inkscape:zoom="1.7536248" - inkscape:cx="466.08451" - inkscape:cy="268.64059" + inkscape:zoom="2.48" + inkscape:cx="834.18001" + inkscape:cy="692.30401" inkscape:document-units="px" inkscape:current-layer="layer1" width="1024px" height="640px" showgrid="false" - inkscape:window-width="3822" - inkscape:window-height="2128" + inkscape:window-width="1914" + inkscape:window-height="1048" inkscape:window-x="0" inkscape:window-y="16" showguides="true" @@ -3113,8 +3113,6 @@ style="font-size:10.6667px;line-height:1.25;font-family:sans-serif;fill:#000000;stroke-width:1.06667" id="flowPara3925">ss - set setting (sl: temp)sk - bind keySs - show settings Date: Sun, 7 Mar 2021 17:52:42 +0100 Subject: Added utils/resources.py and changed calls to util.read_file* --- qutebrowser/app.py | 7 +- qutebrowser/browser/network/pac.py | 4 +- qutebrowser/browser/pdfjs.py | 4 +- qutebrowser/browser/qutescheme.py | 14 +-- qutebrowser/browser/webengine/webenginetab.py | 12 +-- qutebrowser/browser/webkit/webkittab.py | 4 +- qutebrowser/config/configdata.py | 4 +- qutebrowser/utils/jinja.py | 8 +- qutebrowser/utils/resources.py | 148 ++++++++++++++++++++++++++ qutebrowser/utils/utils.py | 99 ----------------- qutebrowser/utils/version.py | 4 +- tests/unit/browser/test_pdfjs.py | 2 +- tests/unit/browser/test_qutescheme.py | 13 +-- tests/unit/utils/test_jinja.py | 6 +- tests/unit/utils/test_utils.py | 20 ++-- tests/unit/utils/test_version.py | 6 +- 16 files changed, 203 insertions(+), 152 deletions(-) create mode 100644 qutebrowser/utils/resources.py diff --git a/qutebrowser/app.py b/qutebrowser/app.py index 5a9c956b0..444d3e69e 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_resources() 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 9f129b609..a2f6682de 100644 --- a/qutebrowser/browser/webengine/webenginetab.py +++ b/qutebrowser/browser/webengine/webenginetab.py @@ -37,7 +37,7 @@ from qutebrowser.browser.webengine import (webview, webengineelem, tabhistory, webenginesettings, certificateerror) from qutebrowser.misc import miscwidgets, objects from qutebrowser.utils import (usertypes, qtutils, log, javascript, utils, - message, jinja, debug, version) + resources, message, jinja, debug, version) from qutebrowser.qt import sip @@ -1036,9 +1036,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) @@ -1059,7 +1059,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) @@ -1174,7 +1174,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 066cce348..9f15e9fb4 100644 --- a/qutebrowser/browser/webkit/webkittab.py +++ b/qutebrowser/browser/webkit/webkittab.py @@ -34,7 +34,7 @@ from PyQt5.QtPrintSupport import QPrinter from qutebrowser.browser import browsertab, shared from qutebrowser.browser.webkit import (webview, tabhistory, webkitelem, webkitsettings) -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 @@ -227,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..a1c5d7f85 --- /dev/null +++ b/qutebrowser/utils/resources.py @@ -0,0 +1,148 @@ +# vim: ft=python fileencoding=utf-8 sts=4 sw=4 et: + +# Copyright 2014-2021 Florian Bruhin (The Compiler) +# +# 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 . + +"""Resources related utilities""" + +import os +import os.path +import io +import re +import sys +import enum +import json +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) + + +# 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 +_resource_cache = {} + +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() + diff --git a/qutebrowser/utils/utils.py b/qutebrowser/utils/utils.py index 698a608ef..93bf17ff5 100644 --- a/qutebrowser/utils/utils.py +++ b/qutebrowser/utils/utils.py @@ -195,105 +195,6 @@ def compact_text(text: str, elidelength: int = None) -> str: out = elide(out, elidelength) 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 parse_version(version: str) -> VersionNumber: """Parse a version string.""" ver, _suffix = QVersionNumber.fromString(version) diff --git a/qutebrowser/utils/version.py b/qutebrowser/utils/version.py index 0e3927948..4e8f0d782 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/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_utils.py b/tests/unit/utils/test_utils.py index 4cf60943c..5fb61bb2f 100644 --- a/tests/unit/utils/test_utils.py +++ b/tests/unit/utils/test_utils.py @@ -39,7 +39,7 @@ import yaml import qutebrowser import qutebrowser.utils # for test_qualname -from qutebrowser.utils import utils, version, usertypes +from qutebrowser.utils import utils, version, usertypes, resources class TestVersionNumber: @@ -185,29 +185,29 @@ class TestReadFile: raise utils.Unreachable(request.param) def test_glob_resources(self, resource_root): - files = sorted(utils._glob_resources(resource_root, 'html', '.html')) + files = sorted(resources._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')) + files = sorted(resources._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')) + 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): - utils.preload_resources() - m = mocker.patch('qutebrowser.utils.utils.importlib_resources.files') - utils.read_file(filename) + resources.preload_resources() + 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 = utils.read_file_binary(os.path.join('utils', 'testfile')) + 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']) @@ -233,10 +233,10 @@ class TestReadFile: return self if fake_exception is not None: - monkeypatch.setattr(utils.importlib_resources, 'files', + monkeypatch.setattr(resources.importlib_resources, 'files', lambda _pkg: BrokenFileFake(fake_exception)) - meth = getattr(utils, name) + meth = getattr(resources, name) with pytest.raises(FileNotFoundError): meth('doesnotexist') diff --git a/tests/unit/utils/test_version.py b/tests/unit/utils/test_version.py index f846c91ac..f5b593231 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 -- cgit v1.2.3-54-g00ecf From 8130c0316a737ddea1624351fa60b1812d0d2015 Mon Sep 17 00:00:00 2001 From: Lembrun Date: Sun, 7 Mar 2021 18:50:08 +0100 Subject: Fixed linting --- qutebrowser/browser/webkit/webkittab.py | 2 +- qutebrowser/utils/utils.py | 17 +++++------------ 2 files changed, 6 insertions(+), 13 deletions(-) diff --git a/qutebrowser/browser/webkit/webkittab.py b/qutebrowser/browser/webkit/webkittab.py index a1d3e5574..df3491ec2 100644 --- a/qutebrowser/browser/webkit/webkittab.py +++ b/qutebrowser/browser/webkit/webkittab.py @@ -33,7 +33,7 @@ from PyQt5.QtPrintSupport import QPrinter from qutebrowser.browser import browsertab, shared from qutebrowser.browser.webkit import (webview, tabhistory, webkitelem, - webkitsettings) + webkitsettings, webkitinspector) from qutebrowser.utils import qtutils, usertypes, utils, log, debug, resources from qutebrowser.keyinput import modeman from qutebrowser.qt import sip diff --git a/qutebrowser/utils/utils.py b/qutebrowser/utils/utils.py index 93bf17ff5..a2c935ff0 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') @@ -195,6 +187,7 @@ def compact_text(text: str, elidelength: int = None) -> str: out = elide(out, elidelength) return out + def parse_version(version: str) -> VersionNumber: """Parse a version string.""" ver, _suffix = QVersionNumber.fromString(version) -- cgit v1.2.3-54-g00ecf From f2447217385dcb526aa9b7faf109d330117f096f Mon Sep 17 00:00:00 2001 From: qutebrowser bot Date: Mon, 8 Mar 2021 04:40:33 +0000 Subject: Update dependencies --- misc/requirements/requirements-flake8.txt | 2 +- misc/requirements/requirements-mypy.txt | 12 ++++++------ misc/requirements/requirements-pyinstaller.txt | 2 +- misc/requirements/requirements-pyroma.txt | 4 ++-- misc/requirements/requirements-sphinx.txt | 4 ++-- misc/requirements/requirements-tests.txt | 6 +++--- misc/requirements/requirements-tox.txt | 4 ++-- requirements.txt | 8 ++++---- 8 files changed, 21 insertions(+), 21 deletions(-) diff --git a/misc/requirements/requirements-flake8.txt b/misc/requirements/requirements-flake8.txt index 493fa3cac..c83b57860 100644 --- a/misc/requirements/requirements-flake8.txt +++ b/misc/requirements/requirements-flake8.txt @@ -2,7 +2,7 @@ attrs==20.3.0 flake8==3.8.4 -flake8-bugbear==20.11.1 +flake8-bugbear==21.3.1 flake8-builtins==1.5.3 flake8-comprehensions==3.3.1 flake8-copyright==0.2.2 diff --git a/misc/requirements/requirements-mypy.txt b/misc/requirements/requirements-mypy.txt index 070339ed6..dfa80656b 100644 --- a/misc/requirements/requirements-mypy.txt +++ b/misc/requirements/requirements-mypy.txt @@ -1,10 +1,10 @@ # This file is automatically generated by scripts/dev/recompile_requirements.py chardet==4.0.0 -diff-cover==4.2.1 -importlib-metadata==3.7.0 -importlib-resources==5.1.1 -inflect==5.2.0 +diff-cover==4.2.3 +importlib-metadata==3.7.2 +importlib-resources==5.1.2 +inflect==3.0.2 Jinja2==2.11.3 jinja2-pluralize==0.3.0 lxml==4.6.2 @@ -12,8 +12,8 @@ MarkupSafe==1.1.1 mypy==0.812 mypy-extensions==0.4.3 pluggy==0.13.1 -Pygments==2.8.0 +Pygments==2.8.1 PyQt5-stubs==5.15.2.0 typed-ast==1.4.2 typing-extensions==3.7.4.3 -zipp==3.4.0 +zipp==3.4.1 diff --git a/misc/requirements/requirements-pyinstaller.txt b/misc/requirements/requirements-pyinstaller.txt index 05a59200f..5b7c0137a 100644 --- a/misc/requirements/requirements-pyinstaller.txt +++ b/misc/requirements/requirements-pyinstaller.txt @@ -2,4 +2,4 @@ altgraph==0.17 pyinstaller==4.2 -pyinstaller-hooks-contrib==2020.11 +pyinstaller-hooks-contrib==2021.1 diff --git a/misc/requirements/requirements-pyroma.txt b/misc/requirements/requirements-pyroma.txt index 22a195e66..b64b99e24 100644 --- a/misc/requirements/requirements-pyroma.txt +++ b/misc/requirements/requirements-pyroma.txt @@ -1,5 +1,5 @@ # This file is automatically generated by scripts/dev/recompile_requirements.py docutils==0.16 -Pygments==2.5.2 -pyroma==2.6.1 +Pygments==2.8.1 +pyroma==3.1 diff --git a/misc/requirements/requirements-sphinx.txt b/misc/requirements/requirements-sphinx.txt index 495b8dcf5..352be342a 100644 --- a/misc/requirements/requirements-sphinx.txt +++ b/misc/requirements/requirements-sphinx.txt @@ -10,12 +10,12 @@ imagesize==1.2.0 Jinja2==2.11.3 MarkupSafe==1.1.1 packaging==20.9 -Pygments==2.8.0 +Pygments==2.8.1 pyparsing==2.4.7 pytz==2021.1 requests==2.25.1 snowballstemmer==2.1.0 -Sphinx==3.5.1 +Sphinx==3.5.2 sphinxcontrib-applehelp==1.0.2 sphinxcontrib-devhelp==1.0.2 sphinxcontrib-htmlhelp==1.0.3 diff --git a/misc/requirements/requirements-tests.txt b/misc/requirements/requirements-tests.txt index bf214be0d..2bfaf91e0 100644 --- a/misc/requirements/requirements-tests.txt +++ b/misc/requirements/requirements-tests.txt @@ -15,7 +15,7 @@ filelock==3.0.12 Flask==1.1.2 glob2==0.7 hunter==3.3.1 -hypothesis==6.3.4 +hypothesis==6.6.0 icdiff==1.9.1 idna==2.10 iniconfig==1.1.1 @@ -33,7 +33,7 @@ pluggy==0.13.1 pprintpp==0.4.0 py==1.10.0 py-cpuinfo==7.0.0 -Pygments==2.8.0 +Pygments==2.8.1 pyparsing==2.4.7 pytest==6.2.2 pytest-bdd==4.0.2 @@ -48,7 +48,7 @@ pytest-repeat==0.9.1 pytest-rerunfailures==9.1.1 pytest-xdist==2.2.1 pytest-xvfb==2.0.0 -PyVirtualDisplay==2.0 +PyVirtualDisplay==2.1 requests==2.25.1 requests-file==1.5.1 six==1.15.0 diff --git a/misc/requirements/requirements-tox.txt b/misc/requirements/requirements-tox.txt index 1e6382e1e..d44522118 100644 --- a/misc/requirements/requirements-tox.txt +++ b/misc/requirements/requirements-tox.txt @@ -8,9 +8,9 @@ pip==21.0.1 pluggy==0.13.1 py==1.10.0 pyparsing==2.4.7 -setuptools==54.0.0 +setuptools==54.1.1 six==1.15.0 toml==0.10.2 -tox==3.22.0 +tox==3.23.0 virtualenv==20.4.2 wheel==0.36.2 diff --git a/requirements.txt b/requirements.txt index c6eb86d6f..5572e206c 100644 --- a/requirements.txt +++ b/requirements.txt @@ -3,11 +3,11 @@ adblock==0.4.2 ; python_version!="3.10" colorama==0.4.4 dataclasses==0.6 ; python_version<"3.7" -importlib-metadata==3.7.0 ; python_version<"3.8" -importlib-resources==5.1.1 ; python_version<"3.9" +importlib-metadata==3.7.2 ; python_version<"3.8" +importlib-resources==5.1.2 ; python_version<"3.9" Jinja2==2.11.3 MarkupSafe==1.1.1 -Pygments==2.8.0 +Pygments==2.8.1 PyYAML==5.4.1 typing-extensions==3.7.4.3 -zipp==3.4.0 +zipp==3.4.1 -- cgit v1.2.3-54-g00ecf From 45387d18a514103c686be559087420199cb49995 Mon Sep 17 00:00:00 2001 From: Florian Bruhin Date: Mon, 8 Mar 2021 11:26:39 +0100 Subject: Fix some tests with non-english locales See #6234 --- tests/end2end/features/misc.feature | 2 +- tests/end2end/features/qutescheme.feature | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/end2end/features/misc.feature b/tests/end2end/features/misc.feature index 351135fab..5d81a890a 100644 --- a/tests/end2end/features/misc.feature +++ b/tests/end2end/features/misc.feature @@ -140,7 +140,7 @@ Feature: Various utility commands. Scenario: :jseval --file using a file that doesn't exist as js-code When I run :jseval --file /nonexistentfile - Then the error "[Errno 2] No such file or directory: '/nonexistentfile'" should be shown + Then the error "[Errno 2] *: '/nonexistentfile'" should be shown And "No output or error" should not be logged # :debug-webaction diff --git a/tests/end2end/features/qutescheme.feature b/tests/end2end/features/qutescheme.feature index 286f8f80a..1424bbf09 100644 --- a/tests/end2end/features/qutescheme.feature +++ b/tests/end2end/features/qutescheme.feature @@ -215,7 +215,7 @@ Feature: Special qute:// pages Scenario: Running :pyeval --file using a non existing file When I run :debug-pyeval --file nonexistentfile - Then the error "[Errno 2] No such file or directory: 'nonexistentfile'" should be shown + Then the error "[Errno 2] *: 'nonexistentfile'" should be shown Scenario: Running :pyeval with --quiet When I run :debug-pyeval --quiet 1+1 -- cgit v1.2.3-54-g00ecf From 76b03db20eac1d9b52156191656d4c2adb290911 Mon Sep 17 00:00:00 2001 From: Florian Bruhin Date: Mon, 8 Mar 2021 14:04:56 +0100 Subject: Revert "ci: Add workaround for Archlinux/Docker issue" This reverts commit 478e4de7bd1f26bebdcdc166d5369b2b5142c3e2. Fixed according to https://github.com/actions/virtual-environments/issues/2658 --- scripts/dev/ci/docker/Dockerfile.j2 | 7 ------- 1 file changed, 7 deletions(-) diff --git a/scripts/dev/ci/docker/Dockerfile.j2 b/scripts/dev/ci/docker/Dockerfile.j2 index 03e5684ad..d3fc82793 100644 --- a/scripts/dev/ci/docker/Dockerfile.j2 +++ b/scripts/dev/ci/docker/Dockerfile.j2 @@ -1,12 +1,5 @@ FROM archlinux:latest -# WORKAROUND for glibc 2.33 and old Docker -# See https://github.com/actions/virtual-environments/issues/2658 -# Thanks to https://github.com/lxqt/lxqt-panel/pull/1562 -RUN patched_glibc=glibc-linux4-2.33-4-x86_64.pkg.tar.zst && \ - curl -LO "https://repo.archlinuxcn.org/x86_64/$patched_glibc" && \ - bsdtar -C / -xvf "$patched_glibc" - {% if unstable %} RUN sed -i '/^# after the header/a[kde-unstable]\nInclude = /etc/pacman.d/mirrorlist\n\n[testing]\nInclude = /etc/pacman.d/mirrorlist' /etc/pacman.conf {% endif %} -- cgit v1.2.3-54-g00ecf From 55fdae8eed0f93152b5676cca77b6243fd95d9b0 Mon Sep 17 00:00:00 2001 From: rnhmjoj Date: Mon, 8 Mar 2021 17:40:57 +0100 Subject: userscripts/readability: add unique body class Until per-domains stylesheets are implemented, there is no way to apply style to the readability page only. With this patch, you can just use the global setting `content.user_stylesheets` by writing a more specific CSS selector. For example: body.qute-readability { font-family: Libertinus; font-size: 1.2em; text-align: justify; } will change the font and text alignment of the readability page, without altering the style of other websites. --- misc/userscripts/readability | 3 +++ misc/userscripts/readability-js | 3 +++ 2 files changed, 6 insertions(+) diff --git a/misc/userscripts/readability b/misc/userscripts/readability index f9cbbf829..a6a6f2d52 100755 --- a/misc/userscripts/readability +++ b/misc/userscripts/readability @@ -57,6 +57,9 @@ with codecs.open(os.environ['QUTE_HTML'], 'r', 'utf-8') as source: title = doc.title() content = doc.summary().replace('', HEADER % title) + # add a class to make styling the page easier + content = content.replace('', '') + with codecs.open(tmpfile, 'w', 'utf-8') as target: target.write(content.lstrip()) diff --git a/misc/userscripts/readability-js b/misc/userscripts/readability-js index 2f24e065d..532df51c6 100755 --- a/misc/userscripts/readability-js +++ b/misc/userscripts/readability-js @@ -131,6 +131,9 @@ getDOM(target, domOpts).then(dom => { let article = reader.parse(); let content = util.format(HEADER, article.title) + article.content; + // add a class to make styling the page easier + content = content.replace('', '') + fs.writeFile(tmpFile, content, (err) => { if (err) { qute.messageError([`"${err}"`]) -- cgit v1.2.3-54-g00ecf From 163517800365ebc48c4947b3ad4ddd538ca8f31f Mon Sep 17 00:00:00 2001 From: Florian Bruhin Date: Mon, 8 Mar 2021 18:06:20 +0100 Subject: Update changelog --- doc/changelog.asciidoc | 3 +++ 1 file changed, 3 insertions(+) diff --git a/doc/changelog.asciidoc b/doc/changelog.asciidoc index 3840f369d..c78fe556a 100644 --- a/doc/changelog.asciidoc +++ b/doc/changelog.asciidoc @@ -60,6 +60,9 @@ Changed - The `fileselect.*.command` settings now support file selectors writing the selected paths to stdout, which is used if no `{}` placeholder is contained in the configured command. +- The `readability` and `readability-js` userscripts now add a + `qute-readability` CSS class to the page, so that it can be styled easily via + a user stylesheet. Fixed ~~~~~ -- cgit v1.2.3-54-g00ecf From 2b859a71810ac6352efea43de259eb3343e38e5f Mon Sep 17 00:00:00 2001 From: Laurent Arnoud Date: Thu, 4 Mar 2021 21:08:32 +0100 Subject: Force rebuild on history import Before the v2.0.x release, deleting the table had the same effect, but that's not the case anymore --- scripts/hist_importer.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/scripts/hist_importer.py b/scripts/hist_importer.py index 6f2b9fa87..df12bcf2e 100755 --- a/scripts/hist_importer.py +++ b/scripts/hist_importer.py @@ -135,7 +135,8 @@ def insert_qb(history, dest): 'INSERT INTO History (url,title,atime,redirect) VALUES (?,?,?,?)', history ) - cursor.execute('DROP TABLE CompletionHistory') + cursor.execute('UPDATE CompletionMetaInfo SET value = 1 ' + 'WHERE key = "force_rebuild"') conn.commit() conn.close() -- cgit v1.2.3-54-g00ecf From 0fb5352cdf0f220d2cce34f84bf96cfbbf541a30 Mon Sep 17 00:00:00 2001 From: Florian Bruhin Date: Mon, 8 Mar 2021 20:51:32 +0100 Subject: Simplify _inject_greasemonkey_scripts The arguments aren't needed anymore since 05111e84236621d3923ed4efd36a7c6578407c20 and we can use f-strings nowadays. --- qutebrowser/browser/webengine/webenginetab.py | 47 ++++++++------------------- 1 file changed, 14 insertions(+), 33 deletions(-) diff --git a/qutebrowser/browser/webengine/webenginetab.py b/qutebrowser/browser/webengine/webenginetab.py index 4092fbe40..439d99570 100644 --- a/qutebrowser/browser/webengine/webenginetab.py +++ b/qutebrowser/browser/webengine/webenginetab.py @@ -1080,18 +1080,11 @@ class _WebEngineScripts(QObject): removed = page_scripts.remove(script) assert removed, script.name() - def _inject_greasemonkey_scripts(self, scripts=None, injection_point=None, - remove_first=True): + def _inject_greasemonkey_scripts(self, scripts): """Register user JavaScript files with the current tab. Args: - scripts: A list of GreasemonkeyScripts, or None to add all - known by the Greasemonkey subsystem. - injection_point: The QWebEngineScript::InjectionPoint stage - to inject the script into, None to use - auto-detection. - remove_first: Whether to remove all previously injected - scripts before adding these ones. + scripts: A list of GreasemonkeyScripts. """ if sip.isdeleted(self._widget): return @@ -1102,11 +1095,7 @@ class _WebEngineScripts(QObject): # While, taking care not to remove any other scripts that might # have been added elsewhere, like the one for stylesheets. page_scripts = self._widget.page().scripts() - if remove_first: - self._remove_all_greasemonkey_scripts() - - if not scripts: - return + self._remove_all_greasemonkey_scripts() for script in scripts: new_script = QWebEngineScript() @@ -1114,37 +1103,29 @@ class _WebEngineScripts(QObject): world = int(script.jsworld) if not 0 <= world <= qtutils.MAX_WORLD_ID: log.greasemonkey.error( - "script {} has invalid value for '@qute-js-world'" - ": {}, should be between 0 and {}" - .format( - script.name, - script.jsworld, - qtutils.MAX_WORLD_ID)) + f"script {script.name} has invalid value for '@qute-js-world'" + f": {script.jsworld}, should be between 0 and " + f"{qtutils.MAX_WORLD_ID}") continue except ValueError: try: - world = _JS_WORLD_MAP[usertypes.JsWorld[ - script.jsworld.lower()]] + world = _JS_WORLD_MAP[usertypes.JsWorld[script.jsworld.lower()]] except KeyError: log.greasemonkey.error( - "script {} has invalid value for '@qute-js-world'" - ": {}".format(script.name, script.jsworld)) + f"script {script.name} has invalid value for '@qute-js-world'" + f": {script.jsworld}") continue new_script.setWorldId(world) new_script.setSourceCode(script.code()) - new_script.setName("GM-{}".format(script.name)) + new_script.setName(f"GM-{script.name}") new_script.setRunsOnSubFrames(script.runs_on_sub_frames) - # Override the @run-at value parsed by QWebEngineScript if desired. - if injection_point: - new_script.setInjectionPoint(injection_point) - elif script.needs_document_end_workaround(): - log.greasemonkey.debug("Forcing @run-at document-end for {}" - .format(script.name)) + if script.needs_document_end_workaround(): + log.greasemonkey.debug( + f"Forcing @run-at document-end for {script.name}") new_script.setInjectionPoint(QWebEngineScript.DocumentReady) - log.greasemonkey.debug('adding script: {}' - .format(new_script.name())) + log.greasemonkey.debug(f'adding script: {new_script.name()}') page_scripts.insert(new_script) def _inject_site_specific_quirks(self): -- cgit v1.2.3-54-g00ecf From 2abc3c585cdc63723a0423d3871a792439ef2d79 Mon Sep 17 00:00:00 2001 From: rnhmjoj Date: Mon, 8 Mar 2021 22:05:26 +0100 Subject: userscripts/readability-js: fixup of 55fdae8 The mozilla/readability library doesn't output a body by default. So, let's add one in the header and plug the result in. --- misc/userscripts/readability-js | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/misc/userscripts/readability-js b/misc/userscripts/readability-js index 532df51c6..d286e570e 100755 --- a/misc/userscripts/readability-js +++ b/misc/userscripts/readability-js @@ -106,7 +106,12 @@ const HEADER = ` SAwLTIgMC44OTUtMiAyczAuODk1IDIgMiAyaDIwYzEuMTEgMCAyLTAuODk1IDItMnMtMC44OTUtMi0yLTJ6bTAgOGgtMjBjLTEuMTEgMC0yIDAuODk1LTIg MnMwLjg5NSAyIDIgMmgyMGMxLjExIDAgMi0wLjg5NSAyLTJzLTAuODk1LTItMi0yem0tMTIgOGgtOGMtMS4xMSAwLTIgMC44OTUtMiAyczAuODk1IDIgMiA yaDhjMS4xMSAwIDItMC44OTUgMi0ycy0wLjg5NS0yLTItMnoiIGZpbGw9IiNmZmYiLz4KPC9nPgo8L3N2Zz4K"/> -`; + + + %s + + +`; const scriptsDir = path.join(process.env.QUTE_DATA_DIR, 'userscripts'); const tmpFile = path.join(scriptsDir, '/readability.html'); @@ -129,10 +134,7 @@ else { getDOM(target, domOpts).then(dom => { let reader = new Readability(dom.window.document); let article = reader.parse(); - let content = util.format(HEADER, article.title) + article.content; - - // add a class to make styling the page easier - content = content.replace('', '') + let content = util.format(HEADER, article.title, article.content); fs.writeFile(tmpFile, content, (err) => { if (err) { -- cgit v1.2.3-54-g00ecf From e6f470105f2a4a355aeb299a8ebafeba50308091 Mon Sep 17 00:00:00 2001 From: rien333 Date: Mon, 8 Mar 2021 23:16:22 +0100 Subject: Use HTML5 meta tag --- misc/userscripts/readability-js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/misc/userscripts/readability-js b/misc/userscripts/readability-js index 532df51c6..340e3bbb9 100755 --- a/misc/userscripts/readability-js +++ b/misc/userscripts/readability-js @@ -39,8 +39,8 @@ const HEADER = ` - - + + %s