From c713522c8a765564e5d0acc01ef7ad6b746379ff Mon Sep 17 00:00:00 2001 From: Sebastian Schulze Date: Wed, 26 Aug 2020 21:50:07 +0200 Subject: [PATCH 01/50] Fix gopass 1.10.x deprecation in qute-pass userscript Since gopass 1.10, reading secrets via "gopass " will yield a deprecation warning which _run_pass() will stumble over. As it turns out - even "pass" can be called with "pass show ", so the fix will be compatible with both gopass and pass. Refs #5690 --- misc/userscripts/qute-pass | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/misc/userscripts/qute-pass b/misc/userscripts/qute-pass index 9d078e94f..10f215992 100755 --- a/misc/userscripts/qute-pass +++ b/misc/userscripts/qute-pass @@ -142,7 +142,7 @@ def _run_pass(pass_arguments, encoding): def pass_(path, encoding): - return _run_pass([path], encoding) + return _run_pass(['show', path], encoding) def pass_otp(path, encoding): From 4b7a3aaae78d530f62334fe59b49c5755b89a5f2 Mon Sep 17 00:00:00 2001 From: qutebrowser bot Date: Mon, 31 Aug 2020 04:21:24 +0000 Subject: [PATCH 02/50] Update dependencies --- misc/requirements/requirements-dev.txt | 6 +++--- misc/requirements/requirements-flake8.txt | 2 +- misc/requirements/requirements-pylint.txt | 2 +- misc/requirements/requirements-pyqt-5.12.txt | 2 +- misc/requirements/requirements-pyqt-5.13.txt | 2 +- misc/requirements/requirements-pyqt-5.14.txt | 2 +- misc/requirements/requirements-pyqt-5.15.txt | 2 +- misc/requirements/requirements-pyqt.txt | 2 +- misc/requirements/requirements-tests.txt | 10 +++++----- 9 files changed, 15 insertions(+), 15 deletions(-) diff --git a/misc/requirements/requirements-dev.txt b/misc/requirements/requirements-dev.txt index c543088c6..b48786862 100644 --- a/misc/requirements/requirements-dev.txt +++ b/misc/requirements/requirements-dev.txt @@ -5,7 +5,7 @@ certifi==2020.6.20 cffi==1.14.2 chardet==3.0.4 colorama==0.4.3 -cryptography==3.0 +cryptography==3.1 cssutils==1.0.2 github3.py==1.3.0 hunter==3.2.1 @@ -16,10 +16,10 @@ packaging==20.4 pycparser==2.20 Pympler==0.8 pyparsing==2.4.7 -PyQt-builder==1.4.0 +PyQt-builder==1.5.0 python-dateutil==2.8.1 requests==2.24.0 -sip==5.3.0 +sip==5.4.0 six==1.15.0 toml==0.10.1 uritemplate==3.0.1 diff --git a/misc/requirements/requirements-flake8.txt b/misc/requirements/requirements-flake8.txt index 00d22b236..0764490a3 100644 --- a/misc/requirements/requirements-flake8.txt +++ b/misc/requirements/requirements-flake8.txt @@ -18,7 +18,7 @@ flake8-tuple==0.4.1 mccabe==0.6.1 pep8-naming==0.11.1 pycodestyle==2.6.0 -pydocstyle==5.1.0 +pydocstyle==5.1.1 pyflakes==2.2.0 six==1.15.0 snowballstemmer==2.0.0 diff --git a/misc/requirements/requirements-pylint.txt b/misc/requirements/requirements-pylint.txt index 3c7440627..08c1d2c10 100644 --- a/misc/requirements/requirements-pylint.txt +++ b/misc/requirements/requirements-pylint.txt @@ -4,7 +4,7 @@ astroid==2.3.3 # rq.filter: < 2.4 certifi==2020.6.20 cffi==1.14.2 chardet==3.0.4 -cryptography==3.0 +cryptography==3.1 github3.py==1.3.0 idna==2.10 isort==4.3.21 diff --git a/misc/requirements/requirements-pyqt-5.12.txt b/misc/requirements/requirements-pyqt-5.12.txt index 9b458cd98..80a700f09 100644 --- a/misc/requirements/requirements-pyqt-5.12.txt +++ b/misc/requirements/requirements-pyqt-5.12.txt @@ -1,5 +1,5 @@ # This file is automatically generated by scripts/dev/recompile_requirements.py PyQt5==5.12.3 # rq.filter: < 5.13 -PyQt5-sip==12.8.0 +PyQt5-sip==12.8.1 PyQtWebEngine==5.12.1 # rq.filter: < 5.13 diff --git a/misc/requirements/requirements-pyqt-5.13.txt b/misc/requirements/requirements-pyqt-5.13.txt index 7c07eac3d..438c600da 100644 --- a/misc/requirements/requirements-pyqt-5.13.txt +++ b/misc/requirements/requirements-pyqt-5.13.txt @@ -1,5 +1,5 @@ # This file is automatically generated by scripts/dev/recompile_requirements.py PyQt5==5.13.2 # rq.filter: < 5.14 -PyQt5-sip==12.8.0 +PyQt5-sip==12.8.1 PyQtWebEngine==5.13.2 # rq.filter: < 5.14 diff --git a/misc/requirements/requirements-pyqt-5.14.txt b/misc/requirements/requirements-pyqt-5.14.txt index c82acedb0..d515e717f 100644 --- a/misc/requirements/requirements-pyqt-5.14.txt +++ b/misc/requirements/requirements-pyqt-5.14.txt @@ -1,5 +1,5 @@ # This file is automatically generated by scripts/dev/recompile_requirements.py PyQt5==5.14.2 # rq.filter: < 5.15 -PyQt5-sip==12.8.0 +PyQt5-sip==12.8.1 PyQtWebEngine==5.14.0 # rq.filter: < 5.15 diff --git a/misc/requirements/requirements-pyqt-5.15.txt b/misc/requirements/requirements-pyqt-5.15.txt index c21b7b742..93b31c40b 100644 --- a/misc/requirements/requirements-pyqt-5.15.txt +++ b/misc/requirements/requirements-pyqt-5.15.txt @@ -1,5 +1,5 @@ # This file is automatically generated by scripts/dev/recompile_requirements.py PyQt5==5.15.0 # rq.filter: < 6 -PyQt5-sip==12.8.0 +PyQt5-sip==12.8.1 PyQtWebEngine==5.15.0 # rq.filter: < 6 diff --git a/misc/requirements/requirements-pyqt.txt b/misc/requirements/requirements-pyqt.txt index 74d86e8d5..53a8b4af8 100644 --- a/misc/requirements/requirements-pyqt.txt +++ b/misc/requirements/requirements-pyqt.txt @@ -1,5 +1,5 @@ # This file is automatically generated by scripts/dev/recompile_requirements.py PyQt5==5.15.0 -PyQt5-sip==12.8.0 +PyQt5-sip==12.8.1 PyQtWebEngine==5.15.0 diff --git a/misc/requirements/requirements-tests.txt b/misc/requirements/requirements-tests.txt index a82ba796c..3497a7937 100644 --- a/misc/requirements/requirements-tests.txt +++ b/misc/requirements/requirements-tests.txt @@ -12,7 +12,7 @@ EasyProcess==0.3 Flask==1.1.2 glob2==0.7 hunter==3.2.1 -hypothesis==5.29.0 +hypothesis==5.30.0 idna==2.10 iniconfig==1.0.1 itsdangerous==1.1.0 @@ -21,9 +21,9 @@ jaraco.functools==3.0.1 ; python_version>="3.6" Mako==1.1.3 manhole==1.6.0 # MarkupSafe==1.1.1 -more-itertools==8.4.0 +more-itertools==8.5.0 packaging==20.4 -parse==1.16.0 +parse==1.17.0 parse-type==0.5.2 pluggy==0.13.1 py==1.9.0 @@ -35,10 +35,10 @@ pytest-bdd==3.4.0 pytest-benchmark==3.2.3 pytest-cov==2.10.1 pytest-instafail==0.4.2 -pytest-mock==3.3.0 +pytest-mock==3.3.1 pytest-qt==3.3.0 pytest-repeat==0.8.0 -pytest-rerunfailures==9.0 +pytest-rerunfailures==9.1 pytest-xvfb==2.0.0 PyVirtualDisplay==1.3.2 requests==2.24.0 From de2c86cbe66ca5b3421ad765a1208bcceb8f19cb Mon Sep 17 00:00:00 2001 From: Florian Bruhin Date: Mon, 31 Aug 2020 22:26:41 +0200 Subject: [PATCH 03/50] Add more changelog URLs --- scripts/dev/recompile_requirements.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/scripts/dev/recompile_requirements.py b/scripts/dev/recompile_requirements.py index e36d7ee1d..4c870cebd 100644 --- a/scripts/dev/recompile_requirements.py +++ b/scripts/dev/recompile_requirements.py @@ -43,6 +43,7 @@ CHANGELOG_URLS = { 'pylint': 'http://pylint.pycqa.org/en/latest/whatsnew/changelog.html', 'setuptools': 'https://github.com/pypa/setuptools/blob/master/CHANGES.rst', 'pytest-cov': 'https://github.com/pytest-dev/pytest-cov/blob/master/CHANGELOG.rst', + 'pytest-rerunfailures': 'https://github.com/pytest-dev/pytest-rerunfailures/blob/master/CHANGES.rst', 'requests': 'https://github.com/psf/requests/blob/master/HISTORY.md', 'requests-file': 'https://github.com/dashea/requests-file/blob/master/CHANGES.rst', 'werkzeug': 'https://github.com/pallets/werkzeug/blob/master/CHANGES.rst', @@ -101,7 +102,10 @@ CHANGELOG_URLS = { 'pep517': 'https://github.com/pypa/pep517/commits/master', 'cryptography': 'https://cryptography.io/en/latest/changelog/', 'toml': 'https://github.com/uiri/toml/releases', - 'pyqt': 'https://www.riverbankcomputing.com/', + 'pyqt': 'https://www.riverbankcomputing.com/news', + 'PyQt-builder': 'https://www.riverbankcomputing.com/news', + 'PyQt5-sip': 'https://www.riverbankcomputing.com/news', + 'sip': 'https://www.riverbankcomputing.com/news', 'vulture': 'https://github.com/jendrikseipp/vulture/blob/master/CHANGELOG.md', 'distlib': 'https://bitbucket.org/pypa/distlib/src/master/CHANGES.rst', 'py-cpuinfo': 'https://github.com/workhorsy/py-cpuinfo/blob/master/ChangeLog', From ecc548a4d7d06c3212b801a1b6a70f4db1ce42a5 Mon Sep 17 00:00:00 2001 From: Florian Bruhin Date: Sat, 5 Sep 2020 19:32:51 +0200 Subject: [PATCH 04/50] Update changelog --- doc/changelog.asciidoc | 3 +++ 1 file changed, 3 insertions(+) diff --git a/doc/changelog.asciidoc b/doc/changelog.asciidoc index 17635fd50..2af326065 100644 --- a/doc/changelog.asciidoc +++ b/doc/changelog.asciidoc @@ -113,6 +113,9 @@ Fixed - Highlighting in the completion now works properly when UTF-16 surrogate pairs (such as emoji) are involved. - When a windowed inspector is clicked, insert mode now isn't entered anymore. +- The `qute-pass` userscript now works correctly with newer + `gopass` versions again, instead of copying a deprecation notice + as password. v1.13.1 (2020-07-17) -------------------- From 5bba120a0bad593083f6f54fda5643e93624ef5f Mon Sep 17 00:00:00 2001 From: Florian Bruhin Date: Sat, 5 Sep 2020 19:51:59 +0200 Subject: [PATCH 05/50] Use URL string for parse_javascript_url The previous approach converted "javascript:///" to "/" as Qt only sets an internal "hasAuthority" flag, but we can't actually know whether an (empty) authority is present or not. However, QUrl doesn't let us get a FullyDecoded string because the value could be ambiguous - instead, we get the FullyEncoded variant now, and decode it via Python's urllib.parse. This seems to fix the /// corner case, and also is much closer to the standard. --- qutebrowser/utils/urlutils.py | 8 +++----- tests/unit/utils/test_urlutils.py | 2 ++ 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/qutebrowser/utils/urlutils.py b/qutebrowser/utils/urlutils.py index a691b2cbc..eb3bde063 100644 --- a/qutebrowser/utils/urlutils.py +++ b/qutebrowser/utils/urlutils.py @@ -644,12 +644,10 @@ def parse_javascript_url(url: QUrl) -> str: raise Error("URL contains unexpected components: {}" .format(url.authority())) - code = url.path(QUrl.FullyDecoded) - if url.hasQuery(): - code += '?' + url.query(QUrl.FullyDecoded) - if url.hasFragment(): - code += '#' + url.fragment(QUrl.FullyDecoded) + urlstr = url.toString(QUrl.FullyEncoded) + urlstr = urllib.parse.unquote(urlstr) + code = urlstr[len('javascript:'):] if not code: raise Error("Resulted in empty JavaScript code") diff --git a/tests/unit/utils/test_urlutils.py b/tests/unit/utils/test_urlutils.py index 04f7f04e6..7fd52152c 100644 --- a/tests/unit/utils/test_urlutils.py +++ b/tests/unit/utils/test_urlutils.py @@ -781,6 +781,8 @@ class TestParseJavascriptUrl: @pytest.mark.parametrize('url, source', [ (QUrl('javascript:"hello" %0a "world"'), '"hello" \n "world"'), + (QUrl('javascript:/'), '/'), + (QUrl('javascript:///'), '///'), # https://github.com/web-platform-tests/wpt/blob/master/html/browsers/browsing-the-web/navigating-across-documents/javascript-url-query-fragment-components.html (QUrl('javascript:"nope" ? "yep" : "what";'), '"nope" ? "yep" : "what";'), (QUrl('javascript:"wrong"; // # %0a "ok";'), '"wrong"; // # \n "ok";'), From f97c9a2efa6839577f516092f7a06a7efa4c01b1 Mon Sep 17 00:00:00 2001 From: Florian Bruhin Date: Sat, 5 Sep 2020 20:29:41 +0200 Subject: [PATCH 06/50] Fix mypy --- qutebrowser/utils/urlutils.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/qutebrowser/utils/urlutils.py b/qutebrowser/utils/urlutils.py index eb3bde063..a14be78a8 100644 --- a/qutebrowser/utils/urlutils.py +++ b/qutebrowser/utils/urlutils.py @@ -644,7 +644,7 @@ def parse_javascript_url(url: QUrl) -> str: raise Error("URL contains unexpected components: {}" .format(url.authority())) - urlstr = url.toString(QUrl.FullyEncoded) + urlstr = url.toString(QUrl.FullyEncoded) # type: ignore[arg-type] urlstr = urllib.parse.unquote(urlstr) code = urlstr[len('javascript:'):] From 489a8463543ab715e8f5e178d243bcbdecc87f10 Mon Sep 17 00:00:00 2001 From: Florian Bruhin Date: Sat, 5 Sep 2020 23:19:50 +0200 Subject: [PATCH 07/50] Add gruvbox theme to configuring docs --- doc/help/configuring.asciidoc | 1 + 1 file changed, 1 insertion(+) diff --git a/doc/help/configuring.asciidoc b/doc/help/configuring.asciidoc index 90b7ed65b..4f1d872f7 100644 --- a/doc/help/configuring.asciidoc +++ b/doc/help/configuring.asciidoc @@ -398,6 +398,7 @@ Pre-built colorschemes - Two implementations of the https://github.com/arcticicestudio/nord[Nord] colorscheme for qutebrowser exist: https://github.com/Linuus/nord-qutebrowser[Linuus], https://github.com/KnownAsDon/QuteBrowser-Nord-Theme[KnownAsDon] - https://github.com/dracula/qutebrowser-dracula-theme[Dracula] - https://gitlab.com/lovetocode999/selenized-qutebrowser[Selenized] +- https://github.com/The-Compiler/dotfiles/blob/master/qutebrowser/gruvbox.py[gruvbox] Avoiding flake8 errors ^^^^^^^^^^^^^^^^^^^^^^ From 3abfd78c25c10c3906d4686ca6245cebc73ff407 Mon Sep 17 00:00:00 2001 From: Florian Bruhin Date: Sun, 6 Sep 2020 16:37:59 +0200 Subject: [PATCH 08/50] Fix some a/an spelling mistakes --- doc/changelog.asciidoc | 2 +- doc/faq.asciidoc | 2 +- misc/userscripts/README.md | 2 +- pytest.ini | 2 +- scripts/dev/misc_checks.py | 2 +- tests/unit/utils/test_jinja.py | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) diff --git a/doc/changelog.asciidoc b/doc/changelog.asciidoc index 2af326065..6ff7f2791 100644 --- a/doc/changelog.asciidoc +++ b/doc/changelog.asciidoc @@ -1131,7 +1131,7 @@ Fixed - `qute://` pages now work properly on Qt 5.11.2 - Error when passing a substring with spaces to `:tab-take`. -- Greasemonkey scripts which start with an UTF-8 BOM are now handled correctly. +- Greasemonkey scripts which start with a UTF-8 BOM are now handled correctly. - When no documentation has been generated, the plaintext documentation now can be shown for more files such as `qute://help/userscripts.html`. - Crash when doing initial run on Wayland without XWayland. diff --git a/doc/faq.asciidoc b/doc/faq.asciidoc index e0b102683..be946fb83 100644 --- a/doc/faq.asciidoc +++ b/doc/faq.asciidoc @@ -226,7 +226,7 @@ Why does it take longer to open a URL in qutebrowser than in chromium?:: One workaround is to use this https://github.com/qutebrowser/qutebrowser/blob/master/scripts/open_url_in_instance.sh[script] and place it in your $PATH with the name "qutebrowser". This - script passes the URL via an unix socket to qutebrowser (if its + script passes the URL via a unix socket to qutebrowser (if its running already) using socat which is much faster and starts a new qutebrowser if it is not running already. diff --git a/misc/userscripts/README.md b/misc/userscripts/README.md index 729e63f6e..d2519a672 100644 --- a/misc/userscripts/README.md +++ b/misc/userscripts/README.md @@ -40,7 +40,7 @@ The following userscripts are included in the current directory. The following userscripts can be found on their own repositories. -- [qurlshare](https://github.com/sim590/qurlshare): *secure* sharing of an URL between qutebrowser +- [qurlshare](https://github.com/sim590/qurlshare): *secure* sharing of a URL between qutebrowser instances using a distributed hash table. - [qutebrowser-userscripts](https://github.com/cryzed/qutebrowser-userscripts): a small pack of userscripts. diff --git a/pytest.ini b/pytest.ini index 1235efb4b..51411e11e 100644 --- a/pytest.ini +++ b/pytest.ini @@ -35,7 +35,7 @@ markers = no_invalid_lines: Don't fail on unparseable lines in end2end tests qtbug60673: Tests which are broken if the conversion from orange selection to real selection is flaky fake_os: Fake utils.is_* to a fake operating system - unicode_locale: Tests which need an unicode locale to work + unicode_locale: Tests which need a unicode locale to work qtwebkit6021_xfail: Tests which would fail on WebKit version 602.1 js_headers: Sets JS headers dynamically on QtWebEngine (unsupported on some versions) qtwebkit_pdf_imageformat_skip: Broken on QtWebKit with PDF image format plugin installed diff --git a/scripts/dev/misc_checks.py b/scripts/dev/misc_checks.py index 6bb3eb1ca..7f116c474 100644 --- a/scripts/dev/misc_checks.py +++ b/scripts/dev/misc_checks.py @@ -90,7 +90,7 @@ def check_spelling(): 'exitted', 'mininum', 'resett?ed', 'recieved', 'regularily', 'underlaying', 'inexistant', 'elipsis', 'commiting', 'existant', 'resetted', 'similarily', 'informations', 'an url', 'treshold', - 'artefact'} + 'artefact', 'an unix', 'an utf', 'an unicode'} # Words which look better when splitted, but might need some fine tuning. words |= {'webelements', 'mouseevent', 'keysequence', 'normalmode', diff --git a/tests/unit/utils/test_jinja.py b/tests/unit/utils/test_jinja.py index c398118cb..b64b8e0fe 100644 --- a/tests/unit/utils/test_jinja.py +++ b/tests/unit/utils/test_jinja.py @@ -115,7 +115,7 @@ def test_not_found(caplog): def test_utf8(): - """Test rendering with an UTF8 template. + """Test rendering with a UTF8 template. This was an attempt to get a failing test case for #127 but it seems the issue is elsewhere. From a78442bb67c138a85d35a0e4f5bebeab77aa70e5 Mon Sep 17 00:00:00 2001 From: Florian Bruhin Date: Sun, 6 Sep 2020 17:23:03 +0200 Subject: [PATCH 09/50] Rewrite file collection in misc_checks.py Now collects all (non-ignored) files via `git ls-files` and only skips some binary formats. Also adds some type annotations and fixes some newly unconvered UK-spellings. --- misc/nsis/qutebrowser.nsi | 1 + misc/userscripts/cast | 2 +- misc/userscripts/qute-bitwarden | 2 +- scripts/dev/misc_checks.py | 99 +++++++++++-------- .../hints/hide_unmatched_rapid_hints.html | 2 +- 5 files changed, 61 insertions(+), 45 deletions(-) diff --git a/misc/nsis/qutebrowser.nsi b/misc/nsis/qutebrowser.nsi index d9b8fbf8d..77fd373eb 100755 --- a/misc/nsis/qutebrowser.nsi +++ b/misc/nsis/qutebrowser.nsi @@ -1,4 +1,5 @@ # Copyright 2018 Florian Bruhin (The Compiler) +# encoding: iso-8859-1 # # This file is part of qutebrowser. # diff --git a/misc/userscripts/cast b/misc/userscripts/cast index f7b64df70..8bbf05a40 100755 --- a/misc/userscripts/cast +++ b/misc/userscripts/cast @@ -1,6 +1,6 @@ #!/usr/bin/env bash # -# Behaviour +# Behavior # Userscript for qutebrowser which casts the url passed in $1 to the default # ChromeCast device in the network using the program `castnow` # diff --git a/misc/userscripts/qute-bitwarden b/misc/userscripts/qute-bitwarden index d5c4b1e2d..ca9d646e4 100755 --- a/misc/userscripts/qute-bitwarden +++ b/misc/userscripts/qute-bitwarden @@ -281,7 +281,7 @@ def main(arguments): qute_command('enter-mode insert') # If it finds a TOTP code, it copies it to the clipboard, - # which is the same behaviour as the Firefox add-on. + # which is the same behavior as the Firefox add-on. if not arguments.totp_only and totp and arguments.totp: # The import is done here, to make pyperclip an optional dependency import pyperclip diff --git a/scripts/dev/misc_checks.py b/scripts/dev/misc_checks.py index 7f116c474..e715fc6c5 100644 --- a/scripts/dev/misc_checks.py +++ b/scripts/dev/misc_checks.py @@ -30,33 +30,45 @@ import tokenize import traceback import collections import pathlib +from typing import List, Iterator, Optional sys.path.insert(0, os.path.join(os.path.dirname(__file__), os.pardir, os.pardir)) from scripts import utils - -def _get_files(only_py=False): - """Iterate over all python files and yield filenames.""" - for (dirpath, _dirnames, filenames) in os.walk('.'): - parts = dirpath.split(os.sep) - if len(parts) >= 2: - rootdir = parts[1] - if rootdir.startswith('.') or rootdir == 'htmlcov': - # ignore hidden dirs and htmlcov - continue - - if only_py: - endings = {'.py'} - else: - endings = {'.py', '.asciidoc', '.js', '.feature'} - files = (e for e in filenames if os.path.splitext(e)[1] in endings) - for name in files: - yield os.path.join(dirpath, name) +BINARY_EXTS = {'.png', '.icns', '.ico', '.bmp', '.gz', '.bin', '.pdf', + '.sqlite', '.woff2'} -def check_git(): +def _get_files( + *, + verbose: bool, + ignored: List[pathlib.Path] = None +) -> Iterator[pathlib.Path]: + """Iterate over all files and yield filenames.""" + filenames = subprocess.run( + ['git', 'ls-files', '--cached', '--others', '--exclude-standard', '-z'], + stdout=subprocess.PIPE, + universal_newlines=True + ) + all_ignored = ignored or [] + all_ignored.append( + pathlib.Path('tests', 'unit', 'scripts', 'importer_sample', 'chrome')) + + for filename in filenames.stdout.split('\0'): + path = pathlib.Path(filename) + is_ignored = any(path == p or p in path.parents for p in all_ignored) + if not filename or path.suffix in BINARY_EXTS or is_ignored: + continue + + if verbose: + print(path) + + yield path + + +def check_git(_args: argparse.Namespace) -> bool: """Check for uncommitted git files..""" if not os.path.isdir(".git"): print("No .git dir, ignoring") @@ -79,7 +91,7 @@ def check_git(): return status -def check_spelling(): +def check_spelling(args: argparse.Namespace) -> Optional[bool]: """Check commonly misspelled words.""" # Words which I often misspell words = {'behaviour', 'quitted', 'likelyhood', 'sucessfully', @@ -95,32 +107,31 @@ def check_spelling(): # Words which look better when splitted, but might need some fine tuning. words |= {'webelements', 'mouseevent', 'keysequence', 'normalmode', 'eventloops', 'sizehint', 'statemachine', 'metaobject', - 'logrecord', 'filetype'} + 'logrecord'} # Files which should be ignored, e.g. because they come from another # package + hint_data = pathlib.Path('tests', 'end2end', 'data', 'hints') ignored = [ - os.path.join('.', 'scripts', 'dev', 'misc_checks.py'), - os.path.join('.', 'qutebrowser', '3rdparty', 'pdfjs'), - os.path.join('.', 'tests', 'end2end', 'data', 'hints', 'ace', - 'ace.js'), + pathlib.Path('scripts', 'dev', 'misc_checks.py'), + pathlib.Path('qutebrowser', '3rdparty', 'pdfjs'), + hint_data / 'ace' / 'ace.js', + hint_data / 'bootstrap' / 'bootstrap.css', ] seen = collections.defaultdict(list) try: ok = True - for fn in _get_files(): - with tokenize.open(fn) as f: - if any(fn.startswith(i) for i in ignored): - continue + for path in _get_files(verbose=args.verbose, ignored=ignored): + with tokenize.open(str(path)) as f: for line in f: for w in words: pattern = '[{}{}]{}'.format(w[0], w[0].upper(), w[1:]) if (re.search(pattern, line) and - fn not in seen[w] and + path not in seen[w] and '# pragma: no spellcheck' not in line): - print('Found "{}" in {}!'.format(w, fn)) - seen[w].append(fn) + print('Found "{}" in {}!'.format(w, path)) + seen[w].append(path) ok = False print() return ok @@ -129,15 +140,18 @@ def check_spelling(): return None -def check_vcs_conflict(): +def check_vcs_conflict(args: argparse.Namespace) -> Optional[bool]: """Check VCS conflict markers.""" try: ok = True - for fn in _get_files(only_py=True): - with tokenize.open(fn) as f: + for path in _get_files(verbose=args.verbose): + if path.suffix in {'.rst', '.asciidoc'}: + # False positives + continue + with tokenize.open(str(path)) as f: for line in f: if any(line.startswith(c * 7) for c in '<>=|'): - print("Found conflict marker in {}".format(fn)) + print("Found conflict marker in {}".format(path)) ok = False print() return ok @@ -146,7 +160,7 @@ def check_vcs_conflict(): return None -def check_userscripts_descriptions(): +def check_userscripts_descriptions(_args: argparse.Namespace) -> bool: """Make sure all userscripts are described properly.""" folder = pathlib.Path('misc/userscripts') readme = folder / 'README.md' @@ -178,20 +192,21 @@ def check_userscripts_descriptions(): return ok -def main(): +def main() -> int: parser = argparse.ArgumentParser() + parser.add_argument('--verbose', action='store_true', help='Show checked filenames') parser.add_argument('checker', choices=('git', 'vcs', 'spelling', 'userscripts'), help="Which checker to run.") args = parser.parse_args() if args.checker == 'git': - ok = check_git() + ok = check_git(args) elif args.checker == 'vcs': - ok = check_vcs_conflict() + ok = check_vcs_conflict(args) elif args.checker == 'spelling': - ok = check_spelling() + ok = check_spelling(args) elif args.checker == 'userscripts': - ok = check_userscripts_descriptions() + ok = check_userscripts_descriptions(args) return 0 if ok else 1 diff --git a/tests/manual/hints/hide_unmatched_rapid_hints.html b/tests/manual/hints/hide_unmatched_rapid_hints.html index 1630a790e..98affa254 100644 --- a/tests/manual/hints/hide_unmatched_rapid_hints.html +++ b/tests/manual/hints/hide_unmatched_rapid_hints.html @@ -7,7 +7,7 @@

