summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.bumpversion.cfg2
-rw-r--r--.coveragerc3
-rw-r--r--.github/workflows/ci.yml4
-rw-r--r--.pylintrc3
-rw-r--r--doc/changelog.asciidoc39
-rw-r--r--doc/faq.asciidoc6
-rw-r--r--doc/help/settings.asciidoc31
-rw-r--r--doc/install.asciidoc8
-rw-r--r--doc/stacktrace.asciidoc48
-rw-r--r--misc/org.qutebrowser.qutebrowser.appdata.xml1
-rw-r--r--misc/qutebrowser.spec6
-rw-r--r--misc/requirements/requirements-check-manifest.txt4
-rw-r--r--misc/requirements/requirements-dev.txt22
-rw-r--r--misc/requirements/requirements-flake8.txt7
-rw-r--r--misc/requirements/requirements-mypy.txt8
-rw-r--r--misc/requirements/requirements-pyinstaller.txt4
-rw-r--r--misc/requirements/requirements-pylint.txt21
-rw-r--r--misc/requirements/requirements-pylint.txt-raw2
-rw-r--r--misc/requirements/requirements-pyroma.txt8
-rw-r--r--misc/requirements/requirements-sphinx.txt12
-rw-r--r--misc/requirements/requirements-tests-bleeding.txt2
-rw-r--r--misc/requirements/requirements-tests.txt24
-rw-r--r--misc/requirements/requirements-tests.txt-raw2
-rw-r--r--misc/requirements/requirements-tox.txt8
-rw-r--r--misc/requirements/requirements-vulture.txt2
-rw-r--r--misc/userscripts/README.md2
-rwxr-xr-xmisc/userscripts/readability-js3
-rw-r--r--qutebrowser/__init__.py2
-rw-r--r--qutebrowser/browser/inspector.py5
-rw-r--r--qutebrowser/browser/shared.py3
-rw-r--r--qutebrowser/browser/webengine/notification.py9
-rw-r--r--qutebrowser/browser/webengine/webenginesettings.py45
-rw-r--r--qutebrowser/browser/webengine/webenginetab.py4
-rw-r--r--qutebrowser/browser/webkit/http.py7
-rw-r--r--qutebrowser/browser/webkit/webkitsettings.py5
-rw-r--r--qutebrowser/commands/cmdexc.py21
-rw-r--r--qutebrowser/commands/parser.py19
-rw-r--r--qutebrowser/commands/runners.py7
-rw-r--r--qutebrowser/completion/completer.py4
-rw-r--r--qutebrowser/completion/models/configmodel.py18
-rw-r--r--qutebrowser/config/config.py6
-rw-r--r--qutebrowser/config/configdata.yml12
-rw-r--r--qutebrowser/config/configexc.py10
-rw-r--r--qutebrowser/config/configfiles.py6
-rw-r--r--qutebrowser/config/configtypes.py8
-rw-r--r--qutebrowser/config/websettings.py9
-rw-r--r--qutebrowser/html/settings.html5
-rw-r--r--qutebrowser/mainwindow/mainwindow.py4
-rw-r--r--qutebrowser/misc/elf.py2
-rw-r--r--qutebrowser/misc/throttle.py1
-rw-r--r--requirements.txt2
-rwxr-xr-xscripts/asciidoc2html.py5
-rw-r--r--scripts/dev/changelog_urls.json10
-rw-r--r--scripts/dev/run_pylint_on_tests.py2
-rwxr-xr-xscripts/dev/src2asciidoc.py16
-rw-r--r--tests/end2end/data/darkmode/mathml-display.html23
-rw-r--r--tests/end2end/data/darkmode/mathml-inline.html (renamed from tests/end2end/data/darkmode/mathml.html)0
-rw-r--r--tests/end2end/features/misc.feature2
-rw-r--r--tests/end2end/test_invocations.py5
-rw-r--r--tests/unit/browser/webkit/http/test_http.py15
-rw-r--r--tests/unit/browser/webkit/test_webkitelem.py1
-rw-r--r--tests/unit/commands/test_cmdexc.py40
-rw-r--r--tests/unit/commands/test_parser.py14
-rw-r--r--tests/unit/config/test_config.py4
-rw-r--r--tests/unit/config/test_configexc.py26
-rw-r--r--tests/unit/config/test_configtypes.py1
-rw-r--r--tests/unit/misc/test_elf.py17
-rw-r--r--tests/unit/utils/test_log.py1
-rw-r--r--tests/unit/utils/test_utils.py17
-rw-r--r--tests/unit/utils/test_version.py6
70 files changed, 468 insertions, 233 deletions
diff --git a/.bumpversion.cfg b/.bumpversion.cfg
index 83a5b985a..5ae20070b 100644
--- a/.bumpversion.cfg
+++ b/.bumpversion.cfg
@@ -1,5 +1,5 @@
[bumpversion]
-current_version = 2.5.0
+current_version = 2.5.1
commit = True
message = Release v{new_version}
tag = True
diff --git a/.coveragerc b/.coveragerc
index 7c4f7b218..cb0619b80 100644
--- a/.coveragerc
+++ b/.coveragerc
@@ -23,6 +23,3 @@ exclude_lines =
[xml]
output=coverage.xml
-
-[html]
-show_contexts = True
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 28f1d0971..288efbc02 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -198,12 +198,12 @@ jobs:
with:
persist-credentials: false
- name: Initialize CodeQL
- uses: github/codeql-action/init@v1
+ uses: github/codeql-action/init@v2
with:
languages: javascript, python
queries: +security-extended
- name: Perform CodeQL Analysis
- uses: github/codeql-action/analyze@v1
+ uses: github/codeql-action/analyze@v2
irc:
timeout-minutes: 2
diff --git a/.pylintrc b/.pylintrc
index c5a1289fb..47d3a163d 100644
--- a/.pylintrc
+++ b/.pylintrc
@@ -23,10 +23,8 @@ py-version=3.7
[MESSAGES CONTROL]
enable=all
disable=locally-disabled,
- locally-enabled,
suppressed-message,
fixme,
- no-self-use,
cyclic-import,
blacklisted-name,
logging-format-interpolation,
@@ -51,7 +49,6 @@ disable=locally-disabled,
too-many-statements,
too-few-public-methods,
import-outside-toplevel,
- bad-continuation, # This lint disagrees with Black
consider-using-f-string,
logging-fstring-interpolation,
raise-missing-from,
diff --git a/doc/changelog.asciidoc b/doc/changelog.asciidoc
index e26d5beec..dd685a149 100644
--- a/doc/changelog.asciidoc
+++ b/doc/changelog.asciidoc
@@ -19,6 +19,12 @@ breaking changes (such as renamed commands) can happen in minor releases.
v3.0.0 (unreleased)
-------------------
+Added
+~~~~~
+
+- On invalid commands/settings with a similarly spelled match, qutebrowser now
+ suggests the correct name in its error messages.
+
Removed
~~~~~~~
@@ -43,9 +49,31 @@ Changed
resource system (rather than compiling them into a Qt resource file).
Packagers are advised to use `misc/Makefile` if possible, which has been
updated with the new paths.
+- The `content.javascript.can_access_clipboard` setting got renamed to
+ `content.javascript.clipboard` and now understands three different values
+ rather than being a boolean: `none` (formerly `false`), `access` (formerly
+ `true`) and `access-paste` (additionally allows pasting content, needed for
+ websites like Photopea or GitHub Codespaces).
+- The default `hints.selectors` now also match the `treeitem` ARIA roles.
+
+Fixed
+~~~~~
+
+- When the devtools are clicked but `input.insert_mode.auto_enter` is set to
+ `false`, insert mode now isn't entered anymore.
+
+[[v2.5.2]]
+v2.5.2 (unreleased)
+-------------------
+
+Fixed
+~~~~~
+
+- The `install` and `stacktrace` help pages are now included in the docs
+ shipped with qutebrowser when using the recommended packaging workflow.
[[v2.5.1]]
-v2.5.1 (unreleased)
+v2.5.1 (2022-05-26)
-------------------
Fixed
@@ -53,11 +81,20 @@ Fixed
- The `qute-pass` userscript is marked as executable again.
- PDF.js now works properly again with the macOS and Windows releases.
+- The MathML workaround for darkmode (e.g. black on black Wikipedia formula)
+ now also works for display (rather than inline) math.
+- The `content.proxy` setting can now correctly be set to arbitrary values via
+ the `qute://settings` page again.
+- Fixed issues with Chromium version detection on Archlinux with
+ qt5-webengine 5.15.9-3.
+- Fixed a rare possible crash with invalid `Content-Disposition` headers.
- Fixes for various notification-related crashes:
* With the `tiramisu` notification server (due to invalid behavior of the server, now a non-fatal error)
* With the `budgie` notification server when closing a notification (due to invalid behavior of the server, now worked around)
* When a server exits with an unsuccessful exit status (now a non-fatal error)
* When a server couldn't be started successfully (now a non-fatal error)
+ * With the `herbe` notification presenter, when the website tries to close
+ the notification after the user accepting (right-clicking) it.
- Fixes in userscripts:
* The `qute-bitwarden` userscript now correctly searches for entries for
sites on a subdomain of an unrecognized TLD. subdomain names. Previously
diff --git a/doc/faq.asciidoc b/doc/faq.asciidoc
index 503893b66..4b3596285 100644
--- a/doc/faq.asciidoc
+++ b/doc/faq.asciidoc
@@ -354,9 +354,9 @@ There is a total of four possible approaches to get dark websites:
of the Dark Reader extension. This is mostly untested, though.
How do I make copy to clipboard buttons work?::
-You can `:set content.javascript.can_access_clipboard true`, or
-`:set -u some.domain content.javascript.can_access_clipboard true` if you want to limit
-the setting to `some.domain`.
+You can `:set content.javascript.clipboard access` to allow this globally (not
+recommended!), or `:set -u some.domain content.javascript.clipboad access` if
+you want to limit the setting to `some.domain`.
== Troubleshooting
diff --git a/doc/help/settings.asciidoc b/doc/help/settings.asciidoc
index 2f60525f4..1236dc3ac 100644
--- a/doc/help/settings.asciidoc
+++ b/doc/help/settings.asciidoc
@@ -167,9 +167,9 @@
|<<content.hyperlink_auditing,content.hyperlink_auditing>>|Enable hyperlink auditing (`<a ping>`).
|<<content.images,content.images>>|Load images automatically in web pages.
|<<content.javascript.alert,content.javascript.alert>>|Show javascript alerts.
-|<<content.javascript.can_access_clipboard,content.javascript.can_access_clipboard>>|Allow JavaScript to read from or write to the clipboard.
|<<content.javascript.can_close_tabs,content.javascript.can_close_tabs>>|Allow JavaScript to close tabs.
|<<content.javascript.can_open_tabs_automatically,content.javascript.can_open_tabs_automatically>>|Allow JavaScript to open new tabs without user interaction.
+|<<content.javascript.clipboard,content.javascript.clipboard>>|Allow JavaScript to read from or write to the clipboard.
|<<content.javascript.enabled,content.javascript.enabled>>|Enable JavaScript.
|<<content.javascript.log,content.javascript.log>>|Log levels to use for JavaScript console logging messages.
|<<content.javascript.modal_dialog,content.javascript.modal_dialog>>|Use the standard JavaScript modal dialog for `alert()` and `confirm()`.
@@ -2333,17 +2333,6 @@ Type: <<types,Bool>>
Default: +pass:[true]+
-[[content.javascript.can_access_clipboard]]
-=== content.javascript.can_access_clipboard
-Allow JavaScript to read from or write to the clipboard.
-With QtWebEngine, writing the clipboard as response to a user interaction is always allowed.
-
-This setting supports link:configuring{outfilesuffix}#patterns[URL patterns].
-
-Type: <<types,Bool>>
-
-Default: +pass:[false]+
-
[[content.javascript.can_close_tabs]]
=== content.javascript.can_close_tabs
Allow JavaScript to close tabs.
@@ -2366,6 +2355,23 @@ Type: <<types,Bool>>
Default: +pass:[false]+
+[[content.javascript.clipboard]]
+=== content.javascript.clipboard
+Allow JavaScript to read from or write to the clipboard.
+With QtWebEngine, writing the clipboard as response to a user interaction is always allowed.
+
+This setting supports link:configuring{outfilesuffix}#patterns[URL patterns].
+
+Type: <<types,String>>
+
+Valid values:
+
+ * +none+: Disable access to clipboard.
+ * +access+: Allow reading from and writing to the clipboard.
+ * +access-paste+: Allow accessing the clipboard and pasting clipboard content.
+
+Default: +pass:[none]+
+
[[content.javascript.enabled]]
=== content.javascript.enabled
Enable JavaScript.
@@ -3441,6 +3447,7 @@ Default:
* +pass:[[role=&quot;menuitem&quot;\]]+
* +pass:[[role=&quot;menuitemcheckbox&quot;\]]+
* +pass:[[role=&quot;menuitemradio&quot;\]]+
+* +pass:[[role=&quot;treeitem&quot;\]]+
* +pass:[[ng-click\]]+
* +pass:[[ngClick\]]+
* +pass:[[data-ng-click\]]+
diff --git a/doc/install.asciidoc b/doc/install.asciidoc
index bb4e08f5a..0f44e5a91 100644
--- a/doc/install.asciidoc
+++ b/doc/install.asciidoc
@@ -48,7 +48,7 @@ instructions!
Note you'll need some basic libraries to use the virtualenv-installed PyQt:
----
-# apt install --no-install-recommends git ca-certificates python3 python3-venv asciidoc libglib2.0-0 libgl1 libfontconfig1 libxcb-icccm4 libxcb-image0 libxcb-keysyms1 libxcb-randr0 libxcb-render-util0 libxcb-shape0 libxcb-xfixes0 libxcb-xinerama0 libxcb-xkb1 libxkbcommon-x11-0 libdbus-1-3 libyaml-dev gcc python3-dev libnss3
+# apt install --no-install-recommends git ca-certificates python3 python3-venv asciidoc libglib2.0-0 libgl1 libfontconfig1 libxcb-icccm4 libxcb-image0 libxcb-keysyms1 libxcb-randr0 libxcb-render-util0 libxcb-shape0 libxcb-xfixes0 libxcb-xinerama0 libxcb-xkb1 libxkbcommon-x11-0 libdbus-1-3 libyaml-dev gcc python3-dev libnss3 libasound2
----
// FIXME not needed anymore?
@@ -69,6 +69,12 @@ from January 2018 is packaged, with QtWebEngine 5.9 based on a Chromium from Jan
2017. It's recommended to either upgrade to Ubuntu 20.04 LTS or <<tox,install
qutebrowser in a virtualenv>> with a newer PyQt/Qt binary instead.
+Note you'll need some basic libraries to use the virtualenv-installed PyQt:
+
+----
+# apt install --no-install-recommends git ca-certificates python3 python3-venv asciidoc libglib2.0-0 libgl1 libfontconfig1 libxcb-icccm4 libxcb-image0 libxcb-keysyms1 libxcb-randr0 libxcb-render-util0 libxcb-shape0 libxcb-xfixes0 libxcb-xinerama0 libxcb-xkb1 libxkbcommon-x11-0 libdbus-1-3 libyaml-dev gcc python3-dev libnss3 libasound2
+----
+
Ubuntu 20.04 LTS / Linux Mint 20 (or newer)
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
diff --git a/doc/stacktrace.asciidoc b/doc/stacktrace.asciidoc
index 3d95aa25e..87410bd48 100644
--- a/doc/stacktrace.asciidoc
+++ b/doc/stacktrace.asciidoc
@@ -68,55 +68,13 @@ Then install the needed debuginfo packages:
Archlinux
^^^^^^^^^
-For Archlinux, no debug information is provided. You can either compile Qt
-yourself (which will take a few hours even on a modern machine) or use
-debugging symbols compiled/packaged by me (x86_64 only).
-
-To install my pre-built packages
-++++++++++++++++++++++++++++++++
-
-First download and sign the key:
-
-----
-# pacman-key -r 0xD6A1C70FE80A0C82
-$ pacman-key -f 0xD6A1C70FE80A0C82
- Key fingerprint = 14AF EC28 70C6 4863 C5C7 ACCB D6A1 C70F E80A 0C82
-# pacman-key --lsign-key 0xD6A1C70FE80A0C82
-----
-
-Then edit your `/etc/pacman.conf` to add the repository to the bottom:
+For Archlinux, debug information is provided via their https://wiki.archlinux.org/title/Debuginfod[Debuginfod instance]. To use it, set the following in your environment:
----
-[qt-debug]
-Server = https://qutebrowser.org/qt-debug/$arch
+DEBUGINFOD_URLS="https://debuginfod.archlinux.org/"
----
-Then install the packages:
-
-----
-# pacman -Suy python-pyqt5-debug qt5-base-debug qt5-webkit-debug qt5-webengine-debug
-----
-
-The `-debug` packages conflict with the non-debug variants - it's safe to
-remove them.
-
-To compile by yourself
-++++++++++++++++++++++
-
-Note that building Qt will likely take multiple hours, even on a recent system.
-I'd also expect it to take around 6 GB of RAM and 30 GB of disk space for a
-successful compile run.
-
-----
-$ git clone https://github.com/qutebrowser/qt-debug-pkgbuild.git
-$ cd qt-debug-pkgbuild
-$ export DEBUG_CFLAGS='-ggdb3 -fvar-tracking-assignments -Og'
-$ export DEBUG_CXXFLAGS='-ggdb3 -fvar-tracking-assignments -Og'
-$ cd qt5
-$ makepkg -si --pkg qt5-base-debug,qt5-webkit-debug,qt5-webengine-debug
-$ cd ../pyqt5
-$ makepkg -si --pkg python-pyqt5-debug
-----
+(Until early 2021, there was a custom [`qt-debug` repository](https://github.com/qutebrowser/qt-debug-pkgbuild). This is now archived.)
Getting the stack trace
~~~~~~~~~~~~~~~~~~~~~~~
diff --git a/misc/org.qutebrowser.qutebrowser.appdata.xml b/misc/org.qutebrowser.qutebrowser.appdata.xml
index a18511b73..f506bd5a8 100644
--- a/misc/org.qutebrowser.qutebrowser.appdata.xml
+++ b/misc/org.qutebrowser.qutebrowser.appdata.xml
@@ -44,6 +44,7 @@
</content_rating>
<releases>
<!-- Add new releases here -->
+<release version="2.5.1" date="2022-05-26"/>
<release version="2.5.0" date="2022-04-01"/>
<release version="2.4.0" date="2021-10-21"/>
<release version="2.3.1" date="2021-07-28"/>
diff --git a/misc/qutebrowser.spec b/misc/qutebrowser.spec
index 3c75d1b90..60729266e 100644
--- a/misc/qutebrowser.spec
+++ b/misc/qutebrowser.spec
@@ -2,7 +2,6 @@
import sys
import os
-import pathlib
sys.path.insert(0, os.getcwd())
from scripts import setupcommon
@@ -42,10 +41,7 @@ setupcommon.write_git_file()
if os.name == 'nt':
- # WORKAROUND for PyInstaller 5.0 bug:
- # https://github.com/pyinstaller/pyinstaller/issues/6759
- icons_path = pathlib.Path.cwd() / 'qutebrowser' / 'icons'
- icon = str(icons_path / 'qutebrowser.ico')
+ icon = '../qutebrowser/icons/qutebrowser.ico'
elif sys.platform == 'darwin':
icon = '../qutebrowser/icons/qutebrowser.icns'
else:
diff --git a/misc/requirements/requirements-check-manifest.txt b/misc/requirements/requirements-check-manifest.txt
index 012a3dc05..c51ef3d0e 100644
--- a/misc/requirements/requirements-check-manifest.txt
+++ b/misc/requirements/requirements-check-manifest.txt
@@ -1,8 +1,8 @@
# This file is automatically generated by scripts/dev/recompile_requirements.py
-build==0.7.0
+build==0.8.0
check-manifest==0.48
packaging==21.3
pep517==0.12.0
-pyparsing==3.0.8
+pyparsing==3.0.9
tomli==2.0.1
diff --git a/misc/requirements/requirements-dev.txt b/misc/requirements/requirements-dev.txt
index 462ea6e9e..e4e768353 100644
--- a/misc/requirements/requirements-dev.txt
+++ b/misc/requirements/requirements-dev.txt
@@ -1,43 +1,43 @@
# This file is automatically generated by scripts/dev/recompile_requirements.py
bleach==5.0.0
-build==0.7.0
+build==0.8.0
bump2version==1.0.1
-certifi==2021.10.8
+certifi==2022.5.18.1
cffi==1.15.0
charset-normalizer==2.0.12
commonmark==0.9.1
-cryptography==37.0.1
+cryptography==37.0.2
docutils==0.18.1
github3.py==3.2.0
hunter==3.4.3
idna==3.3
-importlib-metadata==4.11.3
+importlib-metadata==4.11.4
jeepney==0.8.0
-keyring==23.5.0
+keyring==23.6.0
manhole==1.8.0
packaging==21.3
pep517==0.12.0
-pkginfo==1.8.2
+pkginfo==1.8.3
ply==3.11
pycparser==2.21
Pygments==2.12.0
-PyJWT==2.3.0
+PyJWT==2.4.0
Pympler==1.0.1
-pyparsing==3.0.8
+pyparsing==3.0.9
PyQt-builder==1.12.2
python-dateutil==2.8.2
readme-renderer==35.0
-requests==2.27.1
+requests==2.28.0
requests-toolbelt==0.9.1
rfc3986==2.0.0
-rich==12.3.0
+rich==12.4.4
SecretStorage==3.3.2
sip==6.6.1
six==1.16.0
toml==0.10.2
tomli==2.0.1
-twine==4.0.0
+twine==4.0.1
typing_extensions==4.2.0
uritemplate==4.1.1
# urllib3==1.26.9
diff --git a/misc/requirements/requirements-flake8.txt b/misc/requirements/requirements-flake8.txt
index 8cfd81966..217089191 100644
--- a/misc/requirements/requirements-flake8.txt
+++ b/misc/requirements/requirements-flake8.txt
@@ -4,7 +4,7 @@ attrs==21.4.0
flake8==4.0.1
flake8-bugbear==22.4.25
flake8-builtins==1.5.3
-flake8-comprehensions==3.8.0
+flake8-comprehensions==3.10.0
flake8-copyright==0.2.2
flake8-debugger==4.1.2
flake8-deprecated==1.3
@@ -12,13 +12,12 @@ flake8-docstrings==1.6.0
flake8-future-import==0.4.6
flake8-mock==0.3
flake8-plugin-utils==1.3.2
-flake8-polyfill==1.0.2
flake8-pytest-style==1.6.0
flake8-string-format==0.3.0
-flake8-tidy-imports==4.6.0
+flake8-tidy-imports==4.8.0
flake8-tuple==0.4.1
mccabe==0.6.1
-pep8-naming==0.12.1
+pep8-naming==0.13.0
pycodestyle==2.8.0
pydocstyle==6.1.1
pyflakes==2.4.0
diff --git a/misc/requirements/requirements-mypy.txt b/misc/requirements/requirements-mypy.txt
index 2f62bf818..a4b555cf3 100644
--- a/misc/requirements/requirements-mypy.txt
+++ b/misc/requirements/requirements-mypy.txt
@@ -2,17 +2,17 @@
chardet==4.0.0
diff-cover==6.5.0
-importlib-metadata==4.11.3
+importlib-metadata==4.11.4
importlib-resources==5.7.1
Jinja2==3.1.2
-lxml==4.8.0
+lxml==4.9.0
MarkupSafe==2.1.1
-mypy==0.950
+mypy==0.961
mypy-extensions==0.4.3
pluggy==1.0.0
Pygments==2.12.0
PyQt5-stubs==5.15.6.0
tomli==2.0.1
-types-PyYAML==6.0.7
+types-PyYAML==6.0.8
typing_extensions==4.2.0
zipp==3.8.0
diff --git a/misc/requirements/requirements-pyinstaller.txt b/misc/requirements/requirements-pyinstaller.txt
index 8700478e5..35e65b6da 100644
--- a/misc/requirements/requirements-pyinstaller.txt
+++ b/misc/requirements/requirements-pyinstaller.txt
@@ -1,5 +1,5 @@
# This file is automatically generated by scripts/dev/recompile_requirements.py
altgraph==0.17.2
-pyinstaller==5.0.1
-pyinstaller-hooks-contrib==2022.4
+pyinstaller==5.1
+pyinstaller-hooks-contrib==2022.7
diff --git a/misc/requirements/requirements-pylint.txt b/misc/requirements/requirements-pylint.txt
index 5a21eb210..38231fa12 100644
--- a/misc/requirements/requirements-pylint.txt
+++ b/misc/requirements/requirements-pylint.txt
@@ -1,29 +1,30 @@
# This file is automatically generated by scripts/dev/recompile_requirements.py
-astroid==2.11.2
-certifi==2021.10.8
+astroid==2.11.5
+certifi==2022.5.18.1
cffi==1.15.0
charset-normalizer==2.0.12
-cryptography==37.0.1
-dill==0.3.4
+cryptography==37.0.2
+dill==0.3.5.1
future==0.18.2
github3.py==3.2.0
idna==3.3
isort==5.10.1
lazy-object-proxy==1.7.1
mccabe==0.7.0
-pefile==2021.9.3
+pefile==2022.5.30
platformdirs==2.5.2
pycparser==2.21
-PyJWT==2.3.0
-pylint==2.13.5
+PyJWT==2.4.0
+pylint==2.14.1
python-dateutil==2.8.2
./scripts/dev/pylint_checkers
-requests==2.27.1
+requests==2.28.0
six==1.16.0
tomli==2.0.1
-typed-ast==1.5.3 ; python_version<"3.8"
+tomlkit==0.11.0
+typed-ast==1.5.4 ; python_version<"3.8"
typing_extensions==4.2.0
uritemplate==4.1.1
# urllib3==1.26.9
-wrapt==1.14.0
+wrapt==1.14.1
diff --git a/misc/requirements/requirements-pylint.txt-raw b/misc/requirements/requirements-pylint.txt-raw
index 0d59fc96d..54e12a02a 100644
--- a/misc/requirements/requirements-pylint.txt-raw
+++ b/misc/requirements/requirements-pylint.txt-raw
@@ -1,6 +1,4 @@
pylint
-# WORKAROUND for https://github.com/PyCQA/pylint/issues/6438#issuecomment-1108747642
-astroid != 2.11.3
./scripts/dev/pylint_checkers
requests
github3.py
diff --git a/misc/requirements/requirements-pyroma.txt b/misc/requirements/requirements-pyroma.txt
index d17b46593..382418dd9 100644
--- a/misc/requirements/requirements-pyroma.txt
+++ b/misc/requirements/requirements-pyroma.txt
@@ -1,15 +1,15 @@
# This file is automatically generated by scripts/dev/recompile_requirements.py
-build==0.7.0
-certifi==2021.10.8
+build==0.8.0
+certifi==2022.5.18.1
charset-normalizer==2.0.12
docutils==0.18.1
idna==3.3
packaging==21.3
pep517==0.12.0
Pygments==2.12.0
-pyparsing==3.0.8
+pyparsing==3.0.9
pyroma==4.0
-requests==2.27.1
+requests==2.28.0
tomli==2.0.1
urllib3==1.26.9
diff --git a/misc/requirements/requirements-sphinx.txt b/misc/requirements/requirements-sphinx.txt
index abf2a2720..f100b6dc0 100644
--- a/misc/requirements/requirements-sphinx.txt
+++ b/misc/requirements/requirements-sphinx.txt
@@ -2,21 +2,21 @@
alabaster==0.7.12
Babel==2.10.1
-certifi==2021.10.8
+certifi==2022.5.18.1
charset-normalizer==2.0.12
-docutils==0.17.1
+docutils==0.18.1
idna==3.3
imagesize==1.3.0
-importlib-metadata==4.11.3
+importlib-metadata==4.11.4
Jinja2==3.1.2
MarkupSafe==2.1.1
packaging==21.3
Pygments==2.12.0
-pyparsing==3.0.8
+pyparsing==3.0.9
pytz==2022.1
-requests==2.27.1
+requests==2.28.0
snowballstemmer==2.2.0
-Sphinx==4.5.0
+Sphinx==5.0.1
sphinxcontrib-applehelp==1.0.2
sphinxcontrib-devhelp==1.0.2
sphinxcontrib-htmlhelp==2.0.0
diff --git a/misc/requirements/requirements-tests-bleeding.txt b/misc/requirements/requirements-tests-bleeding.txt
index 72d6ad083..fca7328f8 100644
--- a/misc/requirements/requirements-tests-bleeding.txt
+++ b/misc/requirements/requirements-tests-bleeding.txt
@@ -24,8 +24,6 @@ git+https://github.com/pytest-dev/pytest-cov.git
git+https://github.com/The-Compiler/pytest-xvfb.git
git+https://github.com/pytest-dev/pytest-xdist.git
git+https://github.com/john-kurkowski/tldextract
-# https://github.com/hjwp/pytest-icdiff/pull/20
-# git+https://github.com/hjwp/pytest-icdiff.git
# Problematic: needs rust (and some time to build)
# git+https://github.com/ArniDagur/python-adblock.git
diff --git a/misc/requirements/requirements-tests.txt b/misc/requirements/requirements-tests.txt
index e77cf12d5..3e9f3233d 100644
--- a/misc/requirements/requirements-tests.txt
+++ b/misc/requirements/requirements-tests.txt
@@ -2,20 +2,20 @@
attrs==21.4.0
beautifulsoup4==4.11.1
-certifi==2021.10.8
+certifi==2022.5.18.1
charset-normalizer==2.0.12
cheroot==8.6.0
click==8.1.3
-coverage==6.3.2
+coverage==6.4.1
+exceptiongroup==1.0.0rc8
execnet==1.9.0
-filelock==3.6.0
+filelock==3.7.1
Flask==2.1.2
glob2==0.7
hunter==3.4.3
-hypothesis==6.46.1
-icdiff==2.0.5
+hypothesis==6.47.2
idna==3.3
-importlib-metadata==4.11.3
+importlib-metadata==4.11.4
iniconfig==1.1.1
itsdangerous==2.1.2
jaraco.functools==3.5.0
@@ -23,22 +23,20 @@ jaraco.functools==3.5.0
Mako==1.2.0
manhole==1.8.0
# MarkupSafe==2.1.1
-more-itertools==8.12.0
+more-itertools==8.13.0
packaging==21.3
parse==1.19.0
parse-type==0.6.0
pluggy==1.0.0
-pprintpp==0.4.0
py==1.11.0
py-cpuinfo==8.0.0
Pygments==2.12.0
-pyparsing==3.0.8
+pyparsing==3.0.9
pytest==7.1.2
pytest-bdd==4.1.0
pytest-benchmark==3.4.1
pytest-cov==3.0.0
pytest-forked==1.4.0
-pytest-icdiff==0.5
pytest-instafail==0.4.2
pytest-mock==3.7.0
pytest-qt==4.0.2
@@ -47,15 +45,15 @@ pytest-rerunfailures==10.2
pytest-xdist==2.5.0
pytest-xvfb==2.0.0
PyVirtualDisplay==3.0
-requests==2.27.1
+requests==2.28.0
requests-file==1.5.1
six==1.16.0
sortedcontainers==2.4.0
soupsieve==2.3.2.post1
-tldextract==3.2.1
+tldextract==3.3.0
toml==0.10.2
tomli==2.0.1
urllib3==1.26.9
-vulture==2.3
+vulture==2.4
Werkzeug==2.1.2
zipp==3.8.0
diff --git a/misc/requirements/requirements-tests.txt-raw b/misc/requirements/requirements-tests.txt-raw
index 5586a86ef..f5edc9b6d 100644
--- a/misc/requirements/requirements-tests.txt-raw
+++ b/misc/requirements/requirements-tests.txt-raw
@@ -28,8 +28,6 @@ pytest-xvfb
PyVirtualDisplay
# To run on multiple cores with -n
pytest-xdist
-# For nicer output
-pytest-icdiff
# Needed to test misc/userscripts/qute-lastpass
tldextract
diff --git a/misc/requirements/requirements-tox.txt b/misc/requirements/requirements-tox.txt
index f6c14de9f..533e91e82 100644
--- a/misc/requirements/requirements-tox.txt
+++ b/misc/requirements/requirements-tox.txt
@@ -1,14 +1,14 @@
# This file is automatically generated by scripts/dev/recompile_requirements.py
distlib==0.3.4
-filelock==3.6.0
+filelock==3.7.1
packaging==21.3
-pip==22.0.4
+pip==22.1.2
platformdirs==2.5.2
pluggy==1.0.0
py==1.11.0
-pyparsing==3.0.8
-setuptools==62.1.0
+pyparsing==3.0.9
+setuptools==62.3.4
six==1.16.0
toml==0.10.2
tox==3.25.0
diff --git a/misc/requirements/requirements-vulture.txt b/misc/requirements/requirements-vulture.txt
index 433500bf0..ac5016c99 100644
--- a/misc/requirements/requirements-vulture.txt
+++ b/misc/requirements/requirements-vulture.txt
@@ -1,4 +1,4 @@
# This file is automatically generated by scripts/dev/recompile_requirements.py
toml==0.10.2
-vulture==2.3
+vulture==2.4
diff --git a/misc/userscripts/README.md b/misc/userscripts/README.md
index e800de0ce..5a7a3d51d 100644
--- a/misc/userscripts/README.md
+++ b/misc/userscripts/README.md
@@ -74,6 +74,8 @@ The following userscripts can be found on their own repositories.
and retrieve they when you want.
- [doi](https://github.com/cadadr/configuration/blob/master/qutebrowser/userscripts/doi):
Opens DOIs on Sci-Hub.
+- [qute-1password](https://github.com/fmartingr/qute-1password):
+ Qutebrowser userscript to fill 1password credentials
- [1password](https://github.com/tomoakley/dotfiles/blob/master/qutebrowser/userscripts/1password):
Integration with 1password on macOS.
- [localhost](https://github.com/SidharthArya/.qutebrowser/blob/master/userscripts/localhost):
diff --git a/misc/userscripts/readability-js b/misc/userscripts/readability-js
index 485957ddb..752b759bb 100755
--- a/misc/userscripts/readability-js
+++ b/misc/userscripts/readability-js
@@ -59,9 +59,6 @@ const HEADER = `
width: 100%;
margin: 0 0;
}
- a.reader-title {
- color: #FFFFFF !important;
- }
img {
max-width:100%;
height:auto;
diff --git a/qutebrowser/__init__.py b/qutebrowser/__init__.py
index c38435242..75d4e0532 100644
--- a/qutebrowser/__init__.py
+++ b/qutebrowser/__init__.py
@@ -26,7 +26,7 @@ __copyright__ = "Copyright 2014-2021 Florian Bruhin (The Compiler)"
__license__ = "GPL"
__maintainer__ = __author__
__email__ = "mail@qutebrowser.org"
-__version__ = "2.5.0"
+__version__ = "2.5.1"
__version_info__ = tuple(int(part) for part in __version__.split('.'))
__description__ = "A keyboard-driven, vim-like browser based on Python and Qt."
diff --git a/qutebrowser/browser/inspector.py b/qutebrowser/browser/inspector.py
index 5908890ba..0eafa0536 100644
--- a/qutebrowser/browser/inspector.py
+++ b/qutebrowser/browser/inspector.py
@@ -29,7 +29,7 @@ from PyQt5.QtCore import pyqtSignal, pyqtSlot, QObject, QEvent
from PyQt5.QtGui import QCloseEvent
from qutebrowser.browser import eventfilter
-from qutebrowser.config import configfiles
+from qutebrowser.config import configfiles, config
from qutebrowser.utils import log, usertypes
from qutebrowser.keyinput import modeman
from qutebrowser.misc import miscwidgets
@@ -137,7 +137,8 @@ class AbstractWebInspector(QWidget):
@pyqtSlot()
def _on_clicked(self) -> None:
"""Enter insert mode if a docked inspector was clicked."""
- if self._position != Position.window:
+ if (self._position != Position.window and
+ config.val.input.insert_mode.auto_enter):
modeman.enter(self._win_id, usertypes.KeyMode.insert,
reason='Inspector clicked', only_if_normal=True)
diff --git a/qutebrowser/browser/shared.py b/qutebrowser/browser/shared.py
index f195bbf28..3ea323d96 100644
--- a/qutebrowser/browser/shared.py
+++ b/qutebrowser/browser/shared.py
@@ -341,7 +341,8 @@ def get_user_stylesheet(searching=False):
'misc-mathml-darkmode' not in config.val.content.site_specific_quirks.skip):
# WORKAROUND for MathML-output on Wikipedia being black on black.
# See https://bugs.chromium.org/p/chromium/issues/detail?id=1126606
- css += '\nimg.mwe-math-fallback-image-inline { filter: invert(100%); }'
+ css += ('\nimg.mwe-math-fallback-image-inline, '
+ 'img.mwe-math-fallback-image-display { filter: invert(100%); }')
return css
diff --git a/qutebrowser/browser/webengine/notification.py b/qutebrowser/browser/webengine/notification.py
index e4ad4d763..3571bb24d 100644
--- a/qutebrowser/browser/webengine/notification.py
+++ b/qutebrowser/browser/webengine/notification.py
@@ -621,18 +621,19 @@ class HerbeNotificationAdapter(AbstractNotificationAdapter):
so there's no point.
"""
if status == QProcess.CrashExit:
- return
-
- if code == 0:
+ pass
+ elif code == 0:
self.click_id.emit(pid)
elif code == 2:
- self.close_id.emit(pid)
+ pass
else:
proc = self.sender()
assert isinstance(proc, QProcess), proc
stderr = proc.readAllStandardError()
raise Error(f'herbe exited with status {code}: {stderr}')
+ self.close_id.emit(pid)
+
@pyqtSlot(QProcess.ProcessError)
def _on_error(self, error: QProcess.ProcessError) -> None:
if error == QProcess.Crashed:
diff --git a/qutebrowser/browser/webengine/webenginesettings.py b/qutebrowser/browser/webengine/webenginesettings.py
index 0b25726c0..ad374ed9b 100644
--- a/qutebrowser/browser/webengine/webenginesettings.py
+++ b/qutebrowser/browser/webengine/webenginesettings.py
@@ -117,8 +117,6 @@ class WebEngineSettings(websettings.AbstractSettings):
Attr(QWebEngineSettings.JavascriptEnabled),
'content.javascript.can_open_tabs_automatically':
Attr(QWebEngineSettings.JavascriptCanOpenWindows),
- 'content.javascript.can_access_clipboard':
- Attr(QWebEngineSettings.JavascriptCanAccessClipboard),
'content.plugins':
Attr(QWebEngineSettings.PluginsEnabled),
'content.hyperlink_auditing':
@@ -199,26 +197,47 @@ class WebEngineSettings(websettings.AbstractSettings):
QWebEngineSettings.FantasyFont: QFont.Fantasy,
}
- def set_unknown_url_scheme_policy(
- self, policy: Union[str, usertypes.Unset]) -> bool:
- """Set the UnknownUrlSchemePolicy to use.
+ _JS_CLIPBOARD_SETTINGS = {
+ 'none': {
+ QWebEngineSettings.WebAttribute.JavascriptCanAccessClipboard: False,
+ QWebEngineSettings.WebAttribute.JavascriptCanPaste: False,
+ },
+ 'access': {
+ QWebEngineSettings.WebAttribute.JavascriptCanAccessClipboard: True,
+ QWebEngineSettings.WebAttribute.JavascriptCanPaste: False,
+ },
+ 'access-paste': {
+ QWebEngineSettings.WebAttribute.JavascriptCanAccessClipboard: True,
+ QWebEngineSettings.WebAttribute.JavascriptCanPaste: True,
+ },
+ }
- Return:
- True if there was a change, False otherwise.
- """
- old_value = self._settings.unknownUrlSchemePolicy()
+ def set_unknown_url_scheme_policy(
+ self, policy: Union[str, usertypes.Unset]) -> None:
+ """Set the UnknownUrlSchemePolicy to use."""
if isinstance(policy, usertypes.Unset):
self._settings.resetUnknownUrlSchemePolicy()
- new_value = self._settings.unknownUrlSchemePolicy()
else:
new_value = self._UNKNOWN_URL_SCHEME_POLICY[policy]
self._settings.setUnknownUrlSchemePolicy(new_value)
- return old_value != new_value
+
+ def _set_js_clipboard(self, value: Union[str, usertypes.Unset]) -> None:
+ attr_access = QWebEngineSettings.WebAttribute.JavascriptCanAccessClipboard
+ attr_paste = QWebEngineSettings.WebAttribute.JavascriptCanPaste
+
+ if isinstance(value, usertypes.Unset):
+ self._settings.resetAttribute(attr_access)
+ self._settings.resetAttribute(attr_paste)
+ else:
+ for attr, attr_val in self._JS_CLIPBOARD_SETTINGS[value].items():
+ self._settings.setAttribute(attr, attr_val)
def _update_setting(self, setting, value):
if setting == 'content.unknown_url_scheme_policy':
- return self.set_unknown_url_scheme_policy(value)
- return super()._update_setting(setting, value)
+ self.set_unknown_url_scheme_policy(value)
+ elif setting == 'content.javascript.clipboard':
+ self._set_js_clipboard(value)
+ super()._update_setting(setting, value)
def init_settings(self):
super().init_settings()
diff --git a/qutebrowser/browser/webengine/webenginetab.py b/qutebrowser/browser/webengine/webenginetab.py
index 15729ccdc..0a2333afc 100644
--- a/qutebrowser/browser/webengine/webenginetab.py
+++ b/qutebrowser/browser/webengine/webenginetab.py
@@ -93,9 +93,7 @@ class WebEnginePrinting(browsertab.AbstractPrinting):
def to_pdf(self, filename):
self._widget.page().printToPdf(filename)
- def to_printer(self, printer, callback=None):
- if callback is None:
- callback = lambda _ok: None
+ def to_printer(self, printer, callback=lambda ok: None):
self._widget.page().print(printer, callback)
diff --git a/qutebrowser/browser/webkit/http.py b/qutebrowser/browser/webkit/http.py
index d13471277..a38cd358a 100644
--- a/qutebrowser/browser/webkit/http.py
+++ b/qutebrowser/browser/webkit/http.py
@@ -89,13 +89,16 @@ class ContentDisposition:
try:
parsed = reg('Content-Disposition', decoded)
except IndexError: # pragma: no cover
- # WORKAROUND for https://bugs.python.org/issue37491
+ # WORKAROUND for https://github.com/python/cpython/issues/81672
# Fixed in Python 3.7.5 and 3.8.0.
# Still getting failures on 3.10 on CI though
raise ContentDispositionError("Missing closing quote character")
except ValueError: # pragma: no cover
- # WORKAROUND for https://bugs.python.org/issue42946
+ # WORKAROUND for https://github.com/python/cpython/issues/87112
raise ContentDispositionError("Non-ASCII digit")
+ except AttributeError:
+ # WORKAROUND for https://github.com/python/cpython/issues/93010
+ raise ContentDispositionError("Section number has an invalid leading 0")
if parsed.defects:
defects = list(parsed.defects)
diff --git a/qutebrowser/browser/webkit/webkitsettings.py b/qutebrowser/browser/webkit/webkitsettings.py
index cac6236ce..1348c849b 100644
--- a/qutebrowser/browser/webkit/webkitsettings.py
+++ b/qutebrowser/browser/webkit/webkitsettings.py
@@ -57,8 +57,9 @@ class WebKitSettings(websettings.AbstractSettings):
Attr(QWebSettings.JavascriptCanOpenWindows),
'content.javascript.can_close_tabs':
Attr(QWebSettings.JavascriptCanCloseWindows),
- 'content.javascript.can_access_clipboard':
- Attr(QWebSettings.JavascriptCanAccessClipboard),
+ 'content.javascript.clipboard':
+ Attr(QWebSettings.JavascriptCanAccessClipboard,
+ converter=lambda val: val != "none"),
'content.plugins':
Attr(QWebSettings.PluginsEnabled),
'content.webgl':
diff --git a/qutebrowser/commands/cmdexc.py b/qutebrowser/commands/cmdexc.py
index fdd06537f..50488f1e5 100644
--- a/qutebrowser/commands/cmdexc.py
+++ b/qutebrowser/commands/cmdexc.py
@@ -22,6 +22,9 @@
Defined here to avoid circular dependency hell.
"""
+from typing import List
+import difflib
+
class Error(Exception):
@@ -32,6 +35,24 @@ class NoSuchCommandError(Error):
"""Raised when a command isn't found."""
+ @classmethod
+ def for_cmd(cls, cmd: str, all_commands: List[str] = None) -> "NoSuchCommandError":
+ """Raise an exception for the given command."""
+ suffix = ''
+ if all_commands:
+ matches = difflib.get_close_matches(cmd, all_commands, n=1)
+ if matches:
+ suffix = f' (did you mean :{matches[0]}?)'
+ return cls(f"{cmd}: no such command{suffix}")
+
+
+class EmptyCommandError(NoSuchCommandError):
+
+ """Raised when no command was given."""
+
+ def __init__(self):
+ super().__init__("No command given")
+
class ArgumentTypeError(Error):
diff --git a/qutebrowser/commands/parser.py b/qutebrowser/commands/parser.py
index 06a20cdf6..5ef46f5e5 100644
--- a/qutebrowser/commands/parser.py
+++ b/qutebrowser/commands/parser.py
@@ -43,10 +43,18 @@ class CommandParser:
Attributes:
_partial_match: Whether to allow partial command matches.
+ _find_similar: Whether to find similar matches on unknown commands.
+ If we use this for completion, errors are not shown in the UI,
+ so we don't need to search.
"""
- def __init__(self, partial_match: bool = False) -> None:
+ def __init__(
+ self,
+ partial_match: bool = False,
+ find_similar: bool = False,
+ ) -> None:
self._partial_match = partial_match
+ self._find_similar = find_similar
def _get_alias(self, text: str, *, default: str) -> str:
"""Get an alias from the config.
@@ -95,7 +103,7 @@ class CommandParser:
"""
text = text.strip().lstrip(':').strip()
if not text:
- raise cmdexc.NoSuchCommandError("No command given")
+ raise cmdexc.EmptyCommandError
if aliases:
text = self._get_alias(text, default=text)
@@ -128,7 +136,7 @@ class CommandParser:
cmdstr, sep, argstr = text.partition(' ')
if not cmdstr:
- raise cmdexc.NoSuchCommandError("No command given")
+ raise cmdexc.EmptyCommandError
if self._partial_match:
cmdstr = self._completion_match(cmdstr)
@@ -136,7 +144,10 @@ class CommandParser:
try:
cmd = objects.commands[cmdstr]
except KeyError:
- raise cmdexc.NoSuchCommandError(f'{cmdstr}: no such command')
+ raise cmdexc.NoSuchCommandError.for_cmd(
+ cmdstr,
+ all_commands=list(objects.commands) if self._find_similar else [],
+ )
args = self._split_args(cmd, argstr, keep)
if keep and args:
diff --git a/qutebrowser/commands/runners.py b/qutebrowser/commands/runners.py
index 5fb054455..e3cd0cc97 100644
--- a/qutebrowser/commands/runners.py
+++ b/qutebrowser/commands/runners.py
@@ -138,9 +138,12 @@ class CommandRunner(AbstractCommandRunner):
_win_id: The window this CommandRunner is associated with.
"""
- def __init__(self, win_id, partial_match=False, parent=None):
+ def __init__(self, win_id, partial_match=False, find_similar=True, parent=None):
super().__init__(parent)
- self._parser = parser.CommandParser(partial_match=partial_match)
+ self._parser = parser.CommandParser(
+ partial_match=partial_match,
+ find_similar=find_similar,
+ )
self._win_id = win_id
@contextlib.contextmanager
diff --git a/qutebrowser/completion/completer.py b/qutebrowser/completion/completer.py
index b94077c6d..cf6984288 100644
--- a/qutebrowser/completion/completer.py
+++ b/qutebrowser/completion/completer.py
@@ -40,7 +40,8 @@ class CompletionInfo:
"""Context passed into all completion functions."""
config: config.Config
- keyconf: config.KeyConfig # pylint: disable=used-before-assignment
+ # pylint: disable-next=used-before-assignment
+ keyconf: config.KeyConfig # type: ignore[name-defined]
win_id: int
cur_tab: 'browsertab.AbstractTab'
@@ -164,6 +165,7 @@ class Completer(QObject):
# cursor is in a space between two existing words
parts.insert(i, '')
prefix = [x.strip() for x in parts[:i]]
+ # pylint: disable-next=unnecessary-list-index-lookup
center = parts[i].strip()
# strip trailing whitespace included as a separate token
postfix = [x.strip() for x in parts[i+1:] if not x.isspace()]
diff --git a/qutebrowser/completion/models/configmodel.py b/qutebrowser/completion/models/configmodel.py
index 7c8473b3f..6c85fbb29 100644
--- a/qutebrowser/completion/models/configmodel.py
+++ b/qutebrowser/completion/models/configmodel.py
@@ -44,16 +44,22 @@ def customized_option(*, info):
def list_option(*, info):
"""A CompletionModel filled with settings whose values are lists."""
- predicate = lambda opt: (isinstance(info.config.get_obj(opt.name),
- list) and not opt.no_autoconfig)
- return _option(info, "List options", predicate)
+ return _option(
+ info,
+ "List options",
+ lambda opt: (isinstance(info.config.get_obj(opt.name), list) and
+ not opt.no_autoconfig)
+ )
def dict_option(*, info):
"""A CompletionModel filled with settings whose values are dicts."""
- predicate = lambda opt: (isinstance(info.config.get_obj(opt.name),
- dict) and not opt.no_autoconfig)
- return _option(info, "Dict options", predicate)
+ return _option(
+ info,
+ "Dict options",
+ lambda opt: (isinstance(info.config.get_obj(opt.name), dict) and
+ not opt.no_autoconfig)
+ )
def _option(info, title, predicate):
diff --git a/qutebrowser/config/config.py b/qutebrowser/config/config.py
index 834709ae6..918c9b894 100644
--- a/qutebrowser/config/config.py
+++ b/qutebrowser/config/config.py
@@ -373,7 +373,11 @@ class Config(QObject):
deleted = name in configdata.MIGRATIONS.deleted
renamed = configdata.MIGRATIONS.renamed.get(name)
exception = configexc.NoOptionError(
- name, deleted=deleted, renamed=renamed)
+ name,
+ deleted=deleted,
+ renamed=renamed,
+ all_names=list(configdata.DATA),
+ )
raise exception from None
def ensure_has_opt(self, name: str) -> None:
diff --git a/qutebrowser/config/configdata.yml b/qutebrowser/config/configdata.yml
index 771cf0493..e91d9aaf1 100644
--- a/qutebrowser/config/configdata.yml
+++ b/qutebrowser/config/configdata.yml
@@ -869,9 +869,14 @@ content.javascript.alert:
type: Bool
desc: Show javascript alerts.
-content.javascript.can_access_clipboard:
- default: false
- type: Bool
+content.javascript.clipboard:
+ default: none
+ type:
+ name: String
+ valid_values:
+ - none: Disable access to clipboard.
+ - access: Allow reading from and writing to the clipboard.
+ - access-paste: Allow accessing the clipboard and pasting clipboard content.
supports_pattern: true
desc: >-
Allow JavaScript to read from or write to the clipboard.
@@ -1680,6 +1685,7 @@ hints.selectors:
- '[role="menuitem"]'
- '[role="menuitemcheckbox"]'
- '[role="menuitemradio"]'
+ - '[role="treeitem"]'
- '[ng-click]'
- '[ngClick]'
- '[data-ng-click]'
diff --git a/qutebrowser/config/configexc.py b/qutebrowser/config/configexc.py
index 27a1001b5..58e5ad67a 100644
--- a/qutebrowser/config/configexc.py
+++ b/qutebrowser/config/configexc.py
@@ -19,8 +19,9 @@
"""Exceptions related to config parsing."""
+import difflib
import dataclasses
-from typing import Any, Mapping, Optional, Sequence, Union
+from typing import Any, Mapping, Optional, Sequence, Union, List
from qutebrowser.utils import usertypes, log
@@ -91,6 +92,7 @@ class NoOptionError(Error):
"""Raised when an option was not found."""
def __init__(self, option: str, *,
+ all_names: List[str] = None,
deleted: bool = False,
renamed: str = None) -> None:
if deleted:
@@ -98,6 +100,12 @@ class NoOptionError(Error):
suffix = ' (this option was removed from qutebrowser)'
elif renamed is not None:
suffix = ' (this option was renamed to {!r})'.format(renamed)
+ elif all_names:
+ matches = difflib.get_close_matches(option, all_names, n=1)
+ if matches:
+ suffix = f' (did you mean {matches[0]!r}?)'
+ else:
+ suffix = ''
else:
suffix = ''
diff --git a/qutebrowser/config/configfiles.py b/qutebrowser/config/configfiles.py
index d97771fad..f2651d1cd 100644
--- a/qutebrowser/config/configfiles.py
+++ b/qutebrowser/config/configfiles.py
@@ -422,6 +422,12 @@ class YamlMigrations(QObject):
false_value='load-insecurely',
ask_value='ask',
)
+ self._migrate_renamed_bool(
+ old_name='content.javascript.can_access_clipboard',
+ new_name='content.javascript.clipboard',
+ true_value='access',
+ false_value='none',
+ )
for setting in ['colors.webpage.force_dark_color_scheme',
'colors.webpage.prefers_color_scheme_dark']:
diff --git a/qutebrowser/config/configtypes.py b/qutebrowser/config/configtypes.py
index d3d5e3fb8..97011b7cf 100644
--- a/qutebrowser/config/configtypes.py
+++ b/qutebrowser/config/configtypes.py
@@ -109,6 +109,8 @@ class ValidValues:
values: A list with the allowed untransformed values.
descriptions: A dict with value/desc mappings.
generate_docs: Whether to show the values in the docs.
+ others_permitted: Whether arbitrary values are permitted.
+ Used to show buttons in qute://settings.
"""
def __init__(
@@ -119,12 +121,14 @@ class ValidValues:
Tuple[str, Optional[str]],
],
generate_docs: bool = True,
+ others_permitted: bool = False
) -> None:
if not values:
raise ValueError("ValidValues with no values makes no sense!")
self.descriptions: DictType[str, str] = {}
self.values: ListType[str] = []
self.generate_docs = generate_docs
+ self.others_permitted = others_permitted
for value in values:
if isinstance(value, str):
# Value without description
@@ -1638,7 +1642,9 @@ class Proxy(BaseType):
super().__init__(none_ok=none_ok, completions=completions)
self.valid_values = ValidValues(
('system', "Use the system wide proxy."),
- ('none', "Don't use any proxy"))
+ ('none', "Don't use any proxy"),
+ others_permitted=True,
+ )
def to_py(
self,
diff --git a/qutebrowser/config/websettings.py b/qutebrowser/config/websettings.py
index 41aeec6a3..779ed0b6b 100644
--- a/qutebrowser/config/websettings.py
+++ b/qutebrowser/config/websettings.py
@@ -164,14 +164,10 @@ class AbstractSettings:
assert encoding is not usertypes.UNSET # unclear how to reset
self._settings.setDefaultTextEncoding(encoding)
- def _update_setting(self, setting: str, value: Any) -> bool:
+ def _update_setting(self, setting: str, value: Any) -> None:
"""Update the given setting/value.
- Unknown settings are ignored.
-
- Return:
- True if there was a change, False otherwise.
- """
+ Unknown settings are ignored."""
if setting in self._ATTRIBUTES:
self.set_attribute(setting, value)
elif setting in self._FONT_SIZES:
@@ -180,7 +176,6 @@ class AbstractSettings:
self.set_font_family(setting, value)
elif setting == 'content.default_encoding':
self.set_default_text_encoding(value)
- return False
def update_setting(self, setting: str) -> None:
"""Update the given setting."""
diff --git a/qutebrowser/html/settings.html b/qutebrowser/html/settings.html
index b06917fd5..f89aaa610 100644
--- a/qutebrowser/html/settings.html
+++ b/qutebrowser/html/settings.html
@@ -179,9 +179,10 @@ summary::selection {
</div>
{% endif %}
</td>
- {% if option.typ.valid_values is not none %}
+ {% set valid_values = option.typ.valid_values %}
+ {% if valid_values is not none and not valid_values.others_permitted %}
<td class="valid-value">
- {% for value in option.typ.valid_values.values %}
+ {% for value in valid_values.values %}
<div class="radio-button">
<input type="radio" id="input-{{ option.name }}-{{ loop.index0 }}"
name="{{ option.name }}" value="{{ value }}"
diff --git a/qutebrowser/mainwindow/mainwindow.py b/qutebrowser/mainwindow/mainwindow.py
index d7229bf31..b247da632 100644
--- a/qutebrowser/mainwindow/mainwindow.py
+++ b/qutebrowser/mainwindow/mainwindow.py
@@ -249,8 +249,8 @@ class MainWindow(QWidget):
log.init.debug("Initializing modes...")
modeman.init(win_id=self.win_id, parent=self)
- self._commandrunner = runners.CommandRunner(self.win_id,
- partial_match=True)
+ self._commandrunner = runners.CommandRunner(
+ self.win_id, partial_match=True, find_similar=True)
self._keyhint = keyhintwidget.KeyHintView(self.win_id, self)
self._add_overlay(self._keyhint, self._keyhint.update_geometry)
diff --git a/qutebrowser/misc/elf.py b/qutebrowser/misc/elf.py
index bf824880a..8fadbcffd 100644
--- a/qutebrowser/misc/elf.py
+++ b/qutebrowser/misc/elf.py
@@ -270,7 +270,7 @@ def _find_versions(data: bytes) -> Versions:
correctly: https://github.com/python/typeshed/issues/1467
"""
match = re.search(
- br'QtWebEngine/([0-9.]+) Chrome/([0-9.]+)',
+ br'\x00QtWebEngine/([0-9.]+) Chrome/([0-9.]+)\x00',
data,
)
if match is None:
diff --git a/qutebrowser/misc/throttle.py b/qutebrowser/misc/throttle.py
index ac565b68d..16a14d4bd 100644
--- a/qutebrowser/misc/throttle.py
+++ b/qutebrowser/misc/throttle.py
@@ -85,6 +85,7 @@ class Throttle(QObject):
(cur_time_ms - self._last_call_ms))
# Disconnect any existing calls, continue if no connections.
try:
+ # pylint: disable=no-value-for-parameter
self._timer.timeout.disconnect()
except TypeError:
pass
diff --git a/requirements.txt b/requirements.txt
index 228d637a1..129fe402a 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -2,7 +2,7 @@
adblock==0.5.2
colorama==0.4.4
-importlib-metadata==4.11.3 ; python_version=="3.7.*"
+importlib-metadata==4.11.4 ; python_version=="3.7.*"
importlib-resources==5.7.1 ; python_version=="3.7.*" or python_version=="3.8.*"
Jinja2==3.1.2
MarkupSafe==2.1.1
diff --git a/scripts/asciidoc2html.py b/scripts/asciidoc2html.py
index 6b4e3fb0d..ba8493247 100755
--- a/scripts/asciidoc2html.py
+++ b/scripts/asciidoc2html.py
@@ -43,7 +43,10 @@ class AsciiDoc:
"""Abstraction of an asciidoc subprocess."""
- FILES = ['faq', 'changelog', 'contributing', 'quickstart', 'userscripts']
+ FILES = [
+ 'faq', 'changelog', 'contributing', 'quickstart', 'userscripts',
+ 'install', 'stacktrace'
+ ]
def __init__(self,
asciidoc: Optional[str],
diff --git a/scripts/dev/changelog_urls.json b/scripts/dev/changelog_urls.json
index cc7a6f639..6b33f15ef 100644
--- a/scripts/dev/changelog_urls.json
+++ b/scripts/dev/changelog_urls.json
@@ -1,6 +1,7 @@
{
"pyparsing": "https://github.com/pyparsing/pyparsing/blob/master/CHANGES",
- "pylint": "https://pylint.pycqa.org/en/latest/whatsnew/changelog.html",
+ "pylint": "https://pylint.pycqa.org/en/latest/whatsnew/2/index.html",
+ "tomlkit": "https://github.com/sdispater/tomlkit/blob/master/CHANGELOG.md",
"dill": "https://github.com/uqfoundation/dill/commits/master",
"isort": "https://pycqa.github.io/isort/CHANGELOG/",
"lazy-object-proxy": "https://github.com/ionelmc/python-lazy-object-proxy/blob/master/CHANGELOG.rst",
@@ -13,7 +14,7 @@
"execnet": "https://execnet.readthedocs.io/en/latest/changelog.html",
"pytest-rerunfailures": "https://github.com/pytest-dev/pytest-rerunfailures/blob/master/CHANGES.rst",
"pytest-repeat": "https://github.com/pytest-dev/pytest-repeat/blob/master/CHANGES.rst",
- "requests": "https://github.com/psf/requests/blob/master/HISTORY.md",
+ "requests": "https://github.com/psf/requests/blob/main/HISTORY.md",
"requests-file": "https://github.com/dashea/requests-file/blob/master/CHANGES.rst",
"Werkzeug": "https://werkzeug.palletsprojects.com/en/latest/changes/",
"click": "https://click.palletsprojects.com/en/latest/changes/",
@@ -25,6 +26,7 @@
"Mako": "https://docs.makotemplates.org/en/latest/changelog.html",
"glob2": "https://github.com/miracle2k/python-glob2/blob/master/CHANGES",
"hypothesis": "https://hypothesis.readthedocs.io/en/latest/changes.html",
+ "exceptiongroup": "https://github.com/agronholm/exceptiongroup/blob/main/CHANGES.rst",
"mypy": "https://mypy-lang.blogspot.com/",
"types-PyYAML": "https://github.com/python/typeshed/commits/master/stubs/PyYAML",
"pytest": "https://docs.pytest.org/en/latest/changelog.html",
@@ -51,7 +53,6 @@
"flake8-deprecated": "https://github.com/gforcada/flake8-deprecated/blob/master/CHANGES.rst",
"flake8-future-import": "https://github.com/xZise/flake8-future-import#changes",
"flake8-mock": "https://github.com/aleGpereira/flake8-mock#changes",
- "flake8-polyfill": "https://gitlab.com/pycqa/flake8-polyfill/-/blob/master/CHANGELOG.rst",
"flake8-string-format": "https://github.com/xZise/flake8-string-format#changes",
"flake8-plugin-utils": "https://github.com/afonasev/flake8-plugin-utils#change-log",
"flake8-pytest-style": "https://github.com/m-burst/flake8-pytest-style#change-log",
@@ -119,9 +120,6 @@
"tldextract": "https://github.com/john-kurkowski/tldextract/blob/master/CHANGELOG.md",
"typing_extensions": "https://github.com/python/typing/blob/master/typing_extensions/CHANGELOG",
"diff-cover": "https://github.com/Bachmann1234/diff_cover/blob/master/CHANGELOG",
- "pytest-icdiff": "https://github.com/hjwp/pytest-icdiff/blob/master/HISTORY.rst",
- "icdiff": "https://github.com/jeffkaufman/icdiff/blob/master/ChangeLog",
- "pprintpp": "https://github.com/wolever/pprintpp/blob/master/CHANGELOG.txt",
"beautifulsoup4": "https://bazaar.launchpad.net/~leonardr/beautifulsoup/bs4/view/head:/CHANGELOG",
"check-manifest": "https://github.com/mgedmin/check-manifest/blob/master/CHANGES.rst",
"yamllint": "https://github.com/adrienverge/yamllint/blob/master/CHANGELOG.rst",
diff --git a/scripts/dev/run_pylint_on_tests.py b/scripts/dev/run_pylint_on_tests.py
index 28c6e32c9..e044de976 100644
--- a/scripts/dev/run_pylint_on_tests.py
+++ b/scripts/dev/run_pylint_on_tests.py
@@ -64,6 +64,8 @@ def main():
'import-error',
# tests/helpers imports
'wrong-import-order',
+ # __tracebackhide__
+ 'unnecessary-lambda-assignment',
]
toxinidir = sys.argv[1]
diff --git a/scripts/dev/src2asciidoc.py b/scripts/dev/src2asciidoc.py
index febd2bf8a..1267a278a 100755
--- a/scripts/dev/src2asciidoc.py
+++ b/scripts/dev/src2asciidoc.py
@@ -176,13 +176,15 @@ def _get_setting_quickref():
def _get_configtypes():
"""Get configtypes classes to document."""
- predicate = lambda e: (
- inspect.isclass(e) and
- # pylint: disable=protected-access
- e not in [configtypes.BaseType, configtypes.MappingType,
- configtypes._Numeric, configtypes.FontBase] and
- # pylint: enable=protected-access
- issubclass(e, configtypes.BaseType))
+ def predicate(e):
+ return (
+ inspect.isclass(e) and
+ # pylint: disable=protected-access
+ e not in [configtypes.BaseType, configtypes.MappingType,
+ configtypes._Numeric, configtypes.FontBase] and
+ # pylint: enable=protected-access
+ issubclass(e, configtypes.BaseType)
+ )
yield from inspect.getmembers(configtypes, predicate)
diff --git a/tests/end2end/data/darkmode/mathml-display.html b/tests/end2end/data/darkmode/mathml-display.html
new file mode 100644
index 000000000..f0c280cca
--- /dev/null
+++ b/tests/end2end/data/darkmode/mathml-display.html
@@ -0,0 +1,23 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <meta charset="utf-8">
+ <title>MathML-like SVG</title>
+ </head>
+ <body style="margin: 0; background-color: #ffff99">
+ <!--
+ Image based on: https://en.wikipedia.org/wiki/Pythagorean_theorem
+ with a black square added for testing.
+
+ onload based on:
+ https://stackoverflow.com/questions/53423742/waiting-for-an-image-to-finish-rendering
+ -->
+ <img
+ class="mwe-math-fallback-image-display"
+ src="mathml.svg"
+ alt="Pythagorean theorem"
+ onload="requestAnimationFrame(() => requestAnimationFrame(() => console.log('Image loaded')));"
+ >
+ <!-- -->
+ </body>
+</html>
diff --git a/tests/end2end/data/darkmode/mathml.html b/tests/end2end/data/darkmode/mathml-inline.html
index fa2371638..fa2371638 100644
--- a/tests/end2end/data/darkmode/mathml.html
+++ b/tests/end2end/data/darkmode/mathml-inline.html
diff --git a/tests/end2end/features/misc.feature b/tests/end2end/features/misc.feature
index e6a02e038..bd8ada576 100644
--- a/tests/end2end/features/misc.feature
+++ b/tests/end2end/features/misc.feature
@@ -389,7 +389,7 @@ Feature: Various utility commands.
Scenario: Partial commandline matching with startup command
When I run :message-i "Hello World" (invalid command)
- Then the error "message-i: no such command" should be shown
+ Then the error "message-i: no such command (did you mean :message-info?)" should be shown
Scenario: Multiple leading : in command
When I run :::::set-cmd-text ::::message-i "Hello World"
diff --git a/tests/end2end/test_invocations.py b/tests/end2end/test_invocations.py
index 0d49ff109..5a34d0357 100644
--- a/tests/end2end/test_invocations.py
+++ b/tests/end2end/test_invocations.py
@@ -723,7 +723,8 @@ def test_dark_mode(webengine_versions, quteproc_new, request,
)
-def test_dark_mode_mathml(quteproc_new, request, qtbot):
+@pytest.mark.parametrize("suffix", ["inline", "display"])
+def test_dark_mode_mathml(quteproc_new, request, qtbot, suffix):
if not request.config.webengine:
pytest.skip("Skipped with QtWebKit")
@@ -734,7 +735,7 @@ def test_dark_mode_mathml(quteproc_new, request, qtbot):
]
quteproc_new.start(args)
- quteproc_new.open_path('data/darkmode/mathml.html')
+ quteproc_new.open_path(f'data/darkmode/mathml-{suffix}.html')
quteproc_new.wait_for_js('Image loaded')
# First make sure loading finished by looking outside of the image
diff --git a/tests/unit/browser/webkit/http/test_http.py b/tests/unit/browser/webkit/http/test_http.py
index 4db78f4ff..d50f1c277 100644
--- a/tests/unit/browser/webkit/http/test_http.py
+++ b/tests/unit/browser/webkit/http/test_http.py
@@ -44,6 +44,21 @@ def test_no_content_disposition(stubs, url, expected):
assert filename == expected
+@pytest.mark.parametrize('value', [
+ # https://github.com/python/cpython/issues/87112
+ 'inline; 0*²'.encode("iso-8859-1"),
+ # https://github.com/python/cpython/issues/81672
+ b'"',
+ # https://github.com/python/cpython/issues/93010
+ b'attachment; 0*00="foo"',
+ # FIXME: Should probably have more tests if this is still relevant after
+ # dropping QtWebKit.
+])
+def test_parse_content_disposition_invalid(value):
+ with pytest.raises(http.ContentDispositionError):
+ http.ContentDisposition.parse(value)
+
+
@pytest.mark.parametrize('template', [
'{}',
'attachment; filename="{}"',
diff --git a/tests/unit/browser/webkit/test_webkitelem.py b/tests/unit/browser/webkit/test_webkitelem.py
index f7cc3e8c2..7df91922d 100644
--- a/tests/unit/browser/webkit/test_webkitelem.py
+++ b/tests/unit/browser/webkit/test_webkitelem.py
@@ -195,6 +195,7 @@ class SelectionAndFilterTests:
('<p role="menuitem" foo="bar"/>', ['all']),
('<p role="menuitemcheckbox" foo="bar"/>', ['all']),
('<p role="menuitemradio" foo="bar"/>', ['all']),
+ ('<p role="treeitem" foo="bar"/>', ['all']),
('<p role="button" foo="bar"/>', ['all']),
('<p role="button" href="bar"/>', ['all', 'url']),
diff --git a/tests/unit/commands/test_cmdexc.py b/tests/unit/commands/test_cmdexc.py
new file mode 100644
index 000000000..f80131e29
--- /dev/null
+++ b/tests/unit/commands/test_cmdexc.py
@@ -0,0 +1,40 @@
+# vim: ft=python fileencoding=utf-8 sts=4 sw=4 et:
+
+# Copyright 2022 Florian Bruhin (The Compiler) <mail@qutebrowser.org>
+#
+# This file is part of qutebrowser.
+#
+# qutebrowser is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# qutebrowser is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with qutebrowser. If not, see <https://www.gnu.org/licenses/>.
+
+"""Tests for qutebrowser.commands.cmdexc."""
+
+import re
+import pytest
+
+from qutebrowser.commands import cmdexc
+
+
+def test_empty_command_error():
+ with pytest.raises(cmdexc.NoSuchCommandError, match="No command given"):
+ raise cmdexc.EmptyCommandError
+
+
+@pytest.mark.parametrize("all_commands, msg", [
+ ([], "testcmd: no such command"),
+ (["fastcmd"], "testcmd: no such command (did you mean :fastcmd?)"),
+ (["thisdoesnotmatch"], "testcmd: no such command"),
+])
+def test_no_such_command_error(all_commands, msg):
+ with pytest.raises(cmdexc.NoSuchCommandError, match=re.escape(msg)):
+ raise cmdexc.NoSuchCommandError.for_cmd("testcmd", all_commands=all_commands)
diff --git a/tests/unit/commands/test_parser.py b/tests/unit/commands/test_parser.py
index b851ad3b0..a0c2fe8f3 100644
--- a/tests/unit/commands/test_parser.py
+++ b/tests/unit/commands/test_parser.py
@@ -19,6 +19,8 @@
"""Tests for qutebrowser.commands.parser."""
+import re
+
import pytest
from qutebrowser.misc import objects
@@ -64,7 +66,7 @@ class TestCommandParser:
and https://github.com/qutebrowser/qutebrowser/issues/1773
"""
p = parser.CommandParser()
- with pytest.raises(cmdexc.NoSuchCommandError):
+ with pytest.raises(cmdexc.EmptyCommandError):
p.parse_all(command)
@pytest.mark.parametrize('command, name, args', [
@@ -135,3 +137,13 @@ class TestCompletions:
result = p.parse('tw')
assert result.cmd.name == 'two'
+
+
+@pytest.mark.parametrize("find_similar, msg", [
+ (True, "tabfocus: no such command (did you mean :tab-focus?)"),
+ (False, "tabfocus: no such command"),
+])
+def test_find_similar(find_similar, msg):
+ p = parser.CommandParser(find_similar=find_similar)
+ with pytest.raises(cmdexc.NoSuchCommandError, match=re.escape(msg)):
+ p.parse_all("tabfocus", aliases=False)
diff --git a/tests/unit/config/test_config.py b/tests/unit/config/test_config.py
index b88bc2f8d..ab1fba789 100644
--- a/tests/unit/config/test_config.py
+++ b/tests/unit/config/test_config.py
@@ -782,7 +782,7 @@ class TestContainer:
assert len(configapi.errors) == 1
error = configapi.errors[0]
assert error.text == "While getting 'tabs.foobar'"
- assert str(error.exception) == "No option 'tabs.foobar'"
+ assert str(error.exception).startswith("No option 'tabs.foobar'")
def test_confapi_missing_prefix(self, container):
configapi = types.SimpleNamespace(errors=[])
@@ -793,7 +793,7 @@ class TestContainer:
error1 = configapi.errors[0]
assert error1.text == "While getting 'content.host_blocking'"
- assert str(error1.exception) == "No option 'content.host_blocking'"
+ assert str(error1.exception).startswith("No option 'content.host_blocking'")
error2 = configapi.errors[1]
assert error2.text == "While setting 'content.host_blocking.lists'"
diff --git a/tests/unit/config/test_configexc.py b/tests/unit/config/test_configexc.py
index a236494f9..b289d3cc8 100644
--- a/tests/unit/config/test_configexc.py
+++ b/tests/unit/config/test_configexc.py
@@ -32,13 +32,27 @@ def test_validation_error():
assert str(e) == "Invalid value 'val' - msg"
-@pytest.mark.parametrize('deleted, renamed, expected', [
- (False, None, "No option 'opt'"),
- (True, None, "No option 'opt' (this option was removed from qutebrowser)"),
- (False, 'new', "No option 'opt' (this option was renamed to 'new')"),
+@pytest.mark.parametrize('deleted, renamed, all_names, expected', [
+ (False, None, [], "No option 'opt'"),
+ (True, None, [], "No option 'opt' (this option was removed from qutebrowser)"),
+ (False, 'new', [], "No option 'opt' (this option was renamed to 'new')"),
+ (False, None, ["opto"], "No option 'opt' (did you mean 'opto'?)"),
+ (False, None, ["thisdoesnotmatch"], "No option 'opt'"),
+ (
+ True,
+ None,
+ ["opto"],
+ "No option 'opt' (this option was removed from qutebrowser)",
+ ),
+ (False, 'new', ["opto"], "No option 'opt' (this option was renamed to 'new')"),
])
-def test_no_option_error(deleted, renamed, expected):
- e = configexc.NoOptionError('opt', deleted=deleted, renamed=renamed)
+def test_no_option_error(deleted, renamed, all_names, expected):
+ e = configexc.NoOptionError(
+ 'opt',
+ deleted=deleted,
+ renamed=renamed,
+ all_names=all_names,
+ )
assert e.option == 'opt'
assert str(e) == expected
diff --git a/tests/unit/config/test_configtypes.py b/tests/unit/config/test_configtypes.py
index c34efce54..e4351d619 100644
--- a/tests/unit/config/test_configtypes.py
+++ b/tests/unit/config/test_configtypes.py
@@ -223,6 +223,7 @@ class TestAll:
"""Patch aliases so Command works."""
config_stub.val.aliases = {}
+ # pylint: disable-next=too-many-function-args
@pytest.fixture(params=list(gen_classes()))
def klass(self, request):
return request.param
diff --git a/tests/unit/misc/test_elf.py b/tests/unit/misc/test_elf.py
index 86060bbde..7d3248da2 100644
--- a/tests/unit/misc/test_elf.py
+++ b/tests/unit/misc/test_elf.py
@@ -75,6 +75,23 @@ def test_result(qapp, caplog):
assert ua.upstream_browser_version == versions.chromium
+@pytest.mark.parametrize("data, expected", [
+ # Simple match
+ (
+ b"\x00QtWebEngine/5.15.9 Chrome/87.0.4280.144\x00",
+ elf.Versions("5.15.9", "87.0.4280.144"),
+ ),
+ # Ignoring garbage string-like data
+ (
+ b"\x00QtWebEngine/5.15.9 Chrome/87.0.4xternalclearkey\x00\x00"
+ b"QtWebEngine/5.15.9 Chrome/87.0.4280.144\x00",
+ elf.Versions("5.15.9", "87.0.4280.144"),
+ ),
+])
+def test_find_versions(data, expected):
+ assert elf._find_versions(data) == expected
+
+
@hypothesis.given(data=hst.builds(
lambda *a: b''.join(a),
hst.sampled_from([b'', b'\x7fELF', b'\x7fELF\x02\x01\x01']),
diff --git a/tests/unit/utils/test_log.py b/tests/unit/utils/test_log.py
index bbc6b02db..20938d6fb 100644
--- a/tests/unit/utils/test_log.py
+++ b/tests/unit/utils/test_log.py
@@ -236,6 +236,7 @@ class TestInitLog:
"""Tests for init_log."""
def _get_default_args(self):
+ # pylint: disable-next=unused-variable
return argparse.Namespace(debug=True, loglevel='debug', color=True,
loglines=10, logfilter=None,
force_color=False, json_logging=False,
diff --git a/tests/unit/utils/test_utils.py b/tests/unit/utils/test_utils.py
index 4620c2198..595aa6426 100644
--- a/tests/unit/utils/test_utils.py
+++ b/tests/unit/utils/test_utils.py
@@ -603,6 +603,15 @@ class TestSanitizeFilename:
LONG_EXTENSION = (LONG_FILENAME.replace("filename", ".extension")
.replace(".txt", ""))
+ # first four-byte unicode char
+ U10K = "\U00010000"
+
+ LONG_4BYTE = U10K * 64
+ LONG_4BYTE_SHORTENED = U10K * 60
+
+ LONG_4BYTE_EXT = f"{U10K * 8}.{U10K * 64}"
+ LONG_4BYTE_EXT_SHORTENED = f"{U10K}.{U10K * 59}"
+
@pytest.mark.parametrize('inp, expected', [
pytest.param('normal.txt', 'normal.txt',
marks=pytest.mark.fake_os('windows')),
@@ -629,6 +638,14 @@ class TestSanitizeFilename:
LONG_EXTENSION.replace("this is a very long .extension",
"this .extension"),
),
+ (
+ LONG_4BYTE,
+ LONG_4BYTE_SHORTENED,
+ ),
+ (
+ LONG_4BYTE_EXT,
+ LONG_4BYTE_EXT_SHORTENED,
+ )
])
@pytest.mark.linux
def test_shorten(self, inp, expected):
diff --git a/tests/unit/utils/test_version.py b/tests/unit/utils/test_version.py
index 7b616d8b7..64df0ece2 100644
--- a/tests/unit/utils/test_version.py
+++ b/tests/unit/utils/test_version.py
@@ -1463,7 +1463,11 @@ def test_uptime(monkeypatch, qapp):
monkeypatch.setattr(qapp, "launch_time", launch_time, raising=False)
class FakeDateTime(datetime.datetime):
- now = lambda x=datetime.datetime(1, 1, 1, 1, 1, 1, 2): x
+
+ @classmethod
+ def now(cls, tz=None):
+ return datetime.datetime(1, 1, 1, 1, 1, 1, 2)
+
monkeypatch.setattr(datetime, 'datetime', FakeDateTime)
uptime_delta = version._uptime()