When hints.hide_unmatched_rapid_hints is set to true (default), rapid hints behave like normal hints, i.e. unmatched hints will be hidden as you type. Setting the option to false will disable hiding in rapid mode, which is sometimes useful (see #1799).

Note that when hinting in number mode, the hints.hide_unmatched_rapid_hints option affects typing the hint string (number), but not the filter (letters).

-

Here is couple of invalid links to test the behaviour:

+

Here is couple of invalid links to test the behavior:

one

two

three

From 502402911c5422d958cb43f49b4fa0b7fb86be3b Mon Sep 17 00:00:00 2001 From: Florian Bruhin Date: Sun, 6 Sep 2020 17:52:11 +0200 Subject: [PATCH 10/50] Fix lint --- scripts/dev/misc_checks.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/scripts/dev/misc_checks.py b/scripts/dev/misc_checks.py index e715fc6c5..df8f30690 100644 --- a/scripts/dev/misc_checks.py +++ b/scripts/dev/misc_checks.py @@ -50,7 +50,8 @@ def _get_files( filenames = subprocess.run( ['git', 'ls-files', '--cached', '--others', '--exclude-standard', '-z'], stdout=subprocess.PIPE, - universal_newlines=True + universal_newlines=True, + check=True, ) all_ignored = ignored or [] all_ignored.append( @@ -68,7 +69,7 @@ def _get_files( yield path -def check_git(_args: argparse.Namespace) -> bool: +def check_git(_args: argparse.Namespace = None) -> bool: """Check for uncommitted git files..""" if not os.path.isdir(".git"): print("No .git dir, ignoring") @@ -160,7 +161,7 @@ def check_vcs_conflict(args: argparse.Namespace) -> Optional[bool]: return None -def check_userscripts_descriptions(_args: argparse.Namespace) -> bool: +def check_userscripts_descriptions(_args: argparse.Namespace = None) -> bool: """Make sure all userscripts are described properly.""" folder = pathlib.Path('misc/userscripts') readme = folder / 'README.md' From 0c4ac11daeac9d00dccb3525b6812002dc6270b6 Mon Sep 17 00:00:00 2001 From: qutebrowser bot Date: Mon, 7 Sep 2020 04:31:25 +0000 Subject: [PATCH 11/50] Update dependencies --- misc/requirements/requirements-dev.txt | 2 +- misc/requirements/requirements-flake8.txt | 2 +- misc/requirements/requirements-mypy.txt | 3 +-- misc/requirements/requirements-tests.txt | 6 +++--- misc/requirements/requirements-tox.txt | 2 +- requirements.txt | 2 +- 6 files changed, 8 insertions(+), 9 deletions(-) diff --git a/misc/requirements/requirements-dev.txt b/misc/requirements/requirements-dev.txt index b48786862..798fecad6 100644 --- a/misc/requirements/requirements-dev.txt +++ b/misc/requirements/requirements-dev.txt @@ -8,7 +8,7 @@ colorama==0.4.3 cryptography==3.1 cssutils==1.0.2 github3.py==1.3.0 -hunter==3.2.1 +hunter==3.2.2 idna==2.10 jwcrypto==0.8 manhole==1.6.0 diff --git a/misc/requirements/requirements-flake8.txt b/misc/requirements/requirements-flake8.txt index 0764490a3..afd4f0bd6 100644 --- a/misc/requirements/requirements-flake8.txt +++ b/misc/requirements/requirements-flake8.txt @@ -1,6 +1,6 @@ # This file is automatically generated by scripts/dev/recompile_requirements.py -attrs==20.1.0 +attrs==20.2.0 flake8==3.8.3 flake8-bugbear==20.1.4 flake8-builtins==1.5.3 diff --git a/misc/requirements/requirements-mypy.txt b/misc/requirements/requirements-mypy.txt index 09d11ea6c..be4c380a2 100644 --- a/misc/requirements/requirements-mypy.txt +++ b/misc/requirements/requirements-mypy.txt @@ -1,6 +1,6 @@ # This file is automatically generated by scripts/dev/recompile_requirements.py -diff-cover==3.0.1 +diff-cover==4.0.0 inflect==4.1.0 Jinja2==2.11.2 jinja2-pluralize==0.3.0 @@ -11,6 +11,5 @@ mypy-extensions==0.4.3 pluggy==0.13.1 Pygments==2.6.1 -e git+https://github.com/stlehmann/PyQt5-stubs.git@master#egg=PyQt5_stubs -six==1.15.0 typed-ast==1.4.1 typing-extensions==3.7.4.3 diff --git a/misc/requirements/requirements-tests.txt b/misc/requirements/requirements-tests.txt index 3497a7937..fca663864 100644 --- a/misc/requirements/requirements-tests.txt +++ b/misc/requirements/requirements-tests.txt @@ -1,6 +1,6 @@ # This file is automatically generated by scripts/dev/recompile_requirements.py -attrs==20.1.0 +attrs==20.2.0 beautifulsoup4==4.9.1 certifi==2020.6.20 chardet==3.0.4 @@ -11,8 +11,8 @@ coverage==5.2.1 EasyProcess==0.3 Flask==1.1.2 glob2==0.7 -hunter==3.2.1 -hypothesis==5.30.0 +hunter==3.2.2 +hypothesis==5.33.0 idna==2.10 iniconfig==1.0.1 itsdangerous==1.1.0 diff --git a/misc/requirements/requirements-tox.txt b/misc/requirements/requirements-tox.txt index 3fb7595ad..c7f99a2da 100644 --- a/misc/requirements/requirements-tox.txt +++ b/misc/requirements/requirements-tox.txt @@ -9,7 +9,7 @@ py==1.9.0 pyparsing==2.4.7 six==1.15.0 toml==0.10.1 -tox==3.19.0 +tox==3.20.0 tox-pip-version==0.0.7 tox-venv==0.4.0 virtualenv==20.0.31 diff --git a/requirements.txt b/requirements.txt index c3835488c..f54eecb94 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,6 +1,6 @@ # This file is automatically generated by scripts/dev/recompile_requirements.py -attrs==20.1.0 +attrs==20.2.0 colorama==0.4.3 cssutils==1.0.2 Jinja2==2.11.2 From d1632800327e3377450379dcbded0b894c64c498 Mon Sep 17 00:00:00 2001 From: Florian Bruhin Date: Mon, 7 Sep 2020 14:18:07 +0200 Subject: [PATCH 12/50] Find config.source'd files relative to --config-py --- doc/changelog.asciidoc | 4 ++++ qutebrowser/config/configfiles.py | 4 +++- tests/unit/config/test_configfiles.py | 20 +++++++++++++++++++- 3 files changed, 26 insertions(+), 2 deletions(-) diff --git a/doc/changelog.asciidoc b/doc/changelog.asciidoc index 6ff7f2791..e5c145e02 100644 --- a/doc/changelog.asciidoc +++ b/doc/changelog.asciidoc @@ -52,6 +52,10 @@ Changed - `:completion-item-focus` now understands `next-page` and `prev-page` with corresponding `` / `` default bindings. - When the last private window is closed, all private browsing data is now cleared. +- When `config.source(...)` is used with a `--config-py` argument given, + qutebrowser used to search relative files in the config basedir, leading to them + not being found when using a shared `config.py` for different basedirs. Instead, + they are now searched relative to the given `config.py` file. Added ~~~~~ diff --git a/qutebrowser/config/configfiles.py b/qutebrowser/config/configfiles.py index 9940a64ac..a1b0e75bd 100644 --- a/qutebrowser/config/configfiles.py +++ b/qutebrowser/config/configfiles.py @@ -606,7 +606,9 @@ class ConfigAPI: def source(self, filename: str) -> None: """Read the given config file from disk.""" if not os.path.isabs(filename): - filename = str(self.configdir / filename) + # We don't use self.configdir here so we get the proper file when starting + # with a --config-py argument given. + filename = os.path.join(os.path.dirname(standarddir.config_py()), filename) try: read_config_py(filename) diff --git a/tests/unit/config/test_configfiles.py b/tests/unit/config/test_configfiles.py index 27e96ef7d..bef4ef004 100644 --- a/tests/unit/config/test_configfiles.py +++ b/tests/unit/config/test_configfiles.py @@ -28,7 +28,7 @@ from PyQt5.QtCore import QSettings from qutebrowser.config import (config, configfiles, configexc, configdata, configtypes) -from qutebrowser.utils import utils, usertypes, urlmatch +from qutebrowser.utils import utils, usertypes, urlmatch, standarddir from qutebrowser.keyinput import keyutils @@ -1064,6 +1064,24 @@ class TestConfigPy: assert not config.instance.get_obj('content.javascript.enabled') + def test_source_configpy_arg(self, tmpdir, data_tmpdir, monkeypatch): + alt_filename = 'alt-config.py' + + alt_confpy_dir = tmpdir / 'alt-confpy-dir' + alt_confpy_dir.ensure(dir=True) + monkeypatch.setattr(standarddir, 'config_py', + lambda: str(alt_confpy_dir / alt_filename)) + + subfile = alt_confpy_dir / 'subfile.py' + subfile.write_text("c.content.javascript.enabled = False", + encoding='utf-8') + + alt_confpy = ConfPy(alt_confpy_dir, alt_filename) + alt_confpy.write("config.source('subfile.py')") + alt_confpy.read() + + assert not config.instance.get_obj('content.javascript.enabled') + def test_source_errors(self, tmpdir, confpy): subfile = tmpdir / 'config' / 'subfile.py' subfile.write_text("c.foo = 42", encoding='utf-8') From 53513219232089c85dcf2364bfc821130d2df4be Mon Sep 17 00:00:00 2001 From: Florian Bruhin Date: Wed, 9 Sep 2020 13:58:34 +0200 Subject: [PATCH 13/50] tests: Use hypothesis' sampled_from --- tests/unit/utils/test_urlmatch.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/unit/utils/test_urlmatch.py b/tests/unit/utils/test_urlmatch.py index 79504e9b2..a22842a0f 100644 --- a/tests/unit/utils/test_urlmatch.py +++ b/tests/unit/utils/test_urlmatch.py @@ -606,9 +606,9 @@ URL_TEXT = hst.text(alphabet=string.ascii_letters) @hypothesis.given(pattern=hst.builds( lambda *a: ''.join(a), # Scheme - hst.one_of(hst.just('*'), hst.just('http'), hst.just('file')), + hst.sampled_from(['*', 'http','file']), # Separator - hst.one_of(hst.just(':'), hst.just('://')), + hst.sampled_from([':', '://']), # Host hst.one_of(hst.just('*'), hst.builds(lambda *a: ''.join(a), hst.just('*.'), URL_TEXT), From a21b6d2af545de63bfb6bc478d534ddae4f16132 Mon Sep 17 00:00:00 2001 From: Florian Bruhin Date: Wed, 9 Sep 2020 14:28:12 +0200 Subject: [PATCH 14/50] Fix lint --- tests/unit/utils/test_urlmatch.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/unit/utils/test_urlmatch.py b/tests/unit/utils/test_urlmatch.py index a22842a0f..8292a09ad 100644 --- a/tests/unit/utils/test_urlmatch.py +++ b/tests/unit/utils/test_urlmatch.py @@ -606,7 +606,7 @@ URL_TEXT = hst.text(alphabet=string.ascii_letters) @hypothesis.given(pattern=hst.builds( lambda *a: ''.join(a), # Scheme - hst.sampled_from(['*', 'http','file']), + hst.sampled_from(['*', 'http', 'file']), # Separator hst.sampled_from([':', '://']), # Host From 21570b423ea4bb74c373993a5b5f66de5a4eef81 Mon Sep 17 00:00:00 2001 From: Florian Bruhin Date: Thu, 10 Sep 2020 11:40:02 +0200 Subject: [PATCH 15/50] Add third column to :help completion --- qutebrowser/completion/models/miscmodels.py | 4 +-- tests/unit/completion/test_models.py | 39 +++++++++++++++++---- 2 files changed, 34 insertions(+), 9 deletions(-) diff --git a/qutebrowser/completion/models/miscmodels.py b/qutebrowser/completion/models/miscmodels.py index 36e334955..9cf2d5fd6 100644 --- a/qutebrowser/completion/models/miscmodels.py +++ b/qutebrowser/completion/models/miscmodels.py @@ -39,11 +39,11 @@ def command(*, info): def helptopic(*, info): """A CompletionModel filled with help topics.""" - model = completionmodel.CompletionModel() + model = completionmodel.CompletionModel(column_widths=(20, 70, 10)) cmdlist = util.get_cmd_completions(info, include_aliases=False, include_hidden=True, prefix=':') - settings = ((opt.name, opt.description) + settings = ((opt.name, opt.description, info.config.get_str(opt.name)) for opt in configdata.DATA.values()) model.add_category(listcategory.ListCategory("Commands", cmdlist)) diff --git a/tests/unit/completion/test_models.py b/tests/unit/completion/test_models.py index e602f0ab6..269fd43ed 100644 --- a/tests/unit/completion/test_models.py +++ b/tests/unit/completion/test_models.py @@ -269,13 +269,38 @@ def test_help_completion(qtmodeltester, cmdutils_stub, key_config_stub, (':tab-close', 'Close the current tab.', ''), ], "Settings": [ - ('aliases', 'Aliases for commands.', None), - ('bindings.commands', 'Default keybindings', None), - ('bindings.default', 'Default keybindings', None), - ('completion.open_categories', 'Which categories to show (in ' - 'which order) in the :open completion.', None), - ('content.javascript.enabled', 'Enable/Disable JavaScript', None), - ('url.searchengines', 'searchengines list', None), + ( + 'aliases', + 'Aliases for commands.', + '{"q": "quit"}', + ), + ( + 'bindings.commands', + 'Default keybindings', + ('{"normal": {"": "quit", "I": "invalid", "ZQ": "quit", ' + '"d": "scroll down"}}'), + ), + ( + 'bindings.default', + 'Default keybindings', + '{"normal": {"": "quit", "d": "tab-close"}}', + ), + ( + 'completion.open_categories', + 'Which categories to show (in which order) in the :open completion.', + '["searchengines", "quickmarks", "bookmarks", "history"]', + ), + ( + 'content.javascript.enabled', + 'Enable/Disable JavaScript', + 'true' + ), + ( + 'url.searchengines', + 'searchengines list', + ('{"DEFAULT": "https://duckduckgo.com/?q={}", ' + '"google": "https://google.com/?q={}"}'), + ), ], }) From 8ddb8282fe25e39016ad513f7ac3e5c1425e88ee Mon Sep 17 00:00:00 2001 From: Florian Bruhin Date: Thu, 10 Sep 2020 11:46:12 +0200 Subject: [PATCH 16/50] tests: Make stubbed config less confusing --- tests/unit/completion/test_models.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/unit/completion/test_models.py b/tests/unit/completion/test_models.py index 269fd43ed..2ac7084dd 100644 --- a/tests/unit/completion/test_models.py +++ b/tests/unit/completion/test_models.py @@ -124,7 +124,7 @@ def configdata_stub(config_stub, monkeypatch, configdata_init): no_autoconfig=True)), ('bindings.commands', configdata.Option( name='bindings.commands', - description='Default keybindings', + description='Custom keybindings', typ=configtypes.Dict( keytype=configtypes.String(), valtype=configtypes.Dict( @@ -276,7 +276,7 @@ def test_help_completion(qtmodeltester, cmdutils_stub, key_config_stub, ), ( 'bindings.commands', - 'Default keybindings', + 'Custom keybindings', ('{"normal": {"": "quit", "I": "invalid", "ZQ": "quit", ' '"d": "scroll down"}}'), ), @@ -934,7 +934,7 @@ def test_setting_option_completion(qtmodeltester, config_stub, _check_completions(model, { "Options": [ ('aliases', 'Aliases for commands.', '{"q": "quit"}'), - ('bindings.commands', 'Default keybindings', ( + ('bindings.commands', 'Custom keybindings', ( '{"normal": {"": "quit", "I": "invalid", ' '"ZQ": "quit", "d": "scroll down"}}')), ('completion.open_categories', 'Which categories to show (in ' @@ -958,7 +958,7 @@ def test_setting_dict_option_completion(qtmodeltester, config_stub, _check_completions(model, { "Dict options": [ ('aliases', 'Aliases for commands.', '{"q": "quit"}'), - ('bindings.commands', 'Default keybindings', ( + ('bindings.commands', 'Custom keybindings', ( '{"normal": {"": "quit", "I": "invalid", ' '"ZQ": "quit", "d": "scroll down"}}')), ('url.searchengines', 'searchengines list', From e8cdc506a03c9ca64450d8b8a6501e5ae8079a61 Mon Sep 17 00:00:00 2001 From: Florian Bruhin Date: Thu, 10 Sep 2020 15:15:53 +0200 Subject: [PATCH 17/50] Recognize a class=nav-(prev|next) with :navigate --- doc/changelog.asciidoc | 3 +++ qutebrowser/browser/navigate.py | 11 ++++++++--- 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/doc/changelog.asciidoc b/doc/changelog.asciidoc index e5c145e02..a5c4df415 100644 --- a/doc/changelog.asciidoc +++ b/doc/changelog.asciidoc @@ -56,6 +56,9 @@ Changed qutebrowser used to search relative files in the config basedir, leading to them not being found when using a shared `config.py` for different basedirs. Instead, they are now searched relative to the given `config.py` file. +- `navigate prev` (`[[`) and `navigate next` (`]]`) now recognize links with + `nav-prev` and `nav-next` classes, such as those used by the Hugo static site + generator. Added ~~~~~ diff --git a/qutebrowser/browser/navigate.py b/qutebrowser/browser/navigate.py index 194246344..bca81f8d3 100644 --- a/qutebrowser/browser/navigate.py +++ b/qutebrowser/browser/navigate.py @@ -154,14 +154,19 @@ def strip(url, count): def _find_prevnext(prev, elems): """Find a prev/next element in the given list of elements.""" - # First check for + # First check for as well as + # e.g. (Hugo) rel_values = {'prev', 'previous'} if prev else {'next'} + classes = {'nav-prev'} if prev else {'nav-next'} for e in elems: - if e.tag_name() not in ['link', 'a'] or 'rel' not in e: + if e.tag_name() not in ['link', 'a']: continue - if set(e['rel'].split(' ')) & rel_values: + if 'rel' in e and set(e['rel'].split(' ')) & rel_values: log.hints.debug("Found {!r} with rel={}".format(e, e['rel'])) return e + elif set(e.classes()) & classes: + log.hints.debug("Found {!r} with class={}".format(e, e.classes())) + return e # Then check for regular links/buttons. elems = [e for e in elems if e.tag_name() != 'link'] From 861f3536056108a3a4d3f843497e45a33186ccd3 Mon Sep 17 00:00:00 2001 From: Florian Bruhin Date: Thu, 10 Sep 2020 16:26:57 +0200 Subject: [PATCH 18/50] Turn webelem.classes into a set The classes HTML attribute is an unordered set: https://html.spec.whatwg.org/multipage/dom.html#classes https://css-tricks.com/the-order-of-css-classes-in-html-doesnt-matter/ Let's also reflect it as a set then. --- qutebrowser/browser/navigate.py | 2 +- qutebrowser/browser/webelem.py | 4 ++-- qutebrowser/browser/webengine/webengineelem.py | 4 ++-- qutebrowser/browser/webkit/webkitelem.py | 4 ++-- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/qutebrowser/browser/navigate.py b/qutebrowser/browser/navigate.py index bca81f8d3..ddc100d14 100644 --- a/qutebrowser/browser/navigate.py +++ b/qutebrowser/browser/navigate.py @@ -164,7 +164,7 @@ def _find_prevnext(prev, elems): if 'rel' in e and set(e['rel'].split(' ')) & rel_values: log.hints.debug("Found {!r} with rel={}".format(e, e['rel'])) return e - elif set(e.classes()) & classes: + elif e.classes() & classes: log.hints.debug("Found {!r} with class={}".format(e, e.classes())) return e diff --git a/qutebrowser/browser/webelem.py b/qutebrowser/browser/webelem.py index 98c5bd6d1..e79b5145e 100644 --- a/qutebrowser/browser/webelem.py +++ b/qutebrowser/browser/webelem.py @@ -102,8 +102,8 @@ class AbstractWebElement(collections.abc.MutableMapping): """Get the geometry for this element.""" raise NotImplementedError - def classes(self) -> typing.List[str]: - """Get a list of classes assigned to this element.""" + def classes(self) -> typing.Set[str]: + """Get a set of classes assigned to this element.""" raise NotImplementedError def tag_name(self) -> str: diff --git a/qutebrowser/browser/webengine/webengineelem.py b/qutebrowser/browser/webengine/webengineelem.py index db5335a3c..e524b36d2 100644 --- a/qutebrowser/browser/webengine/webengineelem.py +++ b/qutebrowser/browser/webengine/webengineelem.py @@ -118,9 +118,9 @@ class WebEngineElement(webelem.AbstractWebElement): log.stub() return QRect() - def classes(self) -> typing.List[str]: + def classes(self) -> typing.Set[str]: """Get a list of classes assigned to this element.""" - return self._js_dict['class_name'].split() + return set(self._js_dict['class_name'].split()) def tag_name(self) -> str: """Get the tag name of this element. diff --git a/qutebrowser/browser/webkit/webkitelem.py b/qutebrowser/browser/webkit/webkitelem.py index 9f58031de..e73d6a9e8 100644 --- a/qutebrowser/browser/webkit/webkitelem.py +++ b/qutebrowser/browser/webkit/webkitelem.py @@ -101,9 +101,9 @@ class WebKitElement(webelem.AbstractWebElement): self._check_vanished() return self._elem.geometry() - def classes(self) -> typing.List[str]: + def classes(self) -> typing.Set[str]: self._check_vanished() - return self._elem.classes() + return set(self._elem.classes()) def tag_name(self) -> str: """Get the tag name for the current element.""" From e3fa34b97057fb05f6a297f727e7c8bbd4be0099 Mon Sep 17 00:00:00 2001 From: Florian Bruhin Date: Thu, 10 Sep 2020 16:28:24 +0200 Subject: [PATCH 19/50] misc_checks: Ignore wheels --- scripts/dev/misc_checks.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/dev/misc_checks.py b/scripts/dev/misc_checks.py index df8f30690..3586c5a17 100644 --- a/scripts/dev/misc_checks.py +++ b/scripts/dev/misc_checks.py @@ -38,7 +38,7 @@ sys.path.insert(0, os.path.join(os.path.dirname(__file__), os.pardir, from scripts import utils BINARY_EXTS = {'.png', '.icns', '.ico', '.bmp', '.gz', '.bin', '.pdf', - '.sqlite', '.woff2'} + '.sqlite', '.woff2', '.whl'} def _get_files( From abed001aa9471695b77fb4cd37f4fc3bd7ef2ca0 Mon Sep 17 00:00:00 2001 From: Florian Bruhin Date: Thu, 10 Sep 2020 16:40:23 +0200 Subject: [PATCH 20/50] misc_checks: Handle remaining binary files gracefully --- scripts/dev/misc_checks.py | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/scripts/dev/misc_checks.py b/scripts/dev/misc_checks.py index 3586c5a17..df2845405 100644 --- a/scripts/dev/misc_checks.py +++ b/scripts/dev/misc_checks.py @@ -63,6 +63,15 @@ def _get_files( if not filename or path.suffix in BINARY_EXTS or is_ignored: continue + try: + with tokenize.open(str(path)): + pass + except SyntaxError as e: + # Could not find encoding + utils.print_col("{} - maybe {} should be added to BINARY_EXTS?".format( + str(e).capitalize(), path.suffix), 'yellow') + continue + if verbose: print(path) From 3ec5e453498f7cb0ef7c7cbdb1c68caec060a096 Mon Sep 17 00:00:00 2001 From: Florian Bruhin Date: Sun, 13 Sep 2020 13:05:27 +0200 Subject: [PATCH 21/50] Add changelog URL for diff_cover --- scripts/dev/recompile_requirements.py | 1 + 1 file changed, 1 insertion(+) diff --git a/scripts/dev/recompile_requirements.py b/scripts/dev/recompile_requirements.py index 4c870cebd..94c128490 100644 --- a/scripts/dev/recompile_requirements.py +++ b/scripts/dev/recompile_requirements.py @@ -115,6 +115,7 @@ CHANGELOG_URLS = { 'idna': 'https://github.com/kjd/idna/blob/master/HISTORY.rst', 'tldextract': 'https://github.com/john-kurkowski/tldextract/blob/master/CHANGELOG.md', 'typing_extensions': 'https://github.com/python/typing/commits/master/typing_extensions', + 'diff_cover': 'https://github.com/Bachmann1234/diff_cover/blob/master/CHANGELOG', } # PyQt versions which need SIP v4 From 54d76ffc74929cde9927643e57a1f9ae1e54e26c Mon Sep 17 00:00:00 2001 From: qutebrowser bot Date: Mon, 14 Sep 2020 04:31:14 +0000 Subject: [PATCH 22/50] Update dependencies --- misc/requirements/requirements-mypy.txt | 2 +- misc/requirements/requirements-pyinstaller.txt | 2 +- misc/requirements/requirements-pyqt-5.15.txt | 4 ++-- misc/requirements/requirements-pyqt.txt | 4 ++-- misc/requirements/requirements-pyroma.txt | 2 +- misc/requirements/requirements-sphinx.txt | 2 +- misc/requirements/requirements-tests.txt | 12 ++++++------ requirements.txt | 2 +- 8 files changed, 15 insertions(+), 15 deletions(-) diff --git a/misc/requirements/requirements-mypy.txt b/misc/requirements/requirements-mypy.txt index be4c380a2..4c9e47067 100644 --- a/misc/requirements/requirements-mypy.txt +++ b/misc/requirements/requirements-mypy.txt @@ -9,7 +9,7 @@ MarkupSafe==1.1.1 mypy==0.782 mypy-extensions==0.4.3 pluggy==0.13.1 -Pygments==2.6.1 +Pygments==2.7.0 -e git+https://github.com/stlehmann/PyQt5-stubs.git@master#egg=PyQt5_stubs typed-ast==1.4.1 typing-extensions==3.7.4.3 diff --git a/misc/requirements/requirements-pyinstaller.txt b/misc/requirements/requirements-pyinstaller.txt index 6de7b6fa8..06405d96a 100644 --- a/misc/requirements/requirements-pyinstaller.txt +++ b/misc/requirements/requirements-pyinstaller.txt @@ -2,4 +2,4 @@ altgraph==0.17 pyinstaller==4.0 -pyinstaller-hooks-contrib==2020.7 +pyinstaller-hooks-contrib==2020.8 diff --git a/misc/requirements/requirements-pyqt-5.15.txt b/misc/requirements/requirements-pyqt-5.15.txt index 93b31c40b..21745c814 100644 --- a/misc/requirements/requirements-pyqt-5.15.txt +++ b/misc/requirements/requirements-pyqt-5.15.txt @@ -1,5 +1,5 @@ # This file is automatically generated by scripts/dev/recompile_requirements.py -PyQt5==5.15.0 # rq.filter: < 6 +PyQt5==5.15.1 # rq.filter: < 6 PyQt5-sip==12.8.1 -PyQtWebEngine==5.15.0 # rq.filter: < 6 +PyQtWebEngine==5.15.1 # rq.filter: < 6 diff --git a/misc/requirements/requirements-pyqt.txt b/misc/requirements/requirements-pyqt.txt index 53a8b4af8..88a04230d 100644 --- a/misc/requirements/requirements-pyqt.txt +++ b/misc/requirements/requirements-pyqt.txt @@ -1,5 +1,5 @@ # This file is automatically generated by scripts/dev/recompile_requirements.py -PyQt5==5.15.0 +PyQt5==5.15.1 PyQt5-sip==12.8.1 -PyQtWebEngine==5.15.0 +PyQtWebEngine==5.15.1 diff --git a/misc/requirements/requirements-pyroma.txt b/misc/requirements/requirements-pyroma.txt index 6b131e155..2100af3cb 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.6.1 +Pygments==2.7.0 pyroma==2.6 diff --git a/misc/requirements/requirements-sphinx.txt b/misc/requirements/requirements-sphinx.txt index da6447009..3fd996cc2 100644 --- a/misc/requirements/requirements-sphinx.txt +++ b/misc/requirements/requirements-sphinx.txt @@ -10,7 +10,7 @@ imagesize==1.2.0 Jinja2==2.11.2 MarkupSafe==1.1.1 packaging==20.4 -Pygments==2.6.1 +Pygments==2.7.0 pyparsing==2.4.7 pytz==2020.1 requests==2.24.0 diff --git a/misc/requirements/requirements-tests.txt b/misc/requirements/requirements-tests.txt index fca663864..1edd6b55d 100644 --- a/misc/requirements/requirements-tests.txt +++ b/misc/requirements/requirements-tests.txt @@ -7,12 +7,12 @@ chardet==3.0.4 cheroot==8.4.5 click==7.1.2 # colorama==0.4.3 -coverage==5.2.1 +coverage==5.3 EasyProcess==0.3 Flask==1.1.2 glob2==0.7 hunter==3.2.2 -hypothesis==5.33.0 +hypothesis==5.35.1 idna==2.10 iniconfig==1.0.1 itsdangerous==1.1.0 @@ -23,15 +23,15 @@ manhole==1.6.0 # MarkupSafe==1.1.1 more-itertools==8.5.0 packaging==20.4 -parse==1.17.0 +parse==1.18.0 parse-type==0.5.2 pluggy==0.13.1 py==1.9.0 py-cpuinfo==7.0.0 -Pygments==2.6.1 +Pygments==2.7.0 pyparsing==2.4.7 -pytest==6.0.1 -pytest-bdd==3.4.0 +pytest==6.0.2 +pytest-bdd==4.0.1 pytest-benchmark==3.2.3 pytest-cov==2.10.1 pytest-instafail==0.4.2 diff --git a/requirements.txt b/requirements.txt index f54eecb94..706369e16 100644 --- a/requirements.txt +++ b/requirements.txt @@ -5,6 +5,6 @@ colorama==0.4.3 cssutils==1.0.2 Jinja2==2.11.2 MarkupSafe==1.1.1 -Pygments==2.6.1 +Pygments==2.7.0 pyPEG2==2.15.2 PyYAML==5.3.1 From 676ff395c79656391034f677671132a8a232c50b Mon Sep 17 00:00:00 2001 From: Florian Bruhin Date: Mon, 14 Sep 2020 09:25:31 +0200 Subject: [PATCH 23/50] Refactor/Fix TabWidget.update_tab_favicon The self.window().setWindowIcon(self.window().windowIcon()) line we've had before doesn't make any sense, as it does nothing. It's coming from 046a3dc159a04362b31f9411c82a1c8e75a70328 and probably was unintentional? Instead, we now always set the window favicon, even if tab favicons are disabled. --- qutebrowser/mainwindow/tabwidget.py | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/qutebrowser/mainwindow/tabwidget.py b/qutebrowser/mainwindow/tabwidget.py index 3f94f9901..0d3ca2f87 100644 --- a/qutebrowser/mainwindow/tabwidget.py +++ b/qutebrowser/mainwindow/tabwidget.py @@ -344,14 +344,11 @@ class TabWidget(QTabWidget): """Update favicon of the given tab.""" idx = self.indexOf(tab) - if tab.data.should_show_icon(): - self.setTabIcon(idx, tab.icon()) - if config.val.tabs.tabs_are_windows: - self.window().setWindowIcon(tab.icon()) - else: - self.setTabIcon(idx, QIcon()) - if config.val.tabs.tabs_are_windows: - self.window().setWindowIcon(self.window().windowIcon()) + icon = tab.icon() if tab.data.should_show_icon() else QIcon() + self.setTabIcon(idx, icon) + + if config.val.tabs.tabs_are_windows: + self.window().setWindowIcon(tab.icon()) def setTabIcon(self, idx: int, icon: QIcon) -> None: """Always show tab icons for pinned tabs in some circumstances.""" From 1e473c4bc01da1d7f1c4386d8b7b887e00fbf385 Mon Sep 17 00:00:00 2001 From: Florian Bruhin Date: Mon, 14 Sep 2020 09:33:28 +0200 Subject: [PATCH 24/50] Use TabWidget for icon handling in TabbedBrowser Avoids a bit of code duplication. --- qutebrowser/mainwindow/tabbedbrowser.py | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/qutebrowser/mainwindow/tabbedbrowser.py b/qutebrowser/mainwindow/tabbedbrowser.py index 0f9cafac0..76ca7c721 100644 --- a/qutebrowser/mainwindow/tabbedbrowser.py +++ b/qutebrowser/mainwindow/tabbedbrowser.py @@ -788,26 +788,21 @@ class TabbedBrowser(QWidget): if not self.widget.page_title(idx): self.widget.set_page_title(idx, url.toDisplayString()) - @pyqtSlot(browsertab.AbstractTab, QIcon) - def _on_icon_changed(self, tab, icon): + @pyqtSlot(browsertab.AbstractTab) + def _on_icon_changed(self, tab): """Set the icon of a tab. Slot for the iconChanged signal of any tab. Args: tab: The WebView where the title was changed. - icon: The new icon """ - if not tab.data.should_show_icon(): - return try: - idx = self._tab_index(tab) + self._tab_index(tab) except TabDeletedError: # We can get signals for tabs we already deleted... return - self.widget.setTabIcon(idx, icon) - if config.val.tabs.tabs_are_windows: - self.widget.window().setWindowIcon(icon) + self.widget.update_tab_favicon(tab) @pyqtSlot(usertypes.KeyMode) def on_mode_entered(self, mode): From 1943fa072ec3df5a87e18a23b0916f134c131016 Mon Sep 17 00:00:00 2001 From: Florian Bruhin Date: Mon, 14 Sep 2020 14:51:54 +0200 Subject: [PATCH 25/50] Handle tab pinned status in AbstractTab This replaces TabbedBrowser.set_tab_pinned() with AbstractTab.set_pinned() and thus also allows us to set a tab as pinned without having to know which TabbedBrowser it belongs to. This also fixes a bug when :undo is used after closing a tab and then setting tabs_are_windows to true - we asked "self" (i.e. the TabbedBrowser the tab was closed in) to restore the tab's pinned status, but the tab wasn't actually part of that TabbedBrowser as it was opened in a new window after the undo. --- qutebrowser/browser/browsertab.py | 6 ++++++ qutebrowser/browser/commands.py | 5 +++-- qutebrowser/mainwindow/tabbedbrowser.py | 11 +++++++++-- qutebrowser/mainwindow/tabwidget.py | 14 +------------- qutebrowser/misc/sessions.py | 3 +-- tests/end2end/features/tabs.feature | 9 +++++++++ tests/unit/mainwindow/test_tabwidget.py | 5 +++-- 7 files changed, 32 insertions(+), 21 deletions(-) diff --git a/qutebrowser/browser/browsertab.py b/qutebrowser/browser/browsertab.py index b7b2f3d91..f7d951b33 100644 --- a/qutebrowser/browser/browsertab.py +++ b/qutebrowser/browser/browsertab.py @@ -897,6 +897,8 @@ class AbstractTab(QWidget): icon_changed = pyqtSignal(QIcon) #: Signal emitted when a page's title changed (new title as str) title_changed = pyqtSignal(str) + #: Signal emitted when this tab was pinned/unpinned (new pinned state as bool) + pinned_changed = pyqtSignal(bool) #: Signal emitted when a new tab should be opened (url as QUrl) new_tab_requested = pyqtSignal(QUrl) #: Signal emitted when a page's URL changed (url as QUrl) @@ -1191,6 +1193,10 @@ class AbstractTab(QWidget): def set_html(self, html: str, base_url: QUrl = QUrl()) -> None: raise NotImplementedError + def set_pinned(self, pinned: bool) -> None: + self.data.pinned = pinned + self.pinned_changed.emit(pinned) + def __repr__(self) -> str: try: qurl = self.url() diff --git a/qutebrowser/browser/commands.py b/qutebrowser/browser/commands.py index ff18b5408..40bb45dd0 100644 --- a/qutebrowser/browser/commands.py +++ b/qutebrowser/browser/commands.py @@ -278,7 +278,7 @@ class CommandDispatcher: return to_pin = not tab.data.pinned - self._tabbed_browser.widget.set_tab_pinned(tab, to_pin) + tab.set_pinned(to_pin) @cmdutils.register(instance='command-dispatcher', name='open', maxsplit=0, scope='window') @@ -421,7 +421,8 @@ class CommandDispatcher: newtab.data.keep_icon = True newtab.history.private_api.deserialize(history) newtab.zoom.set_factor(curtab.zoom.factor()) - new_tabbed_browser.widget.set_tab_pinned(newtab, curtab.data.pinned) + + newtab.set_pinned(curtab.data.pinned) return newtab @cmdutils.register(instance='command-dispatcher', scope='window', diff --git a/qutebrowser/mainwindow/tabbedbrowser.py b/qutebrowser/mainwindow/tabbedbrowser.py index 76ca7c721..707527c81 100644 --- a/qutebrowser/mainwindow/tabbedbrowser.py +++ b/qutebrowser/mainwindow/tabbedbrowser.py @@ -28,7 +28,6 @@ import datetime import attr from PyQt5.QtWidgets import QSizePolicy, QWidget, QApplication from PyQt5.QtCore import pyqtSignal, pyqtSlot, QTimer, QUrl -from PyQt5.QtGui import QIcon from qutebrowser.config import config from qutebrowser.keyinput import modeman @@ -351,6 +350,8 @@ class TabbedBrowser(QWidget): functools.partial(self._on_title_changed, tab)) tab.icon_changed.connect( functools.partial(self._on_icon_changed, tab)) + tab.pinned_changed.connect( + functools.partial(self._on_pinned_changed, tab)) tab.load_progress.connect( functools.partial(self._on_load_progress, tab)) tab.load_finished.connect( @@ -530,7 +531,7 @@ class TabbedBrowser(QWidget): newtab = self.tabopen(background=False, idx=entry.index) newtab.history.private_api.deserialize(entry.history) - self.widget.set_tab_pinned(newtab, entry.pinned) + newtab.set_pinned(entry.pinned) @pyqtSlot('QUrl', bool) def load_url(self, url, newtab): @@ -917,6 +918,12 @@ class TabbedBrowser(QWidget): self._update_window_title('scroll_pos') self.widget.update_tab_title(idx, 'scroll_pos') + def _on_pinned_changed(self, tab): + """Update the tab's pinned status.""" + idx = self.widget.indexOf(tab) + self.widget.update_tab_favicon(tab) + self.widget.update_tab_title(idx) + def _on_audio_changed(self, tab, _muted): """Update audio field in tab when mute or recentlyAudible changed.""" try: diff --git a/qutebrowser/mainwindow/tabwidget.py b/qutebrowser/mainwindow/tabwidget.py index 0d3ca2f87..fdefa075e 100644 --- a/qutebrowser/mainwindow/tabwidget.py +++ b/qutebrowser/mainwindow/tabwidget.py @@ -99,19 +99,6 @@ class TabWidget(QTabWidget): bar.set_tab_data(idx, 'indicator-color', color) bar.update(bar.tabRect(idx)) - def set_tab_pinned(self, tab: QWidget, - pinned: bool) -> None: - """Set the tab status as pinned. - - Args: - tab: The tab to pin - pinned: Pinned tab state to set. - """ - idx = self.indexOf(tab) - tab.data.pinned = pinned - self.update_tab_favicon(tab) - self.update_tab_title(idx) - def tab_indicator_color(self, idx): """Get the tab indicator color for the given index.""" return self.tabBar().tab_indicator_color(idx) @@ -139,6 +126,7 @@ class TabWidget(QTabWidget): field: A field name which was updated. If given, the title is only set if the given field is in the template. """ + assert idx != -1 tab = self.widget(idx) if tab.data.pinned: fmt = config.cache['tabs.title.format_pinned'] diff --git a/qutebrowser/misc/sessions.py b/qutebrowser/misc/sessions.py index dcdc0821b..0ebb415ac 100644 --- a/qutebrowser/misc/sessions.py +++ b/qutebrowser/misc/sessions.py @@ -470,8 +470,7 @@ class SessionManager(QObject): if tab.get('active', False): tab_to_focus = i if new_tab.data.pinned: - tabbed_browser.widget.set_tab_pinned(new_tab, - new_tab.data.pinned) + new_tab.set_pinned(True) if tab_to_focus is not None: tabbed_browser.widget.setCurrentIndex(tab_to_focus) if win.get('active', False): diff --git a/tests/end2end/features/tabs.feature b/tests/end2end/features/tabs.feature index 4b645d554..2d3dfe1d1 100644 --- a/tests/end2end/features/tabs.feature +++ b/tests/end2end/features/tabs.feature @@ -1613,3 +1613,12 @@ Feature: Tab management And I open data/hello.txt in a new tab And I run :fake-key -g hello-world Then the message "hello-world" should be shown + + Scenario: Undo after changing tabs_are_windows + When I open data/hello.txt + And I open data/hello.txt in a new tab + And I set tabs.tabs_are_windows to true + And I run :tab-close + And I run :undo + And I run :message-info "Still alive!" + Then the message "Still alive!" should be shown diff --git a/tests/unit/mainwindow/test_tabwidget.py b/tests/unit/mainwindow/test_tabwidget.py index 659aac7ec..b271c18ab 100644 --- a/tests/unit/mainwindow/test_tabwidget.py +++ b/tests/unit/mainwindow/test_tabwidget.py @@ -94,8 +94,9 @@ class TestTabWidget: config_stub.val.tabs.position = "left" pinned_num = [1, num_tabs - 1] - for tab in pinned_num: - widget.set_tab_pinned(widget.widget(tab), True) + for num in pinned_num: + tab = widget.widget(num) + tab.set_pinned(True) first_size = widget.tabBar().tabSizeHint(0) first_size_min = widget.tabBar().minimumTabSizeHint(0) From 43de7adbb3cbf088ea1ac7abaff416b3833fd011 Mon Sep 17 00:00:00 2001 From: Florian Bruhin Date: Mon, 14 Sep 2020 14:55:21 +0200 Subject: [PATCH 26/50] Update changelog --- doc/changelog.asciidoc | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/doc/changelog.asciidoc b/doc/changelog.asciidoc index a5c4df415..a0eb8cddf 100644 --- a/doc/changelog.asciidoc +++ b/doc/changelog.asciidoc @@ -59,6 +59,8 @@ Changed - `navigate prev` (`[[`) and `navigate next` (`]]`) now recognize links with `nav-prev` and `nav-next` classes, such as those used by the Hugo static site generator. +- When `tabs.favicons` is disabled but `tabs.tabs_are_windows` is set, the + window icon is still set to the page's favicon now. Added ~~~~~ @@ -123,6 +125,8 @@ Fixed - The `qute-pass` userscript now works correctly with newer `gopass` versions again, instead of copying a deprecation notice as password. +- When `:undo` to re-open a tab but `tabs.tabs_are_windows` was set between + closing and undoing the close, qutebrowser crashed. This is now fixed. v1.13.1 (2020-07-17) -------------------- From da3d793fde6f1eb64b8c2591f0f2b4c0a58a58ba Mon Sep 17 00:00:00 2001 From: Florian Bruhin Date: Mon, 14 Sep 2020 18:42:43 +0200 Subject: [PATCH 27/50] mypy: Remove now unneeded ignores --- qutebrowser/browser/webkit/webview.py | 4 ++-- qutebrowser/mainwindow/tabbedbrowser.py | 3 +-- qutebrowser/misc/crashdialog.py | 2 +- 3 files changed, 4 insertions(+), 5 deletions(-) diff --git a/qutebrowser/browser/webkit/webview.py b/qutebrowser/browser/webkit/webview.py index 6706848dd..980ae78d0 100644 --- a/qutebrowser/browser/webkit/webview.py +++ b/qutebrowser/browser/webkit/webview.py @@ -181,9 +181,9 @@ class WebView(QWebView): This is not needed for QtWebEngine, so it's in here. """ menu = self.page().createStandardContextMenu() - self.shutting_down.connect(menu.close) # type: ignore[arg-type] + self.shutting_down.connect(menu.close) mm = modeman.instance(self.win_id) - mm.entered.connect(menu.close) # type: ignore[arg-type] + mm.entered.connect(menu.close) menu.exec_(e.globalPos()) def showEvent(self, e): diff --git a/qutebrowser/mainwindow/tabbedbrowser.py b/qutebrowser/mainwindow/tabbedbrowser.py index 707527c81..57a9ae018 100644 --- a/qutebrowser/mainwindow/tabbedbrowser.py +++ b/qutebrowser/mainwindow/tabbedbrowser.py @@ -211,8 +211,7 @@ class TabbedBrowser(QWidget): self._tab_insert_idx_right = -1 self.is_shutting_down = False self.widget.tabCloseRequested.connect(self.on_tab_close_requested) - self.widget.new_tab_requested.connect( - self.tabopen) # type: ignore[arg-type] + self.widget.new_tab_requested.connect(self.tabopen) self.widget.currentChanged.connect(self._on_current_changed) self.cur_fullscreen_requested.connect(self.widget.tabBar().maybe_hide) self.widget.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding) diff --git a/qutebrowser/misc/crashdialog.py b/qutebrowser/misc/crashdialog.py index 4387e479a..850883bf4 100644 --- a/qutebrowser/misc/crashdialog.py +++ b/qutebrowser/misc/crashdialog.py @@ -635,7 +635,7 @@ class ReportErrorDialog(QDialog): hbox = QHBoxLayout() hbox.addStretch() btn = QPushButton("Close") - btn.clicked.connect(self.close) # type: ignore[arg-type] + btn.clicked.connect(self.close) hbox.addWidget(btn) vbox.addLayout(hbox) From f59d39b0cef026c6656e6a000358b6d6a5152848 Mon Sep 17 00:00:00 2001 From: Florian Bruhin Date: Tue, 15 Sep 2020 11:48:40 +0200 Subject: [PATCH 28/50] Add pip marker for hypothesis and Python 3.5 --- misc/requirements/requirements-tests.txt | 3 ++- misc/requirements/requirements-tests.txt-raw | 3 +++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/misc/requirements/requirements-tests.txt b/misc/requirements/requirements-tests.txt index 1edd6b55d..8aff56513 100644 --- a/misc/requirements/requirements-tests.txt +++ b/misc/requirements/requirements-tests.txt @@ -12,7 +12,7 @@ EasyProcess==0.3 Flask==1.1.2 glob2==0.7 hunter==3.2.2 -hypothesis==5.35.1 +hypothesis==5.35.2 ; python_version>="3.6" idna==2.10 iniconfig==1.0.1 itsdangerous==1.1.0 @@ -53,3 +53,4 @@ vulture==2.1 ; python_version>="3.6" Werkzeug==1.0.1 jaraco.functools==2.0; python_version<"3.6" vulture==1.6; python_version<"3.6" +hypothesis<5.34.0; python_version<"3.6" diff --git a/misc/requirements/requirements-tests.txt-raw b/misc/requirements/requirements-tests.txt-raw index f063a3512..016caca0b 100644 --- a/misc/requirements/requirements-tests.txt-raw +++ b/misc/requirements/requirements-tests.txt-raw @@ -35,4 +35,7 @@ tldextract #@ markers: vulture python_version>="3.6" #@ add: vulture==1.6; python_version<"3.6" +#@ markers: hypothesis python_version>="3.6" +#@ add: hypothesis<5.34.0; python_version<"3.6" + #@ ignore: Jinja2, MarkupSafe, colorama From f11a0864afe42f8806c8e955e8d1b5ce320afd2b Mon Sep 17 00:00:00 2001 From: Florian Bruhin Date: Tue, 15 Sep 2020 11:55:39 +0200 Subject: [PATCH 29/50] Add xdist to test requirements --- misc/requirements/requirements-tests.txt | 4 ++++ misc/requirements/requirements-tests.txt-raw | 2 ++ scripts/dev/recompile_requirements.py | 4 ++++ 3 files changed, 10 insertions(+) diff --git a/misc/requirements/requirements-tests.txt b/misc/requirements/requirements-tests.txt index 8aff56513..af751739f 100644 --- a/misc/requirements/requirements-tests.txt +++ b/misc/requirements/requirements-tests.txt @@ -1,5 +1,6 @@ # This file is automatically generated by scripts/dev/recompile_requirements.py +apipkg==1.5 attrs==20.2.0 beautifulsoup4==4.9.1 certifi==2020.6.20 @@ -9,6 +10,7 @@ click==7.1.2 # colorama==0.4.3 coverage==5.3 EasyProcess==0.3 +execnet==1.7.1 Flask==1.1.2 glob2==0.7 hunter==3.2.2 @@ -34,11 +36,13 @@ pytest==6.0.2 pytest-bdd==4.0.1 pytest-benchmark==3.2.3 pytest-cov==2.10.1 +pytest-forked==1.3.0 pytest-instafail==0.4.2 pytest-mock==3.3.1 pytest-qt==3.3.0 pytest-repeat==0.8.0 pytest-rerunfailures==9.1 +pytest-xdist==2.1.0 pytest-xvfb==2.0.0 PyVirtualDisplay==1.3.2 requests==2.24.0 diff --git a/misc/requirements/requirements-tests.txt-raw b/misc/requirements/requirements-tests.txt-raw index 016caca0b..73f58461c 100644 --- a/misc/requirements/requirements-tests.txt-raw +++ b/misc/requirements/requirements-tests.txt-raw @@ -25,6 +25,8 @@ pytest-cov # To avoid windows from popping up pytest-xvfb PyVirtualDisplay +# To run on multiple cores with -n +pytest-xdist # Needed to test misc/userscripts/qute-lastpass tldextract diff --git a/scripts/dev/recompile_requirements.py b/scripts/dev/recompile_requirements.py index 94c128490..e165522a7 100644 --- a/scripts/dev/recompile_requirements.py +++ b/scripts/dev/recompile_requirements.py @@ -43,6 +43,10 @@ CHANGELOG_URLS = { 'pylint': 'http://pylint.pycqa.org/en/latest/whatsnew/changelog.html', 'setuptools': 'https://github.com/pypa/setuptools/blob/master/CHANGES.rst', 'pytest-cov': 'https://github.com/pytest-dev/pytest-cov/blob/master/CHANGELOG.rst', + 'pytest-xdist': 'https://github.com/pytest-dev/pytest-xdist/blob/master/CHANGELOG.rst', + 'pytest-forked': 'https://github.com/pytest-dev/pytest-forked/blob/master/CHANGELOG', + 'execnet': 'https://execnet.readthedocs.io/en/latest/changelog.html', + 'apipkg': 'https://github.com/pytest-dev/apipkg/blob/master/CHANGELOG', 'pytest-rerunfailures': 'https://github.com/pytest-dev/pytest-rerunfailures/blob/master/CHANGES.rst', 'requests': 'https://github.com/psf/requests/blob/master/HISTORY.md', 'requests-file': 'https://github.com/dashea/requests-file/blob/master/CHANGES.rst', From 966873ae03eb7c6a1ebbdfa3456524a6a3b6288b Mon Sep 17 00:00:00 2001 From: Florian Bruhin Date: Tue, 15 Sep 2020 13:25:15 +0200 Subject: [PATCH 30/50] Add missing changelog URLs --- scripts/dev/recompile_requirements.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/scripts/dev/recompile_requirements.py b/scripts/dev/recompile_requirements.py index e165522a7..c1d3f8c36 100644 --- a/scripts/dev/recompile_requirements.py +++ b/scripts/dev/recompile_requirements.py @@ -106,10 +106,12 @@ CHANGELOG_URLS = { 'pep517': 'https://github.com/pypa/pep517/commits/master', 'cryptography': 'https://cryptography.io/en/latest/changelog/', 'toml': 'https://github.com/uiri/toml/releases', - 'pyqt': 'https://www.riverbankcomputing.com/news', + 'PyQt5': 'https://www.riverbankcomputing.com/news', + 'PyQtWebEngine': 'https://www.riverbankcomputing.com/news', 'PyQt-builder': 'https://www.riverbankcomputing.com/news', 'PyQt5-sip': 'https://www.riverbankcomputing.com/news', 'sip': 'https://www.riverbankcomputing.com/news', + 'Pygments': 'https://pygments.org/docs/changelog/', 'vulture': 'https://github.com/jendrikseipp/vulture/blob/master/CHANGELOG.md', 'distlib': 'https://bitbucket.org/pypa/distlib/src/master/CHANGES.rst', 'py-cpuinfo': 'https://github.com/workhorsy/py-cpuinfo/blob/master/ChangeLog', From b55675ad4afe12be7e4a4f39960bb58818c502f8 Mon Sep 17 00:00:00 2001 From: Florian Bruhin Date: Tue, 15 Sep 2020 13:56:00 +0200 Subject: [PATCH 31/50] Work around PyQt 5.15.1 waitSignals issues This should be reverted with PyQt 5.15.2. See #5719 --- .../webkit/network/test_networkreply.py | 7 +++-- tests/unit/completion/test_completionmodel.py | 4 +-- tests/unit/config/test_config.py | 7 +++-- tests/unit/keyinput/test_basekeyparser.py | 4 ++- tests/unit/misc/test_guiprocess.py | 29 +++++++++---------- tests/unit/misc/test_ipc.py | 8 ++--- tests/unit/utils/usertypes/test_question.py | 14 +++++---- 7 files changed, 39 insertions(+), 34 deletions(-) diff --git a/tests/unit/browser/webkit/network/test_networkreply.py b/tests/unit/browser/webkit/network/test_networkreply.py index e1c9d04f8..0aa3943e7 100644 --- a/tests/unit/browser/webkit/network/test_networkreply.py +++ b/tests/unit/browser/webkit/network/test_networkreply.py @@ -52,8 +52,9 @@ class TestFixedDataNetworkReply: b'Hello World! This is a test.']) def test_data(self, qtbot, req, data): reply = networkreply.FixedDataNetworkReply(req, data, 'test/foo') - with qtbot.waitSignals([reply.metaDataChanged, reply.readyRead, - reply.finished], order='strict'): + with qtbot.waitSignal(reply.metaDataChanged), \ + qtbot.waitSignal(reply.readyRead), \ + qtbot.waitSignal(reply.finished): pass assert reply.bytesAvailable() == len(data) @@ -78,7 +79,7 @@ def test_error_network_reply(qtbot, req): reply = networkreply.ErrorNetworkReply( req, "This is an error", QNetworkReply.UnknownNetworkError) - with qtbot.waitSignals([reply.error, reply.finished], order='strict'): + with qtbot.waitSignal(reply.error), qtbot.waitSignal(reply.finished): pass reply.abort() # shouldn't do anything diff --git a/tests/unit/completion/test_completionmodel.py b/tests/unit/completion/test_completionmodel.py index 98e70dc01..1fc0b4d73 100644 --- a/tests/unit/completion/test_completionmodel.py +++ b/tests/unit/completion/test_completionmodel.py @@ -77,8 +77,8 @@ def test_set_pattern(pat, qtbot): for c in cats: c.set_pattern = mock.Mock(spec=[]) model.add_category(c) - with qtbot.waitSignals([model.layoutAboutToBeChanged, model.layoutChanged], - order='strict'): + with qtbot.waitSignal(model.layoutAboutToBeChanged), \ + qtbot.waitSignal(model.layoutChanged): model.set_pattern(pat) for c in cats: c.set_pattern.assert_called_with(pat) diff --git a/tests/unit/config/test_config.py b/tests/unit/config/test_config.py index b30ab4bee..588e4a5cf 100644 --- a/tests/unit/config/test_config.py +++ b/tests/unit/config/test_config.py @@ -432,11 +432,12 @@ class TestConfig: assert conf.get_obj(name1) == 'never' assert conf.get_obj(name2) is True - with qtbot.waitSignals([conf.changed, conf.changed]) as blocker: + with qtbot.waitSignal(conf.changed), qtbot.waitSignal(conf.changed): conf.clear(save_yaml=save_yaml) - options = {e.args[0] for e in blocker.all_signals_and_args} - assert options == {name1, name2} + # Doesn't work with PyQt 5.15.1 workaround + # options = {blocker1.args[0], blocker2.args[0]} + # assert options == {name1, name2} if save_yaml: assert yaml_value(name1) is usertypes.UNSET diff --git a/tests/unit/keyinput/test_basekeyparser.py b/tests/unit/keyinput/test_basekeyparser.py index f8caaf1af..4e474b4f3 100644 --- a/tests/unit/keyinput/test_basekeyparser.py +++ b/tests/unit/keyinput/test_basekeyparser.py @@ -21,7 +21,7 @@ from unittest import mock -from PyQt5.QtCore import Qt +from PyQt5.QtCore import Qt, PYQT_VERSION import pytest from qutebrowser.keyinput import basekeyparser, keyutils @@ -309,6 +309,8 @@ class TestCount: # https://github.com/qutebrowser/qutebrowser/issues/3743 handle_text(prompt_keyparser, Qt.Key_twosuperior, Qt.Key_B, Qt.Key_A) + @pytest.mark.skipif(PYQT_VERSION == 0x050F01, + reason='waitSignals is broken in PyQt 5.15.1') def test_count_keystring_update(self, qtbot, handle_text, prompt_keyparser): """Make sure the keystring is updated correctly when entering count.""" diff --git a/tests/unit/misc/test_guiprocess.py b/tests/unit/misc/test_guiprocess.py index 4ed19f64e..7e71478ce 100644 --- a/tests/unit/misc/test_guiprocess.py +++ b/tests/unit/misc/test_guiprocess.py @@ -54,8 +54,8 @@ def fake_proc(monkeypatch, stubs): def test_start(proc, qtbot, message_mock, py_proc): """Test simply starting a process.""" - with qtbot.waitSignals([proc.started, proc.finished], timeout=10000, - order='strict'): + with qtbot.waitSignal(proc.started, timeout=10000), \ + qtbot.waitSignal(proc.finished, timeout=10000): argv = py_proc("import sys; print('test'); sys.exit(0)") proc.start(*argv) @@ -70,8 +70,8 @@ def test_start_verbose(proc, qtbot, message_mock, py_proc): """Test starting a process verbosely.""" proc.verbose = True - with qtbot.waitSignals([proc.started, proc.finished], timeout=10000, - order='strict'): + with qtbot.waitSignal(proc.started, timeout=10000), \ + qtbot.waitSignal(proc.finished, timeout=10000): argv = py_proc("import sys; print('test'); sys.exit(0)") proc.start(*argv) @@ -99,9 +99,8 @@ def test_start_output_message(proc, qtbot, caplog, message_mock, py_proc, code.append("sys.exit(0)") with caplog.at_level(logging.ERROR, 'message'): - with qtbot.waitSignals([proc.started, proc.finished], - timeout=10000, - order='strict'): + with qtbot.waitSignal(proc.started, timeout=10000), \ + qtbot.waitSignal(proc.finished, timeout=10000): argv = py_proc(';'.join(code)) proc.start(*argv) @@ -147,8 +146,8 @@ def test_start_env(monkeypatch, qtbot, py_proc): sys.exit(0) """) - with qtbot.waitSignals([proc.started, proc.finished], timeout=10000, - order='strict'): + with qtbot.waitSignal(proc.started, timeout=10000), \ + qtbot.waitSignal(proc.finished, timeout=10000): proc.start(*argv) data = qutescheme.spawn_output @@ -187,12 +186,12 @@ def test_double_start(qtbot, proc, py_proc): def test_double_start_finished(qtbot, proc, py_proc): """Test starting a GUIProcess twice (with the first call finished).""" - with qtbot.waitSignals([proc.started, proc.finished], timeout=10000, - order='strict'): + with qtbot.waitSignal(proc.started, timeout=10000), \ + qtbot.waitSignal(proc.finished, timeout=10000): argv = py_proc("import sys; sys.exit(0)") proc.start(*argv) - with qtbot.waitSignals([proc.started, proc.finished], timeout=10000, - order='strict'): + with qtbot.waitSignal(proc.started, timeout=10000), \ + qtbot.waitSignal(proc.finished, timeout=10000): argv = py_proc("import sys; sys.exit(0)") proc.start(*argv) @@ -267,8 +266,8 @@ def test_exit_successful_output(qtbot, proc, py_proc, stream): def test_stdout_not_decodable(proc, qtbot, message_mock, py_proc): """Test handling malformed utf-8 in stdout.""" - with qtbot.waitSignals([proc.started, proc.finished], timeout=10000, - order='strict'): + with qtbot.waitSignal(proc.started, timeout=10000), \ + qtbot.waitSignal(proc.finished, timeout=10000): argv = py_proc(r""" import sys # Using \x81 because it's invalid in UTF-8 and CP1252 diff --git a/tests/unit/misc/test_ipc.py b/tests/unit/misc/test_ipc.py index dd4a5cc40..28c570586 100644 --- a/tests/unit/misc/test_ipc.py +++ b/tests/unit/misc/test_ipc.py @@ -493,10 +493,10 @@ NEW_VERSION = str(ipc.PROTOCOL_VERSION + 1).encode('utf-8') (b'{"args": [], "target_arg": null}\n', 'invalid version'), ]) def test_invalid_data(qtbot, ipc_server, connected_socket, caplog, data, msg): - signals = [ipc_server.got_invalid_data, connected_socket.disconnected] with caplog.at_level(logging.ERROR): with qtbot.assertNotEmitted(ipc_server.got_args): - with qtbot.waitSignals(signals, order='strict'): + with qtbot.waitSignal(ipc_server.got_invalid_data), \ + qtbot.waitSignal(connected_socket.disconnected): connected_socket.write(data) invalid_msg = 'Ignoring invalid IPC data from socket ' @@ -514,8 +514,8 @@ def test_multiline(qtbot, ipc_server, connected_socket): version=ipc.PROTOCOL_VERSION)) with qtbot.assertNotEmitted(ipc_server.got_invalid_data): - with qtbot.waitSignals([ipc_server.got_args, ipc_server.got_args], - order='strict'): + with qtbot.waitSignal(ipc_server.got_args), \ + qtbot.waitSignal(ipc_server.got_args): connected_socket.write(data.encode('utf-8')) assert len(spy) == 2 diff --git a/tests/unit/utils/usertypes/test_question.py b/tests/unit/utils/usertypes/test_question.py index 59c3f7d43..014ef7f0c 100644 --- a/tests/unit/utils/usertypes/test_question.py +++ b/tests/unit/utils/usertypes/test_question.py @@ -53,23 +53,25 @@ def test_done(mode, answer, signal_names, question, qtbot): question.mode = mode question.answer = answer signals = [getattr(question, name) for name in signal_names] - with qtbot.waitSignals(signals, order='strict'): - question.done() + blockers = [qtbot.waitSignal(signal) for signal in signals] + + question.done() + for blocker in blockers: + blocker.wait() + assert not question.is_aborted def test_cancel(question, qtbot): """Test Question.cancel().""" - with qtbot.waitSignals([question.cancelled, question.completed], - order='strict'): + with qtbot.waitSignal(question.cancelled), qtbot.waitSignal(question.completed): question.cancel() assert not question.is_aborted def test_abort(question, qtbot): """Test Question.abort().""" - with qtbot.waitSignals([question.aborted, question.completed], - order='strict'): + with qtbot.waitSignal(question.aborted), qtbot.waitSignal(question.completed): question.abort() assert question.is_aborted From 55fb6a17fc130472da85ec861edc5f667f70b959 Mon Sep 17 00:00:00 2001 From: Florian Bruhin Date: Tue, 15 Sep 2020 15:06:34 +0200 Subject: [PATCH 32/50] Update sessions warning --- qutebrowser/html/warning-sessions.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/qutebrowser/html/warning-sessions.html b/qutebrowser/html/warning-sessions.html index fadc9908d..6f447483f 100644 --- a/qutebrowser/html/warning-sessions.html +++ b/qutebrowser/html/warning-sessions.html @@ -9,7 +9,7 @@ qute://warning/sessions to show it again at a later time.

Since Qt doesn't provide an API to load the history of a tab, qutebrowser relies on a reverse-engineered binary serialization format to load tab history from session files. With Qt 5.15, unfortunately that format changed (due to the underlying Chromium upgrade), in a way which makes it impossible for qutebrowser to load tab history from existing session data.

-

At the time of writing (April 2020), a new session format which stores part of the needed binary data in saved sessions is in development and is expected to be released with qutebrowser v1.14.0.

+

At the time of writing (September 2020), a new session format which stores part of the needed binary data in saved sessions is in development and is expected to be released with qutebrowser v1.15.0.

As a stop-gap measure:

From afb3e8e01b31319c66c4e666b8a3b1d8ba55db24 Mon Sep 17 00:00:00 2001 From: Florian Bruhin Date: Tue, 15 Sep 2020 16:30:54 +0200 Subject: [PATCH 33/50] Improve jseval --world descriptions --- doc/help/commands.asciidoc | 11 ++++++++++- qutebrowser/browser/commands.py | 10 +++++++++- 2 files changed, 19 insertions(+), 2 deletions(-) diff --git a/doc/help/commands.asciidoc b/doc/help/commands.asciidoc index 6c9b7f1a8..b9e525075 100644 --- a/doc/help/commands.asciidoc +++ b/doc/help/commands.asciidoc @@ -765,7 +765,16 @@ Evaluate a JavaScript string. * +*-u*+, +*--url*+: Interpret js-code as a `javascript:...` URL. * +*-q*+, +*--quiet*+: Don't show resulting JS object. -* +*-w*+, +*--world*+: Ignored on QtWebKit. On QtWebEngine, a world ID or name to run the snippet in. +* +*-w*+, +*--world*+: Ignored on QtWebKit. On QtWebEngine, a world ID or name to run the snippet in. Predefined world names are: + + + - `main` (same world as the web page's JavaScript and + Greasemonkey, unless overridden via `@qute-js-world`) + - `application` (used for internal qutebrowser JS code, + should not be used via `:jseval` unless you know what + you're doing) + - `user` (currently unused) + - `jseval` (used for this command by default) ==== note diff --git a/qutebrowser/browser/commands.py b/qutebrowser/browser/commands.py index 40bb45dd0..13f57e7dc 100644 --- a/qutebrowser/browser/commands.py +++ b/qutebrowser/browser/commands.py @@ -1664,7 +1664,15 @@ class CommandDispatcher: url: Interpret js-code as a `javascript:...` URL. quiet: Don't show resulting JS object. world: Ignored on QtWebKit. On QtWebEngine, a world ID or name to - run the snippet in. + run the snippet in. Predefined world names are: + + - `main` (same world as the web page's JavaScript and + Greasemonkey, unless overridden via `@qute-js-world`) + - `application` (used for internal qutebrowser JS code, + should not be used via `:jseval` unless you know what + you're doing) + - `user` (currently unused) + - `jseval` (used for this command by default) """ cmdutils.check_exclusive((file, url), 'fu') From 61b307ec03c74d19eed25d0c021f3595e5f668da Mon Sep 17 00:00:00 2001 From: Florian Bruhin Date: Wed, 16 Sep 2020 16:11:44 +0200 Subject: [PATCH 34/50] Update changelog --- doc/changelog.asciidoc | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/doc/changelog.asciidoc b/doc/changelog.asciidoc index a0eb8cddf..d02a55a5c 100644 --- a/doc/changelog.asciidoc +++ b/doc/changelog.asciidoc @@ -36,8 +36,7 @@ Changed partially transparent qutebrowser window on a setup which supports doing so. - If QtWebEngine is compiled with PipeWire support and libpipewire is installed, qutebrowser will now support screen sharing on Wayland. Note that - QtWebEngine 5.15.1 (planned for August 2020) is needed, though the Archlinux - qt5-webengine package backports the patch. + QtWebEngine 5.15.1 is needed. - When `:undo` is used with a count, it now reopens the count-th to last tab instead of the last one. The depth can instead be passed as an argument, which is also completed. From 51428a03dc7058dee10e68481b35135cc43ef4d2 Mon Sep 17 00:00:00 2001 From: Florian Bruhin Date: Wed, 16 Sep 2020 16:30:53 +0200 Subject: [PATCH 35/50] Add tox environment for build-release --- tox.ini | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/tox.ini b/tox.ini index 369d4afe6..a25374672 100644 --- a/tox.ini +++ b/tox.ini @@ -224,3 +224,17 @@ deps = -r{toxinidir}/misc/requirements/requirements-sphinx.txt commands = {envpython} -m sphinx -jauto -W --color {posargs} {toxinidir}/doc/extapi/ {toxinidir}/doc/extapi/_build/ + +[testenv:build-release] +basepython = {env:PYTHON:python3} +pip_version = pip +passenv = * +usedevelop = true +deps = + -r{toxinidir}/requirements.txt + -r{toxinidir}/misc/requirements/requirements-tox.txt + -r{toxinidir}/misc/requirements/requirements-pyqt.txt + -r{toxinidir}/misc/requirements/requirements-dev.txt + -r{toxinidir}/misc/requirements/requirements-pyinstaller.txt +commands = + {envpython} {toxinidir}/scripts/dev/build_release.py {posargs} From 1c950a2ce6803414896fc6df927468877ac9adce Mon Sep 17 00:00:00 2001 From: Florian Bruhin Date: Wed, 16 Sep 2020 16:31:54 +0200 Subject: [PATCH 36/50] Makefile: Filter out misc/userscripts/__pycache__ --- misc/Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/misc/Makefile b/misc/Makefile index 985541fdd..b916a20d5 100644 --- a/misc/Makefile +++ b/misc/Makefile @@ -29,7 +29,7 @@ install: man install -Dm644 icons/qutebrowser.svg \ "$(DESTDIR)$(DATADIR)/icons/hicolor/scalable/apps/qutebrowser.svg" install -Dm755 -t "$(DESTDIR)$(DATADIR)/qutebrowser/userscripts/" \ - $(wildcard misc/userscripts/*) + $(filter-out misc/userscripts/__pycache__,$(wildcard misc/userscripts/*)) install -Dm755 -t "$(DESTDIR)$(DATADIR)/qutebrowser/scripts/" \ $(filter-out scripts/__init__.py scripts/__pycache__ scripts/dev \ scripts/testbrowser scripts/asciidoc2html.py scripts/setupcommon.py \ From 5663bd24f5ee37ac0ac974ada737eb1409921e97 Mon Sep 17 00:00:00 2001 From: Florian Bruhin Date: Wed, 16 Sep 2020 16:57:31 +0200 Subject: [PATCH 37/50] Fix tox environment name for macOS This was a regression introduced in 76eed0aa9c4f01f8282a9f305b8fde64588d26c4. --- scripts/dev/build_release.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/dev/build_release.py b/scripts/dev/build_release.py index ee0ac2c53..53c17843e 100755 --- a/scripts/dev/build_release.py +++ b/scripts/dev/build_release.py @@ -201,7 +201,7 @@ def build_mac(): utils.print_title("Updating 3rdparty content") update_3rdparty.run(ace=False, pdfjs=True, fancy_dmg=False) utils.print_title("Building .app via pyinstaller") - call_tox('pyinstaller', '-r') + call_tox('pyinstaller-64', '-r') utils.print_title("Patching .app") patch_mac_app() utils.print_title("Building .dmg") From 28bd35ed58f85f23570a9901094d550baa6bff1c Mon Sep 17 00:00:00 2001 From: Florian Bruhin Date: Wed, 16 Sep 2020 21:04:40 +0200 Subject: [PATCH 38/50] Simplify asciidoc arg --- doc/changelog.asciidoc | 6 ++++++ scripts/asciidoc2html.py | 21 ++++++++++++++------- scripts/dev/build_release.py | 17 ++++++++++------- 3 files changed, 30 insertions(+), 14 deletions(-) diff --git a/doc/changelog.asciidoc b/doc/changelog.asciidoc index d02a55a5c..449f51f4b 100644 --- a/doc/changelog.asciidoc +++ b/doc/changelog.asciidoc @@ -60,6 +60,12 @@ Changed generator. - When `tabs.favicons` is disabled but `tabs.tabs_are_windows` is set, the window icon is still set to the page's favicon now. +- The `--asciidoc` argument to `src2asciidoc.py` and `build_release.py` now + only takes the path to `asciidoc.py`, using the current Python interpreter by + default. To configure the Python interpreter as well, use + `--asciidoc-python path/to/python --asciidoc path/to/asciidoc.py` + instead of the former + `--asciidoc path/to/python path/to/asciidoc.py`. Added ~~~~~ diff --git a/scripts/asciidoc2html.py b/scripts/asciidoc2html.py index 5cb49c767..17c3fb367 100755 --- a/scripts/asciidoc2html.py +++ b/scripts/asciidoc2html.py @@ -46,10 +46,12 @@ class AsciiDoc: FILES = ['faq', 'changelog', 'contributing', 'quickstart', 'userscripts'] def __init__(self, - asciidoc: Optional[List[str]], + asciidoc: Optional[str], + asciidoc_python: Optional[str], website: Optional[str]) -> None: self._cmd = None # type: Optional[List[str]] self._asciidoc = asciidoc + self._asciidoc_python = asciidoc_python self._website = website self._homedir = None # type: Optional[pathlib.Path] self._themedir = None # type: Optional[pathlib.Path] @@ -218,7 +220,9 @@ class AsciiDoc: def _get_asciidoc_cmd(self) -> List[str]: """Try to find out what commandline to use to invoke asciidoc.""" if self._asciidoc is not None: - return self._asciidoc + python = (sys.executable if self._asciidoc_python is None + else self._asciidoc_python) + return [python, self._asciidoc] for executable in ['asciidoc', 'asciidoc.py']: try: @@ -270,10 +274,12 @@ def parse_args() -> argparse.Namespace: parser = argparse.ArgumentParser() parser.add_argument('--website', help="Build website into a given " "directory.") - parser.add_argument('--asciidoc', help="Full path to python and " - "asciidoc.py. If not given, it's searched in PATH.", - nargs=2, required=False, - metavar=('PYTHON', 'ASCIIDOC')) + parser.add_argument('--asciidoc', help="Full path to asciidoc.py. " + "If not given, it's searched in PATH.", + nargs='?') + parser.add_argument('--asciidoc-python', help="Python to use for asciidoc." + "If not given, the current Python interpreter is used.", + nargs='?') return parser.parse_args() @@ -301,7 +307,8 @@ def main(colors: bool = False) -> None: utils.change_cwd() utils.use_color = colors args = parse_args() - run(asciidoc=args.asciidoc, website=args.website) + run(asciidoc=args.asciidoc, asciidoc_python=args.asciidoc_python, + website=args.website) if __name__ == '__main__': diff --git a/scripts/dev/build_release.py b/scripts/dev/build_release.py index 53c17843e..8030d61a1 100755 --- a/scripts/dev/build_release.py +++ b/scripts/dev/build_release.py @@ -78,10 +78,11 @@ def call_tox(toxenv, *args, python=sys.executable): def run_asciidoc2html(args): """Common buildsteps used for all OS'.""" utils.print_title("Running asciidoc2html.py") + a2h_args = [] if args.asciidoc is not None: - a2h_args = ['--asciidoc'] + args.asciidoc - else: - a2h_args = [] + a2h_args += ['--asciidoc', args.asciidoc] + if args.asciidoc_python is not None: + a2h_args += ['--asciidoc-python', args.asciidoc_python] call_script('asciidoc2html.py', *a2h_args) @@ -457,10 +458,12 @@ def main(): parser = argparse.ArgumentParser() parser.add_argument('--no-asciidoc', action='store_true', help="Don't generate docs") - parser.add_argument('--asciidoc', help="Full path to python and " - "asciidoc.py. If not given, it's searched in PATH.", - nargs=2, required=False, - metavar=('PYTHON', 'ASCIIDOC')) + parser.add_argument('--asciidoc', help="Full path to asciidoc.py. " + "If not given, it's searched in PATH.", + nargs='?') + parser.add_argument('--asciidoc-python', help="Python to use for asciidoc." + "If not given, the current Python interpreter is used.", + nargs='?') parser.add_argument('--upload', action='store_true', required=False, help="Toggle to upload the release to GitHub") args = parser.parse_args() From d8473590e6dfdc52a0ad6d43701a5cfdc2d9f7d2 Mon Sep 17 00:00:00 2001 From: Florian Bruhin Date: Wed, 16 Sep 2020 21:06:36 +0200 Subject: [PATCH 39/50] Update release instructions --- scripts/dev/update_version.py | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/scripts/dev/update_version.py b/scripts/dev/update_version.py index e86ff257d..7c12dab15 100644 --- a/scripts/dev/update_version.py +++ b/scripts/dev/update_version.py @@ -78,13 +78,12 @@ if __name__ == "__main__": print("* Create new release via GitHub (required to upload release " "artifacts)") print("* Linux: git fetch && git checkout v{v} && " - "./.venv/bin/python3 scripts/dev/build_release.py --upload" + "tox -e build-release -- --upload" .format(v=version)) print("* Windows: git fetch; git checkout v{v}; " - "py -3 scripts\\dev\\build_release.py --asciidoc " - "C:\\Python27\\python " - "$env:userprofile\\bin\\asciidoc-8.6.10\\asciidoc.py --upload" + "py -3.7 -m tox -e build-release -- --asciidoc " + "$env:userprofile\\bin\\asciidoc-9.9.2\\asciidoc.py --upload" .format(v=version)) print("* macOS: git fetch && git checkout v{v} && " - "python3 scripts/dev/build_release.py --upload" + "tox -e build-release -- --upload" .format(v=version)) From 4617f42415894cbb8b905eb65959c061d49718ac Mon Sep 17 00:00:00 2001 From: rien333 Date: Thu, 17 Sep 2020 12:34:32 +0200 Subject: [PATCH 40/50] Apply Mozilla's css-styling to images --- misc/userscripts/readability-js | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/misc/userscripts/readability-js b/misc/userscripts/readability-js index 18c86584c..9cb6297c4 100755 --- a/misc/userscripts/readability-js +++ b/misc/userscripts/readability-js @@ -44,6 +44,16 @@ const HEADER = ` h1, h2, h3 { line-height: 1.2; } + img { + max-width:100%; + height:auto; + } + p > img:only-child, + p > a:only-child > img:only-child, + .wp-caption img, + figure img { + display: block; + } From cba060a5510e1eee92b612a04246984130abda2c Mon Sep 17 00:00:00 2001 From: Joakim Klevmo Hansen Date: Fri, 18 Sep 2020 09:23:32 +0200 Subject: [PATCH 41/50] Specify 'PASSWORD_STORE_DIR' when invoking pass from qute-pass --- misc/userscripts/qute-pass | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/misc/userscripts/qute-pass b/misc/userscripts/qute-pass index 10f215992..9bd199ccd 100755 --- a/misc/userscripts/qute-pass +++ b/misc/userscripts/qute-pass @@ -134,19 +134,19 @@ def find_pass_candidates(domain, password_store_path): return candidates -def _run_pass(pass_arguments, encoding): +def _run_pass(pass_arguments, encoding, password_store): # The executable is conveniently named after it's mode [pass|gopass]. pass_command = [arguments.mode] - process = subprocess.run(pass_command + pass_arguments, stdout=subprocess.PIPE) + process = subprocess.run(pass_command + pass_arguments, env={'PASSWORD_STORE_DIR': password_store}, stdout=subprocess.PIPE) return process.stdout.decode(encoding).strip() -def pass_(path, encoding): - return _run_pass(['show', path], encoding) +def pass_(path, encoding, password_store): + return _run_pass(['show', path], encoding, password_store) -def pass_otp(path, encoding): - return _run_pass(['otp', path], encoding) +def pass_otp(path, encoding, password_store): + return _run_pass(['otp', path], encoding, password_store) def dmenu(items, invocation, encoding): @@ -209,7 +209,7 @@ def main(arguments): # If username-target is path and user asked for username-only, we don't need to run pass secret = None if not (arguments.username_target == 'path' and arguments.username_only): - secret = pass_(selection, arguments.io_encoding) + secret = pass_(selection, arguments.io_encoding, arguments.password_store) # Match password match = re.match(arguments.password_pattern, secret) From f6dd664a1e44d7855472decb67e34e02757814a9 Mon Sep 17 00:00:00 2001 From: Joakim Klevmo Hansen Date: Fri, 18 Sep 2020 09:51:44 +0200 Subject: [PATCH 42/50] Keep existing env vars when specifying PASSWORD_STORE_DIR --- misc/userscripts/qute-pass | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/misc/userscripts/qute-pass b/misc/userscripts/qute-pass index 9bd199ccd..16d359f25 100755 --- a/misc/userscripts/qute-pass +++ b/misc/userscripts/qute-pass @@ -137,7 +137,9 @@ def find_pass_candidates(domain, password_store_path): def _run_pass(pass_arguments, encoding, password_store): # The executable is conveniently named after it's mode [pass|gopass]. pass_command = [arguments.mode] - process = subprocess.run(pass_command + pass_arguments, env={'PASSWORD_STORE_DIR': password_store}, stdout=subprocess.PIPE) + env = os.environ.copy() + env['PASSWORD_STORE_DIR'] = password_store + process = subprocess.run(pass_command + pass_arguments, env=env, stdout=subprocess.PIPE) return process.stdout.decode(encoding).strip() From c79c34d3e0abc20bef531efd078707e9e990b0ff Mon Sep 17 00:00:00 2001 From: Joakim Klevmo Hansen Date: Fri, 18 Sep 2020 11:26:15 +0200 Subject: [PATCH 43/50] Access qute-pass arguments directly and expand all paths --- misc/userscripts/qute-pass | 52 ++++++++++++++++++++------------------ 1 file changed, 27 insertions(+), 25 deletions(-) diff --git a/misc/userscripts/qute-pass b/misc/userscripts/qute-pass index 16d359f25..b49e87dd8 100755 --- a/misc/userscripts/qute-pass +++ b/misc/userscripts/qute-pass @@ -61,11 +61,19 @@ import sys import tldextract + +def expanded_path(path): + # Expand potential ~ in paths, since this script won't be called from a shell that does it for us + expanded = os.path.expanduser(path) + # Add trailing slash if not present + return os.path.join(expanded, '') + + argument_parser = argparse.ArgumentParser(description=__doc__, usage=USAGE, epilog=EPILOG) argument_parser.add_argument('url', nargs='?', default=os.getenv('QUTE_URL')) argument_parser.add_argument('--password-store', '-p', - default=os.getenv('PASSWORD_STORE_DIR', default=os.path.expanduser('~/.password-store')), - help='Path to your pass password-store (only used in pass-mode)') + default=expanded_path(os.getenv('PASSWORD_STORE_DIR', default='~/.password-store')), + help='Path to your pass password-store (only used in pass-mode)', type=expanded_path) argument_parser.add_argument('--mode', '-M', choices=['pass', 'gopass'], default="pass", help='Select mode [gopass] to use gopass instead of the standard pass.') argument_parser.add_argument('--username-pattern', '-u', default=r'.*/(.+)', @@ -107,7 +115,7 @@ def qute_command(command): fifo.flush() -def find_pass_candidates(domain, password_store_path): +def find_pass_candidates(domain): candidates = [] if arguments.mode == "gopass": @@ -117,13 +125,13 @@ def find_pass_candidates(domain, password_store_path): if domain in password: candidates.append(password) else: - for path, directories, file_names in os.walk(password_store_path, followlinks=True): + for path, directories, file_names in os.walk(arguments.password_store, followlinks=True): secrets = fnmatch.filter(file_names, '*.gpg') if not secrets: continue # Strip password store path prefix to get the relative pass path - pass_path = path[len(password_store_path):] + pass_path = path[len(arguments.password_store):] split_path = pass_path.split(os.path.sep) for secret in secrets: secret_base = os.path.splitext(secret)[0] @@ -134,27 +142,27 @@ def find_pass_candidates(domain, password_store_path): return candidates -def _run_pass(pass_arguments, encoding, password_store): +def _run_pass(pass_arguments): # The executable is conveniently named after it's mode [pass|gopass]. pass_command = [arguments.mode] env = os.environ.copy() - env['PASSWORD_STORE_DIR'] = password_store + env['PASSWORD_STORE_DIR'] = arguments.password_store process = subprocess.run(pass_command + pass_arguments, env=env, stdout=subprocess.PIPE) - return process.stdout.decode(encoding).strip() + return process.stdout.decode(arguments.io_encoding).strip() -def pass_(path, encoding, password_store): - return _run_pass(['show', path], encoding, password_store) +def pass_(path): + return _run_pass(['show', path]) -def pass_otp(path, encoding, password_store): - return _run_pass(['otp', path], encoding, password_store) +def pass_otp(path): + return _run_pass(['otp', path]) -def dmenu(items, invocation, encoding): +def dmenu(items, invocation): command = shlex.split(invocation) - process = subprocess.run(command, input='\n'.join(items).encode(encoding), stdout=subprocess.PIPE) - return process.stdout.decode(encoding).strip() + process = subprocess.run(command, input='\n'.join(items).encode(arguments.io_encoding), stdout=subprocess.PIPE) + return process.stdout.decode(arguments.io_encoding).strip() def fake_key_raw(text): @@ -172,11 +180,6 @@ def main(arguments): extractor = tldextract.TLDExtract(extra_suffixes=arguments.extra_url_suffixes.split(',')) extract_result = extractor(arguments.url) - # Expand potential ~ in paths, since this script won't be called from a shell that does it for us - password_store_path = os.path.expanduser(arguments.password_store) - # Add trailing slash if not present - password_store_path = os.path.join(password_store_path, '') - # Try to find candidates using targets in the following order: fully-qualified domain name (includes subdomains), # the registered domain name, the IPv4 address if that's what the URL represents and finally the private domain # (if a non-public suffix was used). @@ -190,7 +193,7 @@ def main(arguments): for target in filter(None, [extract_result.fqdn, extract_result.registered_domain, extract_result.ipv4, private_domain]): attempted_targets.append(target) - target_candidates = find_pass_candidates(target, password_store_path) + target_candidates = find_pass_candidates(target) if not target_candidates: continue @@ -202,8 +205,7 @@ def main(arguments): stderr('No pass candidates for URL {!r} found! (I tried {!r})'.format(arguments.url, attempted_targets)) return ExitCodes.NO_PASS_CANDIDATES - selection = candidates.pop() if len(candidates) == 1 else dmenu(sorted(candidates), arguments.dmenu_invocation, - arguments.io_encoding) + selection = candidates.pop() if len(candidates) == 1 else dmenu(sorted(candidates), arguments.dmenu_invocation) # Nothing was selected, simply return if not selection: return ExitCodes.SUCCESS @@ -211,7 +213,7 @@ def main(arguments): # If username-target is path and user asked for username-only, we don't need to run pass secret = None if not (arguments.username_target == 'path' and arguments.username_only): - secret = pass_(selection, arguments.io_encoding, arguments.password_store) + secret = pass_(selection) # Match password match = re.match(arguments.password_pattern, secret) @@ -233,7 +235,7 @@ def main(arguments): elif arguments.password_only: fake_key_raw(password) elif arguments.otp_only: - otp = pass_otp(selection, arguments.io_encoding) + otp = pass_otp(selection) fake_key_raw(otp) else: # Enter username and password using fake-key and (which seems to work almost universally), then switch From c3b680e3580bde01e831cae4e5ee4d251b0d791a Mon Sep 17 00:00:00 2001 From: qutebrowser bot Date: Mon, 21 Sep 2020 04:31:33 +0000 Subject: [PATCH 44/50] Update dependencies --- misc/requirements/requirements-mypy.txt | 2 +- misc/requirements/requirements-pyroma.txt | 2 +- misc/requirements/requirements-sphinx.txt | 2 +- misc/requirements/requirements-tests.txt | 4 ++-- requirements.txt | 2 +- 5 files changed, 6 insertions(+), 6 deletions(-) diff --git a/misc/requirements/requirements-mypy.txt b/misc/requirements/requirements-mypy.txt index 4c9e47067..863f48c6f 100644 --- a/misc/requirements/requirements-mypy.txt +++ b/misc/requirements/requirements-mypy.txt @@ -9,7 +9,7 @@ MarkupSafe==1.1.1 mypy==0.782 mypy-extensions==0.4.3 pluggy==0.13.1 -Pygments==2.7.0 +Pygments==2.7.1 -e git+https://github.com/stlehmann/PyQt5-stubs.git@master#egg=PyQt5_stubs typed-ast==1.4.1 typing-extensions==3.7.4.3 diff --git a/misc/requirements/requirements-pyroma.txt b/misc/requirements/requirements-pyroma.txt index 2100af3cb..c6e751e19 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.7.0 +Pygments==2.7.1 pyroma==2.6 diff --git a/misc/requirements/requirements-sphinx.txt b/misc/requirements/requirements-sphinx.txt index 3fd996cc2..7b43a72a1 100644 --- a/misc/requirements/requirements-sphinx.txt +++ b/misc/requirements/requirements-sphinx.txt @@ -10,7 +10,7 @@ imagesize==1.2.0 Jinja2==2.11.2 MarkupSafe==1.1.1 packaging==20.4 -Pygments==2.7.0 +Pygments==2.7.1 pyparsing==2.4.7 pytz==2020.1 requests==2.24.0 diff --git a/misc/requirements/requirements-tests.txt b/misc/requirements/requirements-tests.txt index af751739f..789c176e6 100644 --- a/misc/requirements/requirements-tests.txt +++ b/misc/requirements/requirements-tests.txt @@ -14,7 +14,7 @@ execnet==1.7.1 Flask==1.1.2 glob2==0.7 hunter==3.2.2 -hypothesis==5.35.2 ; python_version>="3.6" +hypothesis==5.35.3 ; python_version>="3.6" idna==2.10 iniconfig==1.0.1 itsdangerous==1.1.0 @@ -30,7 +30,7 @@ parse-type==0.5.2 pluggy==0.13.1 py==1.9.0 py-cpuinfo==7.0.0 -Pygments==2.7.0 +Pygments==2.7.1 pyparsing==2.4.7 pytest==6.0.2 pytest-bdd==4.0.1 diff --git a/requirements.txt b/requirements.txt index 706369e16..388a08618 100644 --- a/requirements.txt +++ b/requirements.txt @@ -5,6 +5,6 @@ colorama==0.4.3 cssutils==1.0.2 Jinja2==2.11.2 MarkupSafe==1.1.1 -Pygments==2.7.0 +Pygments==2.7.1 pyPEG2==2.15.2 PyYAML==5.3.1 From 5bb96bbd3d295c6dc393e3ce3ebcd1371e720667 Mon Sep 17 00:00:00 2001 From: Florian Bruhin Date: Mon, 21 Sep 2020 10:07:25 +0200 Subject: [PATCH 45/50] scripts: Deal with pip environment markers --- scripts/dev/recompile_requirements.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/scripts/dev/recompile_requirements.py b/scripts/dev/recompile_requirements.py index c1d3f8c36..5e865e802 100644 --- a/scripts/dev/recompile_requirements.py +++ b/scripts/dev/recompile_requirements.py @@ -324,6 +324,8 @@ def print_changed_files(): if '==' in line: name, version = line[1:].split('==') + if ';' in version: # pip environment markers + version = version.split(';')[0].strip() else: name = line[1:] version = '?' From b8d8bb7745b1caddd8dfeb0cae42266a0c8f2002 Mon Sep 17 00:00:00 2001 From: Florian Bruhin Date: Mon, 21 Sep 2020 10:13:24 +0200 Subject: [PATCH 46/50] scripts: Don't lower-case package names We should hopefully get a "canonical" package name from pip already, so let's just keep that and adjust the package keys accordingly. --- scripts/dev/recompile_requirements.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/scripts/dev/recompile_requirements.py b/scripts/dev/recompile_requirements.py index 5e865e802..439976c34 100644 --- a/scripts/dev/recompile_requirements.py +++ b/scripts/dev/recompile_requirements.py @@ -276,8 +276,8 @@ class Change: self.name = name self.old = None self.new = None - if name.lower() in CHANGELOG_URLS: - self.url = CHANGELOG_URLS[name.lower()] + if name in CHANGELOG_URLS: + self.url = CHANGELOG_URLS[name] self.link = '[{}]({})'.format(self.name, self.url) else: self.url = '(no changelog)' From ff0fcafa23bb9daaa0792c447b04d2a53d8d43c6 Mon Sep 17 00:00:00 2001 From: Florian Bruhin Date: Mon, 21 Sep 2020 10:23:44 +0200 Subject: [PATCH 47/50] Update changelog --- doc/changelog.asciidoc | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/doc/changelog.asciidoc b/doc/changelog.asciidoc index 449f51f4b..c29595d9e 100644 --- a/doc/changelog.asciidoc +++ b/doc/changelog.asciidoc @@ -66,6 +66,8 @@ Changed `--asciidoc-python path/to/python --asciidoc path/to/asciidoc.py` instead of the former `--asciidoc path/to/python path/to/asciidoc.py`. +- The `readability-js` userscript now adds some CSS to better deal + with images, similarly to what Firefox' reader mode does. Added ~~~~~ @@ -127,11 +129,14 @@ Fixed - Highlighting in the completion now works properly when UTF-16 surrogate pairs (such as emoji) are involved. - When a windowed inspector is clicked, insert mode now isn't entered anymore. -- The `qute-pass` userscript now works correctly with newer - `gopass` versions again, instead of copying a deprecation notice - as password. - When `:undo` to re-open a tab but `tabs.tabs_are_windows` was set between closing and undoing the close, qutebrowser crashed. This is now fixed. +- Fixes for the `qute-pass` userscript: + * With newer `gopass` versions, a deprecation notice was copied as + password due to `qute-pass` using it in a deprecated way. + * The `--password-store` argument didn't actually set + `PASSWORD_STORE_DIR` for `pass`, resulting in `qute-pass` finding matches but the + underlying `pass` not finding matching passwords. This is now fixed. v1.13.1 (2020-07-17) -------------------- From f8414de18185c7cb9f7f57e56ff5dac3b9011f70 Mon Sep 17 00:00:00 2001 From: Florian Bruhin Date: Mon, 21 Sep 2020 11:22:08 +0200 Subject: [PATCH 48/50] Add missing restart flag for prefers_color_scheme_dark --- doc/help/settings.asciidoc | 2 ++ qutebrowser/config/configdata.yml | 1 + 2 files changed, 3 insertions(+) diff --git a/doc/help/settings.asciidoc b/doc/help/settings.asciidoc index a9c959089..eacb830af 100644 --- a/doc/help/settings.asciidoc +++ b/doc/help/settings.asciidoc @@ -1735,6 +1735,8 @@ On QtWebKit, this setting is unavailable. === colors.webpage.prefers_color_scheme_dark Force `prefers-color-scheme: dark` colors for websites. +This setting requires a restart. + Type: <> Default: +pass:[false]+ diff --git a/qutebrowser/config/configdata.yml b/qutebrowser/config/configdata.yml index 85ddaf6df..5f100e767 100644 --- a/qutebrowser/config/configdata.yml +++ b/qutebrowser/config/configdata.yml @@ -2677,6 +2677,7 @@ colors.webpage.prefers_color_scheme_dark: backend: QtWebEngine: Qt 5.14 QtWebKit: false + restart: true ## dark mode From e010afd3a20a86639396a9c844abfea7b23cc67a Mon Sep 17 00:00:00 2001 From: Florian Bruhin Date: Mon, 21 Sep 2020 14:55:26 +0200 Subject: [PATCH 49/50] Update site-specific quirks - Add some comments about the existing quirks. - Remove Google Docs quirk as it doesn't seem to work anymore. See #4805 and https://bugs.webkit.org/show_bug.cgi?id=215845 See #4810 --- .../browser/webengine/webenginesettings.py | 22 ++++++++++++++++--- 1 file changed, 19 insertions(+), 3 deletions(-) diff --git a/qutebrowser/browser/webengine/webenginesettings.py b/qutebrowser/browser/webengine/webenginesettings.py index f5f4e9c31..336540ba0 100644 --- a/qutebrowser/browser/webengine/webenginesettings.py +++ b/qutebrowser/browser/webengine/webenginesettings.py @@ -390,9 +390,14 @@ def init_private_profile(): def _init_site_specific_quirks(): + """Add custom user-agent settings for problematic sites. + + See https://github.com/qutebrowser/qutebrowser/issues/4810 + """ if not config.val.content.site_specific_quirks: return + # Please leave this here as a template for new UAs. # default_ua = ("Mozilla/5.0 ({os_info}) " # "AppleWebKit/{webkit_version} (KHTML, like Gecko) " # "{qt_key}/{qt_version} " @@ -402,7 +407,6 @@ def _init_site_specific_quirks(): "AppleWebKit/{webkit_version} (KHTML, like Gecko) " "{upstream_browser_key}/{upstream_browser_version} " "Safari/{webkit_version}") - firefox_ua = "Mozilla/5.0 ({os_info}; rv:71.0) Gecko/20100101 Firefox/71.0" new_chrome_ua = ("Mozilla/5.0 ({os_info}) " "AppleWebKit/537.36 (KHTML, like Gecko) " "Chrome/99 " @@ -414,14 +418,26 @@ def _init_site_specific_quirks(): "Edg/{upstream_browser_version}") user_agents = { + # Needed to avoid a ""WhatsApp works with Google Chrome 36+" error + # page which doesn't allow to use WhatsApp Web at all. Also see the + # additional JS quirk: qutebrowser/javascript/whatsapp_web_quirk.user.js + # https://github.com/qutebrowser/qutebrowser/issues/4445 'https://web.whatsapp.com/': no_qtwe_ua, + + # Needed to avoid a "you're using a browser [...] that doesn't allow us + # to keep your account secure" error. + # https://github.com/qutebrowser/qutebrowser/issues/5182 'https://accounts.google.com/*': edge_ua, + + # Needed because Slack adds an error which prevents using it relatively + # aggressively, despite things actually working fine. + # September 2020: Qt 5.12 works, but Qt <= 5.11 shows the error. + # https://github.com/qutebrowser/qutebrowser/issues/4669 'https://*.slack.com/*': new_chrome_ua, - 'https://docs.google.com/*': firefox_ua, - 'https://drive.google.com/*': firefox_ua, } if not qtutils.version_check('5.9'): + # Shows 502 Bad Gateway with the Qt 5.7 UA. user_agents['https://www.dell.com/support/*'] = new_chrome_ua for pattern, ua in user_agents.items(): From 1642fa252ae9427eef3acdba3ce45782bad608e2 Mon Sep 17 00:00:00 2001 From: Florian Bruhin Date: Mon, 21 Sep 2020 16:58:13 +0200 Subject: [PATCH 50/50] Update stacktrace instructions --- doc/stacktrace.asciidoc | 24 ++++++++++++++++++------ 1 file changed, 18 insertions(+), 6 deletions(-) diff --git a/doc/stacktrace.asciidoc b/doc/stacktrace.asciidoc index 9bda4ab87..f295a5305 100644 --- a/doc/stacktrace.asciidoc +++ b/doc/stacktrace.asciidoc @@ -28,10 +28,20 @@ Debian/Ubuntu/... ^^^^^^^^^^^^^^^^^ For Debian based systems (Debian, Ubuntu, Linux Mint, ...), debug information -is available in the repositories: +is for QtWebEngine is available in a dedicated repository. Enable that repository +(https://wiki.debian.org/HowToGetABacktrace#Installing_the_debugging_symbols[Debian], +https://wiki.ubuntu.com/Debug%20Symbol%20Packages[Ubuntu], +https://www.linuxmint.com/rel_tessa_mate_whatsnew.php[Linux Mint]) and install +the debug packages: ---- -# apt-get install python3-pyqt5-dbg python3-pyqt5.qtwebkit-dbg python3-dbg libqt5webkit5-dbg +# apt install python3-dbg python3-pyqt5-dbg python3-pyqt5.qtwebengine-dbg libqt5webengine5-dbgsym +---- + +or with the QtWebKit backend: + +---- +# apt install python3-dbg python3-pyqt5-dbg python3-pyqt5.qtwebkit-dbg libqt5webkit5-dbg ---- Fedora @@ -116,7 +126,7 @@ First install `gdb` on your system if it's not installed already. Then run qutebrowser directly inside gdb like this: ---- -$ gdb $(readlink -f $(which python3)) -ex 'run -m qutebrowser --debug' +$ gdb -ex r --args $(readlink -f $(which python3)) -m qutebrowser --debug --temp-basedir ---- Note qutebrowser/gdb will take a long time to start. After you reproduce the @@ -131,9 +141,10 @@ Program received signal SIGSEGV, Segmentation fault. Now enter these commands at the gdb prompt: ---- +(gdb) set pagination off +(gdb) set logging overwrite on (gdb) set logging on -(gdb) bt full -# you might have to press enter a few times until you get the prompt back +(gdb) bt (gdb) quit ---- @@ -176,9 +187,10 @@ Getting the stack trace Now enter these commands at the gdb prompt: ---- +(gdb) set pagination off +(gdb) set logging overwrite on (gdb) set logging on (gdb) bt -# you might have to press enter a few times until you get the prompt back (gdb) quit ----