summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authortoofar <toofar@spalge.com>2024-04-21 17:31:39 +1200
committertoofar <toofar@spalge.com>2024-04-21 17:31:39 +1200
commit9bf2bae60f6daf631729cb901c845e6c48da4778 (patch)
tree8dfbd390b2ad17bd303c45609a124f7be75fe56b
parent7ab4346e501b2011a4be1c5d584008833c6aad6f (diff)
parent80931acab0cc63760dfc484820b783aaced439b6 (diff)
downloadqutebrowser-tree-tabs-integration.tar.gz
qutebrowser-tree-tabs-integration.zip
Merge remote-tracking branch 'upstream/main' into tree-tabs-integrationtree-tabs-integration
-rw-r--r--.flake82
-rw-r--r--.github/workflows/ci.yml6
-rw-r--r--.github/workflows/docker.yml1
-rw-r--r--.github/workflows/nightly.yml4
-rw-r--r--.github/workflows/recompile-requirements.yml2
-rw-r--r--.github/workflows/release.yml2
-rw-r--r--.mypy.ini4
-rw-r--r--doc/changelog.asciidoc22
-rw-r--r--doc/help/settings.asciidoc5
-rw-r--r--doc/install.asciidoc6
-rw-r--r--misc/requirements/requirements-check-manifest.txt8
-rw-r--r--misc/requirements/requirements-dev.txt47
-rw-r--r--misc/requirements/requirements-flake8.txt6
-rw-r--r--misc/requirements/requirements-mypy.txt28
-rw-r--r--misc/requirements/requirements-pyinstaller.txt10
-rw-r--r--misc/requirements/requirements-pylint.txt24
-rw-r--r--misc/requirements/requirements-pyqt-6.6.txt4
-rw-r--r--misc/requirements/requirements-pyqt-6.txt4
-rw-r--r--misc/requirements/requirements-pyqt.txt4
-rw-r--r--misc/requirements/requirements-pyroma.txt16
-rw-r--r--misc/requirements/requirements-qutebrowser.txt-raw4
-rw-r--r--misc/requirements/requirements-sphinx.txt18
-rw-r--r--misc/requirements/requirements-tests.txt59
-rw-r--r--misc/requirements/requirements-tox.txt20
-rw-r--r--misc/requirements/requirements-vulture.txt4
-rw-r--r--misc/requirements/requirements-yamllint.txt2
-rwxr-xr-xmisc/userscripts/tor_identity2
-rw-r--r--pytest.ini9
-rw-r--r--qutebrowser/app.py3
-rw-r--r--qutebrowser/browser/browsertab.py10
-rw-r--r--qutebrowser/browser/network/proxy.py4
-rw-r--r--qutebrowser/browser/qtnetworkdownloads.py4
-rw-r--r--qutebrowser/browser/qutescheme.py4
-rw-r--r--qutebrowser/browser/webengine/webenginesettings.py2
-rw-r--r--qutebrowser/browser/webengine/webenginetab.py2
-rw-r--r--qutebrowser/browser/webengine/webview.py4
-rw-r--r--qutebrowser/browser/webkit/httpheaders.py (renamed from qutebrowser/browser/webkit/http.py)0
-rw-r--r--qutebrowser/browser/webkit/webpage.py6
-rw-r--r--qutebrowser/commands/runners.py4
-rw-r--r--qutebrowser/completion/completionwidget.py3
-rw-r--r--qutebrowser/config/config.py2
-rw-r--r--qutebrowser/config/configdata.yml3
-rw-r--r--qutebrowser/config/configinit.py3
-rw-r--r--qutebrowser/config/websettings.py2
-rw-r--r--qutebrowser/extensions/loader.py2
-rw-r--r--qutebrowser/javascript/caret.js19
-rw-r--r--qutebrowser/keyinput/keyutils.py2
-rw-r--r--qutebrowser/misc/consolewidget.py4
-rw-r--r--qutebrowser/misc/crashsignal.py29
-rw-r--r--qutebrowser/misc/ipc.py2
-rw-r--r--qutebrowser/misc/nativeeventfilter.py2
-rw-r--r--qutebrowser/qt/machinery.py8
-rw-r--r--qutebrowser/qutebrowser.py3
-rw-r--r--qutebrowser/utils/log.py4
-rw-r--r--qutebrowser/utils/qtlog.py2
-rw-r--r--qutebrowser/utils/qtutils.py11
-rw-r--r--qutebrowser/utils/resources.py4
-rw-r--r--qutebrowser/utils/standarddir.py4
-rw-r--r--qutebrowser/utils/utils.py4
-rw-r--r--qutebrowser/utils/version.py33
-rw-r--r--requirements.txt8
-rwxr-xr-xscripts/dev/build_release.py3
-rw-r--r--scripts/dev/changelog_urls.json27
-rw-r--r--scripts/dev/check_coverage.py4
-rwxr-xr-xscripts/importer.py2
-rw-r--r--tests/end2end/data/click_element.html2
-rw-r--r--tests/end2end/data/insert_mode_settings/html/autofocus.html3
-rw-r--r--tests/end2end/features/caret.feature4
-rw-r--r--tests/end2end/features/conftest.py11
-rw-r--r--tests/end2end/features/misc.feature5
-rw-r--r--tests/end2end/features/utilcmds.feature1
-rw-r--r--tests/end2end/fixtures/quteprocess.py20
-rw-r--r--tests/end2end/fixtures/webserver.py20
-rw-r--r--tests/end2end/test_insert_mode.py1
-rw-r--r--tests/unit/browser/test_caret.py10
-rw-r--r--tests/unit/browser/webkit/http/test_content_disposition.py10
-rw-r--r--tests/unit/browser/webkit/http/test_httpheaders.py (renamed from tests/unit/browser/webkit/http/test_http.py)20
-rw-r--r--tests/unit/browser/webkit/test_certificateerror.py17
-rw-r--r--tests/unit/config/test_configtypes.py20
-rw-r--r--tests/unit/misc/test_crashsignal.py104
-rw-r--r--tests/unit/utils/test_qtutils.py35
-rw-r--r--tests/unit/utils/test_version.py25
-rw-r--r--tox.ini6
83 files changed, 601 insertions, 269 deletions
diff --git a/.flake8 b/.flake8
index 01c259784..6c4dd923e 100644
--- a/.flake8
+++ b/.flake8
@@ -70,6 +70,4 @@ per-file-ignores =
copyright-check = True
copyright-regexp = # Copyright [\d-]+ .*
copyright-min-file-size = 110
-pytest-fixture-no-parentheses = True
-pytest-mark-no-parentheses = True
pytest-parametrize-names-type = csv
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index fe1bbefe7..599ba3b1b 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -38,7 +38,7 @@ jobs:
- uses: actions/checkout@v4
with:
persist-credentials: false
- - uses: actions/cache@v3
+ - uses: actions/cache@v4
with:
path: |
.mypy_cache
@@ -56,7 +56,7 @@ jobs:
run: "python scripts/dev/ci/problemmatchers.py ${{ matrix.testenv }} ${{ runner.temp }}"
- name: Install dependencies
run: |
- [[ ${{ matrix.testenv }} == eslint ]] && npm install -g eslint
+ [[ ${{ matrix.testenv }} == eslint ]] && npm install -g 'eslint@<9.0.0'
[[ ${{ matrix.testenv }} == docs ]] && sudo apt-get update && sudo apt-get install --no-install-recommends asciidoc libegl1-mesa
[[ ${{ matrix.testenv }} == vulture || ${{ matrix.testenv }} == pylint ]] && sudo apt-get update && sudo apt-get install --no-install-recommends libegl1-mesa
if [[ ${{ matrix.testenv }} == shellcheck ]]; then
@@ -184,7 +184,7 @@ jobs:
- uses: actions/checkout@v4
with:
persist-credentials: false
- - uses: actions/cache@v3
+ - uses: actions/cache@v4
with:
path: |
.mypy_cache
diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml
index 3762e6f48..9dc925e29 100644
--- a/.github/workflows/docker.yml
+++ b/.github/workflows/docker.yml
@@ -10,6 +10,7 @@ jobs:
if: "github.repository == 'qutebrowser/qutebrowser'"
runs-on: ubuntu-20.04
strategy:
+ fail-fast: false
matrix:
image:
- archlinux-webkit
diff --git a/.github/workflows/nightly.yml b/.github/workflows/nightly.yml
index 764bcae31..b326c2ad6 100644
--- a/.github/workflows/nightly.yml
+++ b/.github/workflows/nightly.yml
@@ -59,7 +59,9 @@ jobs:
- name: Patch qutebrowser for debugging
if: "contains(matrix.args, '--debug')"
run: |
- sed -i '' '/.-d., .--debug.,/s/$/ default=True,/' qutebrowser/qutebrowser.py
+ sed '/.-d., .--debug.,/s/$/ default=True,/' qutebrowser/qutebrowser.py > qutebrowser/qutebrowser.py.tmp
+ cp qutebrowser/qutebrowser.py.tmp qutebrowser/qutebrowser.py
+ rm qutebrowser/qutebrowser.py.tmp
- name: Run tox
run: "tox -e ${{ matrix.toxenv }} -- --gh-token ${{ secrets.GITHUB_TOKEN }} ${{ matrix.args }}"
- name: Gather info
diff --git a/.github/workflows/recompile-requirements.yml b/.github/workflows/recompile-requirements.yml
index 1e5637a10..6d42c3137 100644
--- a/.github/workflows/recompile-requirements.yml
+++ b/.github/workflows/recompile-requirements.yml
@@ -41,7 +41,7 @@ jobs:
- name: Run qutebrowser smoke test
run: "xvfb-run .venv/bin/python3 -m qutebrowser --no-err-windows --nowindow --temp-basedir about:blank ':later 500 quit'"
- name: Create pull request
- uses: peter-evans/create-pull-request@v5
+ uses: peter-evans/create-pull-request@v6
with:
committer: qutebrowser bot <bot@qutebrowser.org>
author: qutebrowser bot <bot@qutebrowser.org>
diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
index 9a751591f..aa8b3b2ef 100644
--- a/.github/workflows/release.yml
+++ b/.github/workflows/release.yml
@@ -117,7 +117,7 @@ jobs:
git push --set-upstream origin v${{ steps.bump.outputs.version_x }}
- name: Create GitHub draft release
id: create-release
- uses: softprops/action-gh-release@v1
+ uses: softprops/action-gh-release@v2
with:
tag_name: v${{ steps.bump.outputs.version }}
draft: true
diff --git a/.mypy.ini b/.mypy.ini
index ef34dbada..81f69a09e 100644
--- a/.mypy.ini
+++ b/.mypy.ini
@@ -22,13 +22,15 @@ disallow_any_unimported = True
enable_error_code = ignore-without-code
### Output
-show_error_codes = True
show_error_context = True
pretty = True
### FIXME:v4 get rid of this
no_implicit_optional = False
+### Future default behavior
+local_partial_types = True
+
[mypy-hunter]
# https://github.com/ionelmc/python-hunter/issues/43
ignore_missing_imports = True
diff --git a/doc/changelog.asciidoc b/doc/changelog.asciidoc
index 91b02b0da..b6385b015 100644
--- a/doc/changelog.asciidoc
+++ b/doc/changelog.asciidoc
@@ -19,12 +19,34 @@ breaking changes (such as renamed commands) can happen in minor releases.
v3.2.0 (unreleased)
-------------------
+Added
+~~~~~
+
+- When qutebrowser receives a SIGHUP it will now reload any config.py file
+ in use (same as the `:config-source` command does). (#8108)
+- The Chromium security patch version is now shown in the backend string in
+ --version and :version. This reflects the latest Chromium version that
+ security fixes have been backported to the base QtWebEngine version from.
+ (#7187)
+
Changed
~~~~~~~
- A few more completions will now match search terms in any order:
`:quickmark-*`, `:bookmark-*`, `:tab-take` and `:tab-select` (for the quick
and bookmark categories). (#7955)
+- Elements with an ARIA `role="switch"` now get hints (toggle switches like
+ e.g. on cookie banners).
+- The `tor_identity` userscript now validates that the -c|--control-port
+ argument value is an int. (#8162)
+
+Fixed
+~~~~~
+
+- `input.insert_mode.auto_load` sometimes not triggering due to a race
+ condition.
+- Worked around qutebrowser quitting when closing a KDE file dialog due to a Qt
+ bug.
[[v3.1.1]]
v3.1.1 (unreleased)
diff --git a/doc/help/settings.asciidoc b/doc/help/settings.asciidoc
index 3ff98a9bf..73646e541 100644
--- a/doc/help/settings.asciidoc
+++ b/doc/help/settings.asciidoc
@@ -115,7 +115,7 @@
|<<colors.tooltip.bg,colors.tooltip.bg>>|Background color of tooltips.
|<<colors.tooltip.fg,colors.tooltip.fg>>|Foreground color of tooltips.
|<<colors.webpage.bg,colors.webpage.bg>>|Background color for webpages if unset (or empty to use the theme's color).
-|<<colors.webpage.darkmode.algorithm,colors.webpage.darkmode.algorithm>>|Which algorithm to use for modifying how colors are rendered with darkmode.
+|<<colors.webpage.darkmode.algorithm,colors.webpage.darkmode.algorithm>>|Which algorithm to use for modifying how colors are rendered with dark mode.
|<<colors.webpage.darkmode.contrast,colors.webpage.darkmode.contrast>>|Contrast for dark mode.
|<<colors.webpage.darkmode.enabled,colors.webpage.darkmode.enabled>>|Render all web contents using a dark theme.
|<<colors.webpage.darkmode.policy.images,colors.webpage.darkmode.policy.images>>|Which images to apply dark mode to.
@@ -1662,7 +1662,7 @@ Default: +pass:[white]+
[[colors.webpage.darkmode.algorithm]]
=== colors.webpage.darkmode.algorithm
-Which algorithm to use for modifying how colors are rendered with darkmode.
+Which algorithm to use for modifying how colors are rendered with dark mode.
The `lightness-cielab` value was added with QtWebEngine 5.14 and is treated like `lightness-hsl` with older QtWebEngine versions.
This setting requires a restart.
@@ -3493,6 +3493,7 @@ Default:
* +pass:[[role=&quot;button&quot;\]]+
* +pass:[[role=&quot;tab&quot;\]]+
* +pass:[[role=&quot;checkbox&quot;\]]+
+* +pass:[[role=&quot;switch&quot;\]]+
* +pass:[[role=&quot;menuitem&quot;\]]+
* +pass:[[role=&quot;menuitemcheckbox&quot;\]]+
* +pass:[[role=&quot;menuitemradio&quot;\]]+
diff --git a/doc/install.asciidoc b/doc/install.asciidoc
index ec53e2fb5..98cc6fb05 100644
--- a/doc/install.asciidoc
+++ b/doc/install.asciidoc
@@ -280,14 +280,14 @@ track down issues.
NOTE: Due to GitHub limitations, you need to be signed in with a GitHub account
to download the files.
-https://chocolatey.org/packages/qutebrowser[Chocolatey package]
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+Package managers
+~~~~~~~~~~~~~~~~
* PackageManagement PowerShell module
----
PS C:\> Install-Package qutebrowser
----
-* Chocolatey's client
+* https://chocolatey.org/packages/qutebrowser[Chocolatey package] with `choco`:
----
C:\> choco install qutebrowser
----
diff --git a/misc/requirements/requirements-check-manifest.txt b/misc/requirements/requirements-check-manifest.txt
index 0d250cf3c..b0993ea58 100644
--- a/misc/requirements/requirements-check-manifest.txt
+++ b/misc/requirements/requirements-check-manifest.txt
@@ -1,9 +1,9 @@
# This file is automatically generated by scripts/dev/recompile_requirements.py
-build==1.0.3
+build==1.2.1
check-manifest==0.49
-importlib-metadata==7.0.1
-packaging==23.2
+importlib_metadata==7.1.0
+packaging==24.0
pyproject_hooks==1.0.0
tomli==2.0.1
-zipp==3.17.0
+zipp==3.18.1
diff --git a/misc/requirements/requirements-dev.txt b/misc/requirements/requirements-dev.txt
index 5e7f67489..59a1f8a03 100644
--- a/misc/requirements/requirements-dev.txt
+++ b/misc/requirements/requirements-dev.txt
@@ -1,45 +1,48 @@
# This file is automatically generated by scripts/dev/recompile_requirements.py
-build==1.0.3
+backports.tarfile==1.0.0
+build==1.2.1
bump2version==1.0.1
-certifi==2023.11.17
+certifi==2024.2.2
cffi==1.16.0
charset-normalizer==3.3.2
-cryptography==41.0.7
+cryptography==42.0.5
docutils==0.20.1
github3.py==4.0.1
hunter==3.6.1
-idna==3.6
-importlib-metadata==7.0.1
-importlib-resources==6.1.1
-jaraco.classes==3.3.0
+idna==3.7
+importlib_metadata==7.1.0
+importlib_resources==6.4.0
+jaraco.classes==3.4.0
+jaraco.context==5.3.0
+jaraco.functools==4.0.0
jeepney==0.8.0
-keyring==24.3.0
+keyring==25.1.0
manhole==1.8.0
markdown-it-py==3.0.0
mdurl==0.1.2
-more-itertools==10.1.0
-nh3==0.2.15
-packaging==23.2
-pkginfo==1.9.6
-pycparser==2.21
+more-itertools==10.2.0
+nh3==0.2.17
+packaging==24.0
+pkginfo==1.10.0
+pycparser==2.22
Pygments==2.17.2
PyJWT==2.8.0
Pympler==1.0.1
pyproject_hooks==1.0.0
-PyQt-builder==1.15.4
-python-dateutil==2.8.2
-readme-renderer==42.0
+PyQt-builder==1.16.0
+python-dateutil==2.9.0.post0
+readme_renderer==43.0
requests==2.31.0
requests-toolbelt==1.0.0
rfc3986==2.0.0
-rich==13.7.0
+rich==13.7.1
SecretStorage==3.3.3
-sip==6.8.1
+sip==6.8.3
six==1.16.0
tomli==2.0.1
-twine==4.0.2
-typing_extensions==4.9.0
+twine==5.0.0
+typing_extensions==4.11.0
uritemplate==4.1.1
-# urllib3==2.1.0
-zipp==3.17.0
+# urllib3==2.2.1
+zipp==3.18.1
diff --git a/misc/requirements/requirements-flake8.txt b/misc/requirements/requirements-flake8.txt
index 41b9e6c3e..fa541e4a8 100644
--- a/misc/requirements/requirements-flake8.txt
+++ b/misc/requirements/requirements-flake8.txt
@@ -2,15 +2,15 @@
attrs==23.2.0
flake8==7.0.0
-flake8-bugbear==23.12.2
-flake8-builtins==2.2.0
+flake8-bugbear==24.2.6
+flake8-builtins==2.5.0
flake8-comprehensions==3.14.0
flake8-debugger==4.1.2
flake8-deprecated==2.2.1
flake8-docstrings==1.7.0
flake8-future-import==0.4.7
flake8-plugin-utils==1.3.3
-flake8-pytest-style==1.7.2
+flake8-pytest-style==2.0.0
flake8-string-format==0.3.0
flake8-tidy-imports==4.10.0
flake8-tuple==0.4.1
diff --git a/misc/requirements/requirements-mypy.txt b/misc/requirements/requirements-mypy.txt
index b31bb7722..9bb872d13 100644
--- a/misc/requirements/requirements-mypy.txt
+++ b/misc/requirements/requirements-mypy.txt
@@ -1,21 +1,21 @@
# This file is automatically generated by scripts/dev/recompile_requirements.py
chardet==5.2.0
-diff_cover==8.0.2
-importlib-resources==6.1.1
-Jinja2==3.1.2
-lxml==5.0.1
-MarkupSafe==2.1.3
-mypy==1.8.0
+diff_cover==9.0.0
+importlib_resources==6.4.0
+Jinja2==3.1.3
+lxml==5.2.1
+MarkupSafe==2.1.5
+mypy==1.9.0
mypy-extensions==1.0.0
-pluggy==1.3.0
+pluggy==1.4.0
Pygments==2.17.2
PyQt5-stubs==5.15.6.0
tomli==2.0.1
-types-colorama==0.4.15.20240106
-types-docutils==0.20.0.20240106
-types-Pygments==2.17.0.20240106
-types-PyYAML==6.0.12.12
-types-setuptools==69.0.0.20240106
-typing_extensions==4.9.0
-zipp==3.17.0
+types-colorama==0.4.15.20240311
+types-docutils==0.20.0.20240406
+types-Pygments==2.17.0.20240310
+types-PyYAML==6.0.12.20240311
+types-setuptools==69.5.0.20240415
+typing_extensions==4.11.0
+zipp==3.18.1
diff --git a/misc/requirements/requirements-pyinstaller.txt b/misc/requirements/requirements-pyinstaller.txt
index af7a329fd..6c4b1d43b 100644
--- a/misc/requirements/requirements-pyinstaller.txt
+++ b/misc/requirements/requirements-pyinstaller.txt
@@ -1,8 +1,8 @@
# This file is automatically generated by scripts/dev/recompile_requirements.py
altgraph==0.17.4
-importlib-metadata==7.0.1
-packaging==23.2
-pyinstaller==6.3.0
-pyinstaller-hooks-contrib==2023.12
-zipp==3.17.0
+importlib_metadata==7.1.0
+packaging==24.0
+pyinstaller==6.6.0
+pyinstaller-hooks-contrib==2024.4
+zipp==3.18.1
diff --git a/misc/requirements/requirements-pylint.txt b/misc/requirements/requirements-pylint.txt
index b3a5198b4..94aac82a0 100644
--- a/misc/requirements/requirements-pylint.txt
+++ b/misc/requirements/requirements-pylint.txt
@@ -1,26 +1,26 @@
# This file is automatically generated by scripts/dev/recompile_requirements.py
-astroid==3.0.2
-certifi==2023.11.17
+astroid==3.1.0
+certifi==2024.2.2
cffi==1.16.0
charset-normalizer==3.3.2
-cryptography==41.0.7
-dill==0.3.7
+cryptography==42.0.5
+dill==0.3.8
github3.py==4.0.1
-idna==3.6
+idna==3.7
isort==5.13.2
mccabe==0.7.0
pefile==2023.2.7
-platformdirs==4.1.0
-pycparser==2.21
+platformdirs==4.2.0
+pycparser==2.22
PyJWT==2.8.0
-pylint==3.0.3
-python-dateutil==2.8.2
+pylint==3.1.0
+python-dateutil==2.9.0.post0
./scripts/dev/pylint_checkers
requests==2.31.0
six==1.16.0
tomli==2.0.1
-tomlkit==0.12.3
-typing_extensions==4.9.0
+tomlkit==0.12.4
+typing_extensions==4.11.0
uritemplate==4.1.1
-# urllib3==2.1.0
+# urllib3==2.2.1
diff --git a/misc/requirements/requirements-pyqt-6.6.txt b/misc/requirements/requirements-pyqt-6.6.txt
index 914422a38..02f1a325f 100644
--- a/misc/requirements/requirements-pyqt-6.6.txt
+++ b/misc/requirements/requirements-pyqt-6.6.txt
@@ -1,7 +1,7 @@
# This file is automatically generated by scripts/dev/recompile_requirements.py
PyQt6==6.6.1
-PyQt6-Qt6==6.6.1
+PyQt6-Qt6==6.6.3
PyQt6-sip==13.6.0
PyQt6-WebEngine==6.6.0
-PyQt6-WebEngine-Qt6==6.6.1
+PyQt6-WebEngine-Qt6==6.6.3
diff --git a/misc/requirements/requirements-pyqt-6.txt b/misc/requirements/requirements-pyqt-6.txt
index 914422a38..02f1a325f 100644
--- a/misc/requirements/requirements-pyqt-6.txt
+++ b/misc/requirements/requirements-pyqt-6.txt
@@ -1,7 +1,7 @@
# This file is automatically generated by scripts/dev/recompile_requirements.py
PyQt6==6.6.1
-PyQt6-Qt6==6.6.1
+PyQt6-Qt6==6.6.3
PyQt6-sip==13.6.0
PyQt6-WebEngine==6.6.0
-PyQt6-WebEngine-Qt6==6.6.1
+PyQt6-WebEngine-Qt6==6.6.3
diff --git a/misc/requirements/requirements-pyqt.txt b/misc/requirements/requirements-pyqt.txt
index 914422a38..02f1a325f 100644
--- a/misc/requirements/requirements-pyqt.txt
+++ b/misc/requirements/requirements-pyqt.txt
@@ -1,7 +1,7 @@
# This file is automatically generated by scripts/dev/recompile_requirements.py
PyQt6==6.6.1
-PyQt6-Qt6==6.6.1
+PyQt6-Qt6==6.6.3
PyQt6-sip==13.6.0
PyQt6-WebEngine==6.6.0
-PyQt6-WebEngine-Qt6==6.6.1
+PyQt6-WebEngine-Qt6==6.6.3
diff --git a/misc/requirements/requirements-pyroma.txt b/misc/requirements/requirements-pyroma.txt
index b93fb2be5..141faf1cb 100644
--- a/misc/requirements/requirements-pyroma.txt
+++ b/misc/requirements/requirements-pyroma.txt
@@ -1,17 +1,17 @@
# This file is automatically generated by scripts/dev/recompile_requirements.py
-build==1.0.3
-certifi==2023.11.17
+build==1.2.1
+certifi==2024.2.2
charset-normalizer==3.3.2
docutils==0.20.1
-idna==3.6
-importlib-metadata==7.0.1
-packaging==23.2
+idna==3.7
+importlib_metadata==7.1.0
+packaging==24.0
Pygments==2.17.2
pyproject_hooks==1.0.0
pyroma==4.2
requests==2.31.0
tomli==2.0.1
-trove-classifiers==2023.11.29
-urllib3==2.1.0
-zipp==3.17.0
+trove-classifiers==2024.4.10
+urllib3==2.2.1
+zipp==3.18.1
diff --git a/misc/requirements/requirements-qutebrowser.txt-raw b/misc/requirements/requirements-qutebrowser.txt-raw
index bd33e26e4..ca4081d1d 100644
--- a/misc/requirements/requirements-qutebrowser.txt-raw
+++ b/misc/requirements/requirements-qutebrowser.txt-raw
@@ -13,11 +13,11 @@ PyYAML
#@ add: pyobjc-framework-Cocoa ; sys_platform=="darwin"
## stdlib backports
-importlib-resources
+importlib_resources
## Optional dependencies
Pygments # For :view-source --pygments or on QtWebKit
colorama # Colored log output on Windows
adblock # Improved adblocking
-#@ markers: importlib-resources python_version=="3.8.*"
+#@ markers: importlib_resources python_version=="3.8.*"
diff --git a/misc/requirements/requirements-sphinx.txt b/misc/requirements/requirements-sphinx.txt
index 8d7cc145c..7bb66c6a0 100644
--- a/misc/requirements/requirements-sphinx.txt
+++ b/misc/requirements/requirements-sphinx.txt
@@ -2,17 +2,17 @@
alabaster==0.7.13
Babel==2.14.0
-certifi==2023.11.17
+certifi==2024.2.2
charset-normalizer==3.3.2
docutils==0.20.1
-idna==3.6
+idna==3.7
imagesize==1.4.1
-importlib-metadata==7.0.1
-Jinja2==3.1.2
-MarkupSafe==2.1.3
-packaging==23.2
+importlib_metadata==7.1.0
+Jinja2==3.1.3
+MarkupSafe==2.1.5
+packaging==24.0
Pygments==2.17.2
-pytz==2023.3.post1
+pytz==2024.1
requests==2.31.0
snowballstemmer==2.2.0
Sphinx==7.1.2
@@ -22,5 +22,5 @@ sphinxcontrib-htmlhelp==2.0.1
sphinxcontrib-jsmath==1.0.1
sphinxcontrib-qthelp==1.0.3
sphinxcontrib-serializinghtml==1.1.5
-urllib3==2.1.0
-zipp==3.17.0
+urllib3==2.2.1
+zipp==3.18.1
diff --git a/misc/requirements/requirements-tests.txt b/misc/requirements/requirements-tests.txt
index 744abbcfc..0d8aa9bc4 100644
--- a/misc/requirements/requirements-tests.txt
+++ b/misc/requirements/requirements-tests.txt
@@ -1,57 +1,56 @@
# This file is automatically generated by scripts/dev/recompile_requirements.py
attrs==23.2.0
-beautifulsoup4==4.12.2
+beautifulsoup4==4.12.3
blinker==1.7.0
-certifi==2023.11.17
+certifi==2024.2.2
charset-normalizer==3.3.2
cheroot==10.0.0
click==8.1.7
-coverage==7.4.0
+coverage==7.4.4
exceptiongroup==1.2.0
-execnet==2.0.2
-filelock==3.13.1
-Flask==3.0.0
+execnet==2.1.1
+filelock==3.13.4
+Flask==3.0.3
hunter==3.6.1
-hypothesis==6.92.3
-idna==3.6
-importlib-metadata==7.0.1
+hypothesis==6.100.1
+idna==3.7
+importlib_metadata==7.1.0
iniconfig==2.0.0
itsdangerous==2.1.2
jaraco.functools==4.0.0
-# Jinja2==3.1.2
-Mako==1.3.0
+# Jinja2==3.1.3
+Mako==1.3.3
manhole==1.8.0
-# MarkupSafe==2.1.3
-more-itertools==10.1.0
-packaging==23.2
-parse==1.20.0
+# MarkupSafe==2.1.5
+more-itertools==10.2.0
+packaging==24.0
+parse==1.20.1
parse-type==0.6.2
-pluggy==1.3.0
+pluggy==1.4.0
py-cpuinfo==9.0.0
Pygments==2.17.2
-pytest==7.4.4
-pytest-bdd==7.0.1
+pytest==8.1.1
+pytest-bdd==7.1.2
pytest-benchmark==4.0.0
-pytest-cov==4.1.0
+pytest-cov==5.0.0
pytest-instafail==0.5.0
-pytest-mock==3.12.0
-pytest-qt==4.3.1
+pytest-mock==3.14.0
+pytest-qt==4.4.0
pytest-repeat==0.9.3
-pytest-rerunfailures==13.0
+pytest-rerunfailures==14.0
pytest-xdist==3.5.0
pytest-xvfb==3.0.0
PyVirtualDisplay==3.0
requests==2.31.0
-requests-file==1.5.1
+requests-file==2.0.0
six==1.16.0
sortedcontainers==2.4.0
soupsieve==2.5
-tldextract==5.1.1
-toml==0.10.2
+tldextract==5.1.2
tomli==2.0.1
-typing_extensions==4.9.0
-urllib3==2.1.0
-vulture==2.10
-Werkzeug==3.0.1
-zipp==3.17.0
+typing_extensions==4.11.0
+urllib3==2.2.1
+vulture==2.11
+Werkzeug==3.0.2
+zipp==3.18.1
diff --git a/misc/requirements/requirements-tox.txt b/misc/requirements/requirements-tox.txt
index 79f770342..afea097d0 100644
--- a/misc/requirements/requirements-tox.txt
+++ b/misc/requirements/requirements-tox.txt
@@ -1,17 +1,17 @@
# This file is automatically generated by scripts/dev/recompile_requirements.py
-cachetools==5.3.2
+cachetools==5.3.3
chardet==5.2.0
colorama==0.4.6
distlib==0.3.8
-filelock==3.13.1
-packaging==23.2
-pip==23.3.2
-platformdirs==4.1.0
-pluggy==1.3.0
+filelock==3.13.4
+packaging==24.0
+pip==24.0
+platformdirs==4.2.0
+pluggy==1.4.0
pyproject-api==1.6.1
-setuptools==69.0.3
+setuptools==69.5.1
tomli==2.0.1
-tox==4.11.4
-virtualenv==20.25.0
-wheel==0.42.0
+tox==4.14.2
+virtualenv==20.25.1
+wheel==0.43.0
diff --git a/misc/requirements/requirements-vulture.txt b/misc/requirements/requirements-vulture.txt
index 9bceeb7b1..a7d37e73a 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.10
+tomli==2.0.1
+vulture==2.11
diff --git a/misc/requirements/requirements-yamllint.txt b/misc/requirements/requirements-yamllint.txt
index 8ecdcd508..4fb649ec4 100644
--- a/misc/requirements/requirements-yamllint.txt
+++ b/misc/requirements/requirements-yamllint.txt
@@ -2,4 +2,4 @@
pathspec==0.12.1
PyYAML==6.0.1
-yamllint==1.33.0
+yamllint==1.35.1
diff --git a/misc/userscripts/tor_identity b/misc/userscripts/tor_identity
index 6b3828ed4..a6d7c9250 100755
--- a/misc/userscripts/tor_identity
+++ b/misc/userscripts/tor_identity
@@ -32,7 +32,7 @@ except ImportError:
if __name__ == '__main__':
parser = ArgumentParser(prog='tor_identity')
- parser.add_argument('-c', '--control-port', default=9051,
+ parser.add_argument('-c', '--control-port', type=int, default=9051,
help='Tor control port (default 9051).')
parser.add_argument('-p', '--password', type=str, default=None,
help='Tor control port password.')
diff --git a/pytest.ini b/pytest.ini
index f2f746284..2de880eae 100644
--- a/pytest.ini
+++ b/pytest.ini
@@ -61,6 +61,15 @@ qt_log_ignore =
^QBackingStore::endPaint\(\) called with active painter; did you forget to destroy it or call QPainter::end\(\) on it\?$
# Qt 6.5 after system update, from qt-qt.accessibility.atspi
Error in contacting registry: "org\.freedesktop\.DBus\.Error\.Disconnected" "Not connected to D-Bus server"
+ # Seen in Qt 6.6.2 on CI, https://github.com/qutebrowser/qutebrowser/pull/8106#issuecomment-1952320663
+ ^QDBusConnection: couldn't handle call to Notify, no slot matched
+ ^QDBusConnection: couldn't handle call to CloseNotification, no slot matched
+ # Qt 6.7
+ ^Path override failed for key base::DIR_APP_DICTIONARIES and path '.*/qtwebengine_dictionaries'
+ # Sometime the above message gets printed twice at the same time and the messages get interleaved.
+ # The last part of the outer message gets bumped down to a line on its own, so hopefully this
+ # catches that. And we don't see any other weird permutations of this.
+ ^[^ ]*qtwebengine_dictionaries'$
xfail_strict = true
filterwarnings =
error
diff --git a/qutebrowser/app.py b/qutebrowser/app.py
index 015715eef..51603a2b9 100644
--- a/qutebrowser/app.py
+++ b/qutebrowser/app.py
@@ -132,6 +132,9 @@ def init(*, args: argparse.Namespace) -> None:
crashsignal.crash_handler.init_faulthandler()
objects.qapp.setQuitOnLastWindowClosed(False)
+ # WORKAROUND for KDE file dialogs / QEventLoopLocker quitting:
+ # https://bugreports.qt.io/browse/QTBUG-124386
+ objects.qapp.setQuitLockEnabled(False)
quitter.instance.shutting_down.connect(QApplication.closeAllWindows)
_init_icon()
diff --git a/qutebrowser/browser/browsertab.py b/qutebrowser/browser/browsertab.py
index 9419770ea..e7e56a061 100644
--- a/qutebrowser/browser/browsertab.py
+++ b/qutebrowser/browser/browsertab.py
@@ -14,7 +14,7 @@ from typing import (cast, TYPE_CHECKING, Any, Callable, Iterable, List, Optional
from qutebrowser.qt import machinery
from qutebrowser.qt.core import (pyqtSignal, pyqtSlot, QUrl, QObject, QSizeF, Qt,
- QEvent, QPoint, QRect)
+ QEvent, QPoint, QRect, QTimer)
from qutebrowser.qt.gui import QKeyEvent, QIcon, QPixmap
from qutebrowser.qt.widgets import QApplication, QWidget
from qutebrowser.qt.printsupport import QPrintDialog, QPrinter
@@ -904,7 +904,13 @@ class AbstractTabPrivate:
modeman.enter(self._tab.win_id, usertypes.KeyMode.insert,
'load finished', only_if_normal=True)
- self._tab.elements.find_focused(_auto_insert_mode_cb)
+ # There seems to be a race between loadFinished being called,
+ # and the autoload attribute on websites actually focusing anything.
+ # Thus, we delay this by a bit. Locally, a delay of 13ms caused no races
+ # with 5000 test reruns (even with simultaneous CPU stress testing),
+ # so 65ms should be a safe bet and still not be too noticeable.
+ QTimer.singleShot(
+ 65, lambda: self._tab.elements.find_focused(_auto_insert_mode_cb))
def clear_ssl_errors(self) -> None:
raise NotImplementedError
diff --git a/qutebrowser/browser/network/proxy.py b/qutebrowser/browser/network/proxy.py
index f0c511853..62872d68e 100644
--- a/qutebrowser/browser/network/proxy.py
+++ b/qutebrowser/browser/network/proxy.py
@@ -4,6 +4,8 @@
"""Handling of proxies."""
+from typing import Optional
+
from qutebrowser.qt.core import QUrl, pyqtSlot
from qutebrowser.qt.network import QNetworkProxy, QNetworkProxyFactory
@@ -13,7 +15,7 @@ from qutebrowser.misc import objects
from qutebrowser.browser.network import pac
-application_factory = None
+application_factory: Optional["ProxyFactory"] = None
def init():
diff --git a/qutebrowser/browser/qtnetworkdownloads.py b/qutebrowser/browser/qtnetworkdownloads.py
index 9dd507ab5..0360eed66 100644
--- a/qutebrowser/browser/qtnetworkdownloads.py
+++ b/qutebrowser/browser/qtnetworkdownloads.py
@@ -19,7 +19,7 @@ from qutebrowser.config import config, websettings
from qutebrowser.utils import message, usertypes, log, urlutils, utils, debug, objreg, qtlog
from qutebrowser.misc import quitter
from qutebrowser.browser import downloads
-from qutebrowser.browser.webkit import http
+from qutebrowser.browser.webkit import httpheaders
from qutebrowser.browser.webkit.network import networkmanager
@@ -533,7 +533,7 @@ class DownloadManager(downloads.AbstractDownloadManager):
try:
suggested_filename = target.suggested_filename()
except downloads.NoFilenameError:
- _, suggested_filename = http.parse_content_disposition(reply)
+ _, suggested_filename = httpheaders.parse_content_disposition(reply)
log.downloads.debug("fetch: {} -> {}".format(reply.url(),
suggested_filename))
download = DownloadItem(reply, manager=self)
diff --git a/qutebrowser/browser/qutescheme.py b/qutebrowser/browser/qutescheme.py
index f1c6711fd..e7bb27d8d 100644
--- a/qutebrowser/browser/qutescheme.py
+++ b/qutebrowser/browser/qutescheme.py
@@ -32,10 +32,10 @@ from qutebrowser.qt import sip
pyeval_output = ":pyeval was never called"
-csrf_token = None
+csrf_token: Optional[str] = None
-_HANDLERS = {}
+_HANDLERS: Dict[str, "_HandlerCallable"] = {}
class Error(Exception):
diff --git a/qutebrowser/browser/webengine/webenginesettings.py b/qutebrowser/browser/webengine/webenginesettings.py
index 2b375a7b0..78a4946ad 100644
--- a/qutebrowser/browser/webengine/webenginesettings.py
+++ b/qutebrowser/browser/webengine/webenginesettings.py
@@ -37,7 +37,7 @@ private_profile: Optional[QWebEngineProfile] = None
# The global WebEngineSettings object
_global_settings = cast('WebEngineSettings', None)
-parsed_user_agent = None
+parsed_user_agent: Optional[websettings.UserAgent] = None
_qute_scheme_handler = cast(webenginequtescheme.QuteSchemeHandler, None)
_req_interceptor = cast('interceptor.RequestInterceptor', None)
diff --git a/qutebrowser/browser/webengine/webenginetab.py b/qutebrowser/browser/webengine/webenginetab.py
index 1c712db5e..02d912a50 100644
--- a/qutebrowser/browser/webengine/webenginetab.py
+++ b/qutebrowser/browser/webengine/webenginetab.py
@@ -292,6 +292,8 @@ class WebEngineCaret(browsertab.AbstractCaret):
flags = set()
if utils.is_windows:
flags.add('windows')
+ if 'caret' in objects.debug_flags:
+ flags.add('debug')
return list(flags)
@pyqtSlot(usertypes.KeyMode)
diff --git a/qutebrowser/browser/webengine/webview.py b/qutebrowser/browser/webengine/webview.py
index a6f2ae113..96c0c97e5 100644
--- a/qutebrowser/browser/webengine/webview.py
+++ b/qutebrowser/browser/webengine/webview.py
@@ -238,9 +238,7 @@ class WebEnginePage(QWebEnginePage):
self._set_bg_color()
config.instance.changed.connect(self._set_bg_color)
if machinery.IS_QT6:
- self.certificateError.connect( # pylint: disable=no-member
- self._handle_certificate_error
- )
+ self.certificateError.connect(self._handle_certificate_error)
# Qt 5: Overridden method instead of signal
@config.change_filter('colors.webpage.bg')
diff --git a/qutebrowser/browser/webkit/http.py b/qutebrowser/browser/webkit/httpheaders.py
index 95b7b7104..95b7b7104 100644
--- a/qutebrowser/browser/webkit/http.py
+++ b/qutebrowser/browser/webkit/httpheaders.py
diff --git a/qutebrowser/browser/webkit/webpage.py b/qutebrowser/browser/webkit/webpage.py
index ea19174ec..595432dc9 100644
--- a/qutebrowser/browser/webkit/webpage.py
+++ b/qutebrowser/browser/webkit/webpage.py
@@ -18,7 +18,7 @@ from qutebrowser.qt.webkitwidgets import QWebPage, QWebFrame
from qutebrowser.config import websettings, config
from qutebrowser.browser import pdfjs, shared, downloads, greasemonkey
-from qutebrowser.browser.webkit import http
+from qutebrowser.browser.webkit import httpheaders
from qutebrowser.browser.webkit.network import networkmanager
from qutebrowser.utils import message, usertypes, log, jinja, objreg
from qutebrowser.qt import sip
@@ -263,14 +263,14 @@ class BrowserPage(QWebPage):
At some point we might want to implement the MIME Sniffing standard
here: https://mimesniff.spec.whatwg.org/
"""
- inline, suggested_filename = http.parse_content_disposition(reply)
+ inline, suggested_filename = httpheaders.parse_content_disposition(reply)
download_manager = objreg.get('qtnetwork-download-manager')
if not inline:
# Content-Disposition: attachment -> force download
download_manager.fetch(reply,
suggested_filename=suggested_filename)
return
- mimetype, _rest = http.parse_content_type(reply)
+ mimetype, _rest = httpheaders.parse_content_type(reply)
if mimetype == 'image/jpg':
# Some servers (e.g. the LinkedIn CDN) send a non-standard
# image/jpg (instead of image/jpeg, defined in RFC 1341 section
diff --git a/qutebrowser/commands/runners.py b/qutebrowser/commands/runners.py
index 1ad563c5d..0d63d0021 100644
--- a/qutebrowser/commands/runners.py
+++ b/qutebrowser/commands/runners.py
@@ -7,7 +7,7 @@
import traceback
import re
import contextlib
-from typing import TYPE_CHECKING, Callable, Dict, Iterator, Mapping, MutableMapping
+from typing import TYPE_CHECKING, Callable, Dict, Tuple, Iterator, Mapping, MutableMapping
from qutebrowser.qt.core import pyqtSlot, QUrl, QObject
@@ -21,7 +21,7 @@ if TYPE_CHECKING:
_ReplacementFunction = Callable[['tabbedbrowser.TabbedBrowser'], str]
-last_command = {}
+last_command: Dict[usertypes.KeyMode, Tuple[str, int]] = {}
def _url(tabbed_browser):
diff --git a/qutebrowser/completion/completionwidget.py b/qutebrowser/completion/completionwidget.py
index f042be0a1..0f5dc0de9 100644
--- a/qutebrowser/completion/completionwidget.py
+++ b/qutebrowser/completion/completionwidget.py
@@ -437,8 +437,7 @@ class CompletionView(QTreeView):
contents_height = (
self.viewportSizeHint().height() +
bar.sizeHint().height())
- if contents_height <= height:
- height = contents_height
+ height = min(height, contents_height)
# The width isn't really relevant as we're expanding anyways.
return QSize(-1, height)
diff --git a/qutebrowser/config/config.py b/qutebrowser/config/config.py
index 573f25fa1..cb7fe77b3 100644
--- a/qutebrowser/config/config.py
+++ b/qutebrowser/config/config.py
@@ -29,7 +29,7 @@ key_instance = cast('KeyConfig', None)
cache = cast('configcache.ConfigCache', None)
# Keeping track of all change filters to validate them later.
-change_filters = []
+change_filters: List["change_filter"] = []
# Sentinel
UNSET = object()
diff --git a/qutebrowser/config/configdata.yml b/qutebrowser/config/configdata.yml
index 6d391e596..8abc0b7d2 100644
--- a/qutebrowser/config/configdata.yml
+++ b/qutebrowser/config/configdata.yml
@@ -1784,6 +1784,7 @@ hints.selectors:
- '[role="button"]'
- '[role="tab"]'
- '[role="checkbox"]'
+ - '[role="switch"]'
- '[role="menuitem"]'
- '[role="menuitemcheckbox"]'
- '[role="menuitemradio"]'
@@ -3330,7 +3331,7 @@ colors.webpage.darkmode.enabled:
colors.webpage.darkmode.algorithm:
default: lightness-cielab
desc: >-
- Which algorithm to use for modifying how colors are rendered with darkmode.
+ Which algorithm to use for modifying how colors are rendered with dark mode.
The `lightness-cielab` value was added with QtWebEngine 5.14 and is treated
like `lightness-hsl` with older QtWebEngine versions.
diff --git a/qutebrowser/config/configinit.py b/qutebrowser/config/configinit.py
index 5d000c3ec..a08ddb619 100644
--- a/qutebrowser/config/configinit.py
+++ b/qutebrowser/config/configinit.py
@@ -7,6 +7,7 @@
import argparse
import os.path
import sys
+from typing import Optional
from qutebrowser.qt.widgets import QMessageBox
@@ -19,7 +20,7 @@ from qutebrowser.misc import msgbox, objects, savemanager
# Error which happened during init, so we can show a message box.
-_init_errors = None
+_init_errors: Optional[configexc.ConfigFileErrors] = None
def early_init(args: argparse.Namespace) -> None:
diff --git a/qutebrowser/config/websettings.py b/qutebrowser/config/websettings.py
index 9f5bf92d0..7824ae258 100644
--- a/qutebrowser/config/websettings.py
+++ b/qutebrowser/config/websettings.py
@@ -246,7 +246,7 @@ def clear_private_data() -> None:
elif objects.backend == usertypes.Backend.QtWebKit:
from qutebrowser.browser.webkit import cookies
assert cookies.ram_cookie_jar is not None
- cookies.ram_cookie_jar.setAllCookies([]) # type: ignore[unreachable]
+ cookies.ram_cookie_jar.setAllCookies([])
else:
raise utils.Unreachable(objects.backend)
diff --git a/qutebrowser/extensions/loader.py b/qutebrowser/extensions/loader.py
index b5b232c5a..ff9974d9d 100644
--- a/qutebrowser/extensions/loader.py
+++ b/qutebrowser/extensions/loader.py
@@ -21,7 +21,7 @@ from qutebrowser.misc import objects
# ModuleInfo objects for all loaded plugins
-_module_infos = []
+_module_infos: List["ModuleInfo"] = []
InitHookType = Callable[['InitContext'], None]
ConfigChangedHookType = Callable[[], None]
diff --git a/qutebrowser/javascript/caret.js b/qutebrowser/javascript/caret.js
index b16a15348..4aeefcdb9 100644
--- a/qutebrowser/javascript/caret.js
+++ b/qutebrowser/javascript/caret.js
@@ -740,6 +740,12 @@ window._qutebrowser.caret = (function() {
CaretBrowsing.isWindows = null;
/**
+ * Whether we should log debug outputs.
+ * @type {boolean}
+ */
+ CaretBrowsing.isDebug = null;
+
+ /**
* The id returned by window.setInterval for our stopAnimation function, so
* we can cancel it when we call stopAnimation again.
* @type {number?}
@@ -1150,6 +1156,8 @@ window._qutebrowser.caret = (function() {
action = "extend";
}
+ CaretBrowsing.debug(`(move) ${action} ${count} ${granularity} ${direction}, selection ${CaretBrowsing.selectionState}`);
+
for (let i = 0; i < count; i++) {
if (CaretBrowsing.selectionState === CaretBrowsing.SelectionState.LINE) {
CaretBrowsing.updateLineSelection(direction, granularity);
@@ -1180,6 +1188,8 @@ window._qutebrowser.caret = (function() {
if (CaretBrowsing.selectionState !== CaretBrowsing.SelectionState.NONE) {
action = "extend";
}
+ CaretBrowsing.debug(`(moveToBlock) ${action} paragraph ${paragraph}, boundary ${boundary}, count ${count}, selection ${CaretBrowsing.selectionState}`);
+
for (let i = 0; i < count; i++) {
window.
getSelection().
@@ -1196,6 +1206,7 @@ window._qutebrowser.caret = (function() {
};
CaretBrowsing.toggle = function(value) {
+ CaretBrowsing.debug(`(toggle) enabled ${CaretBrowsing.isEnabled}, force ${CaretBrowsing.forceEnabled}`);
if (CaretBrowsing.forceEnabled) {
CaretBrowsing.recreateCaretElement();
return;
@@ -1231,6 +1242,7 @@ window._qutebrowser.caret = (function() {
* is enabled and whether this window / iframe has focus.
*/
CaretBrowsing.updateIsCaretVisible = function() {
+ CaretBrowsing.debug(`(updateIsCaretVisible) isEnabled ${CaretBrowsing.isEnabled}, isWindowFocused ${CaretBrowsing.isWindowFocused}, isCaretVisible ${CaretBrowsing.isCaretVisible}, caretElement ${CaretBrowsing.caretElement}`);
CaretBrowsing.isCaretVisible =
(CaretBrowsing.isEnabled && CaretBrowsing.isWindowFocused);
if (CaretBrowsing.isCaretVisible && !CaretBrowsing.caretElement) {
@@ -1274,6 +1286,12 @@ window._qutebrowser.caret = (function() {
}
};
+ CaretBrowsing.debug = (text) => {
+ if (CaretBrowsing.isDebug) {
+ console.debug(`caret: ${text}`);
+ }
+ }
+
CaretBrowsing.init = function() {
CaretBrowsing.isWindowFocused = document.hasFocus();
@@ -1313,6 +1331,7 @@ window._qutebrowser.caret = (function() {
funcs.setFlags = (flags) => {
CaretBrowsing.isWindows = flags.includes("windows");
+ CaretBrowsing.isDebug = flags.includes("debug");
};
funcs.disableCaret = () => {
diff --git a/qutebrowser/keyinput/keyutils.py b/qutebrowser/keyinput/keyutils.py
index af19bb61c..54b6e88b1 100644
--- a/qutebrowser/keyinput/keyutils.py
+++ b/qutebrowser/keyinput/keyutils.py
@@ -26,7 +26,7 @@ from qutebrowser.qt.gui import QKeySequence, QKeyEvent
if machinery.IS_QT6:
from qutebrowser.qt.core import QKeyCombination
else:
- QKeyCombination = None # QKeyCombination was added in Qt 6
+ QKeyCombination: None = None # QKeyCombination was added in Qt 6
from qutebrowser.utils import utils, qtutils, debug
diff --git a/qutebrowser/misc/consolewidget.py b/qutebrowser/misc/consolewidget.py
index b489aaf36..08f5dc5ff 100644
--- a/qutebrowser/misc/consolewidget.py
+++ b/qutebrowser/misc/consolewidget.py
@@ -6,7 +6,7 @@
import sys
import code
-from typing import MutableSequence
+from typing import MutableSequence, Optional
from qutebrowser.qt.core import pyqtSignal, pyqtSlot, Qt
from qutebrowser.qt.widgets import QTextEdit, QWidget, QVBoxLayout, QApplication
@@ -17,7 +17,7 @@ from qutebrowser.misc import cmdhistory, miscwidgets
from qutebrowser.utils import utils, objreg
-console_widget = None
+console_widget: Optional["ConsoleWidget"] = None
class ConsoleLineEdit(miscwidgets.CommandLineEdit):
diff --git a/qutebrowser/misc/crashsignal.py b/qutebrowser/misc/crashsignal.py
index c69dcbe29..05e5806df 100644
--- a/qutebrowser/misc/crashsignal.py
+++ b/qutebrowser/misc/crashsignal.py
@@ -22,8 +22,9 @@ from qutebrowser.qt.core import (pyqtSlot, qInstallMessageHandler, QObject,
from qutebrowser.qt.widgets import QApplication
from qutebrowser.api import cmdutils
+from qutebrowser.config import configfiles, configexc
from qutebrowser.misc import earlyinit, crashdialog, ipc, objects
-from qutebrowser.utils import usertypes, standarddir, log, objreg, debug, utils
+from qutebrowser.utils import usertypes, standarddir, log, objreg, debug, utils, message
from qutebrowser.qt import sip
if TYPE_CHECKING:
from qutebrowser.misc import quitter
@@ -322,6 +323,17 @@ class SignalHandler(QObject):
self._activated = False
self._orig_wakeup_fd: Optional[int] = None
+ self._handlers = {
+ signal.SIGINT: self.interrupt,
+ signal.SIGTERM: self.interrupt,
+ }
+ platform_dependant_handlers = {
+ "SIGHUP": self.reload_config,
+ }
+ for sig_str, handler in platform_dependant_handlers.items():
+ if hasattr(signal.Signals, sig_str):
+ self._handlers[signal.Signals[sig_str]] = handler
+
def activate(self):
"""Set up signal handlers.
@@ -331,10 +343,8 @@ class SignalHandler(QObject):
On Unix, it uses a QSocketNotifier with os.set_wakeup_fd to get
notified.
"""
- self._orig_handlers[signal.SIGINT] = signal.signal(
- signal.SIGINT, self.interrupt)
- self._orig_handlers[signal.SIGTERM] = signal.signal(
- signal.SIGTERM, self.interrupt)
+ for sig, handler in self._handlers.items():
+ self._orig_handlers[sig] = signal.signal(sig, handler)
if utils.is_posix and hasattr(signal, 'set_wakeup_fd'):
# pylint: disable=import-error,no-member,useless-suppression
@@ -430,6 +440,15 @@ class SignalHandler(QObject):
print("WHY ARE YOU DOING THIS TO ME? :(")
sys.exit(128 + signum)
+ def reload_config(self, _signum, _frame):
+ """Reload the config."""
+ log.signals.info("SIGHUP received, reloading config.")
+ filename = standarddir.config_py()
+ try:
+ configfiles.read_config_py(filename)
+ except configexc.ConfigFileErrors as e:
+ message.error(str(e))
+
def init(q_app: QApplication,
args: argparse.Namespace,
diff --git a/qutebrowser/misc/ipc.py b/qutebrowser/misc/ipc.py
index 2dc34c886..21a3352d6 100644
--- a/qutebrowser/misc/ipc.py
+++ b/qutebrowser/misc/ipc.py
@@ -28,7 +28,7 @@ PROTOCOL_VERSION = 1
# The ipc server instance
-server = None
+server: Optional["IPCServer"] = None
def _get_socketname_windows(basedir):
diff --git a/qutebrowser/misc/nativeeventfilter.py b/qutebrowser/misc/nativeeventfilter.py
index 9b1bbb97c..06533bd42 100644
--- a/qutebrowser/misc/nativeeventfilter.py
+++ b/qutebrowser/misc/nativeeventfilter.py
@@ -20,7 +20,7 @@ from qutebrowser.utils import log
# Needs to be saved to avoid garbage collection
-_instance = None
+_instance: Optional["NativeEventFilter"] = None
# Using C-style naming for C structures in this file
# pylint: disable=invalid-name
diff --git a/qutebrowser/qt/machinery.py b/qutebrowser/qt/machinery.py
index 616c7ccfc..9f45dd6ce 100644
--- a/qutebrowser/qt/machinery.py
+++ b/qutebrowser/qt/machinery.py
@@ -34,7 +34,7 @@ from qutebrowser.utils import log
# sed -i 's/_WRAPPER_OVERRIDE = .*/_WRAPPER_OVERRIDE = "PyQt6"/' qutebrowser/qt/machinery.py
#
# Users: Set the QUTE_QT_WRAPPER environment variable to change the default wrapper.
-_WRAPPER_OVERRIDE = None
+_WRAPPER_OVERRIDE = None # type: ignore[var-annotated]
WRAPPERS = [
"PyQt6",
@@ -168,9 +168,9 @@ def _select_wrapper(args: Optional[argparse.Namespace]) -> SelectionInfo:
- Otherwise, try the wrappers in WRAPPER in order (PyQt6 -> PyQt5)
"""
# If any Qt wrapper has been imported before this, something strange might
- # be happening.
+ # be happening. With PyInstaller, it imports the Qt bindings early.
for name in WRAPPERS:
- if name in sys.modules:
+ if name in sys.modules and not hasattr(sys, "frozen"):
warnings.warn(f"{name} already imported", stacklevel=1)
if args is not None and args.qt_wrapper is not None:
@@ -190,7 +190,7 @@ def _select_wrapper(args: Optional[argparse.Namespace]) -> SelectionInfo:
return SelectionInfo(wrapper=env_wrapper, reason=SelectionReason.env)
if _WRAPPER_OVERRIDE is not None:
- assert _WRAPPER_OVERRIDE in WRAPPERS # type: ignore[unreachable]
+ assert _WRAPPER_OVERRIDE in WRAPPERS
return SelectionInfo(wrapper=_WRAPPER_OVERRIDE, reason=SelectionReason.override)
return _autoselect_wrapper()
diff --git a/qutebrowser/qutebrowser.py b/qutebrowser/qutebrowser.py
index c3a0a60be..e68156759 100644
--- a/qutebrowser/qutebrowser.py
+++ b/qutebrowser/qutebrowser.py
@@ -171,12 +171,13 @@ def debug_flag_error(flag):
avoid-chromium-init: Enable `--version` without initializing Chromium.
werror: Turn Python warnings into errors.
test-notification-service: Use the testing libnotify service.
+ caret: Enable debug logging for caret.js.
"""
valid_flags = ['debug-exit', 'pdb-postmortem', 'no-sql-history',
'no-scroll-filtering', 'log-requests', 'log-cookies',
'log-scroll-pos', 'log-sensitive-keys', 'stack', 'chromium',
'wait-renderer-process', 'avoid-chromium-init', 'werror',
- 'test-notification-service', 'log-qt-events']
+ 'test-notification-service', 'log-qt-events', 'caret']
if flag in valid_flags:
return flag
diff --git a/qutebrowser/utils/log.py b/qutebrowser/utils/log.py
index 3e3b407b0..9695ec5a2 100644
--- a/qutebrowser/utils/log.py
+++ b/qutebrowser/utils/log.py
@@ -31,7 +31,7 @@ if TYPE_CHECKING:
from qutebrowser.config import config as configmodule
_log_inited = False
-_args = None
+_args: Optional[argparse.Namespace] = None
COLORS = ['black', 'red', 'green', 'yellow', 'blue', 'purple', 'cyan', 'white']
COLOR_ESCAPES = {color: '\033[{}m'.format(i)
@@ -146,7 +146,7 @@ LOGGER_NAMES = [
ram_handler: Optional['RAMHandler'] = None
console_handler: Optional[logging.Handler] = None
-console_filter = None
+console_filter: Optional["LogFilter"] = None
def stub(suffix: str = '') -> None:
diff --git a/qutebrowser/utils/qtlog.py b/qutebrowser/utils/qtlog.py
index 1de9181cf..78b48ebee 100644
--- a/qutebrowser/utils/qtlog.py
+++ b/qutebrowser/utils/qtlog.py
@@ -15,7 +15,7 @@ from typing import Iterator, Optional
from qutebrowser.qt import core as qtcore
from qutebrowser.utils import log
-_args = None
+_args: Optional[argparse.Namespace] = None
def init(args: argparse.Namespace) -> None:
diff --git a/qutebrowser/utils/qtutils.py b/qutebrowser/utils/qtutils.py
index 89175ca4e..21f3b8478 100644
--- a/qutebrowser/utils/qtutils.py
+++ b/qutebrowser/utils/qtutils.py
@@ -193,6 +193,15 @@ def check_qdatastream(stream: QDataStream) -> None:
QDataStream.Status.WriteFailed: ("The data stream cannot write to the "
"underlying device."),
}
+ try:
+ status_to_str[QDataStream.Status.SizeLimitExceeded] = ( # type: ignore[attr-defined]
+ "The data stream cannot read or write the data because its size is larger "
+ "than supported by the current platform."
+ )
+ except AttributeError:
+ # Added in Qt 6.7
+ pass
+
if stream.status() != QDataStream.Status.Ok:
raise OSError(status_to_str[stream.status()])
@@ -736,4 +745,4 @@ else:
def add_optional(obj: Optional[_T]) -> Optional[_T]:
return obj
- QT_NONE = None
+ QT_NONE: None = None
diff --git a/qutebrowser/utils/resources.py b/qutebrowser/utils/resources.py
index 60d90fd31..a40f9d2bd 100644
--- a/qutebrowser/utils/resources.py
+++ b/qutebrowser/utils/resources.py
@@ -9,7 +9,7 @@ import sys
import contextlib
import posixpath
import pathlib
-from typing import Iterator, Iterable, Union
+from typing import Iterator, Iterable, Union, Dict
# We cannot use the stdlib version on 3.8 because we need the files() API.
@@ -25,7 +25,7 @@ else: # pragma: no cover
from importlib_resources.abc import Traversable
import qutebrowser
-_cache = {}
+_cache: Dict[str, str] = {}
_ResourceType = Union[Traversable, pathlib.Path]
diff --git a/qutebrowser/utils/standarddir.py b/qutebrowser/utils/standarddir.py
index 2d2ae53f9..026376dc2 100644
--- a/qutebrowser/utils/standarddir.py
+++ b/qutebrowser/utils/standarddir.py
@@ -10,7 +10,7 @@ import sys
import contextlib
import enum
import argparse
-from typing import Iterator, Optional
+from typing import Iterator, Optional, Dict
from qutebrowser.qt.core import QStandardPaths
from qutebrowser.qt.widgets import QApplication
@@ -18,7 +18,7 @@ from qutebrowser.qt.widgets import QApplication
from qutebrowser.utils import log, debug, utils, version, qtutils
# The cached locations
-_locations = {}
+_locations: Dict["_Location", str] = {}
class _Location(enum.Enum):
diff --git a/qutebrowser/utils/utils.py b/qutebrowser/utils/utils.py
index f83f838fe..11c160c9e 100644
--- a/qutebrowser/utils/utils.py
+++ b/qutebrowser/utils/utils.py
@@ -38,7 +38,7 @@ except ImportError: # pragma: no cover
from qutebrowser.utils import log
-fake_clipboard = None
+fake_clipboard: Optional[str] = None
log_clipboard = False
is_mac = sys.platform.startswith('darwin')
@@ -341,7 +341,7 @@ class prevent_exceptions: # noqa: N801,N806 pylint: disable=invalid-name
"""Call the original function."""
try:
return func(*args, **kwargs)
- except BaseException:
+ except BaseException: # noqa: B036
log.misc.exception("Error in {}".format(qualname(func)))
return retval
diff --git a/qutebrowser/utils/version.py b/qutebrowser/utils/version.py
index 59da5b5f0..32d5357db 100644
--- a/qutebrowser/utils/version.py
+++ b/qutebrowser/utils/version.py
@@ -78,7 +78,7 @@ class DistributionInfo:
pretty: str
-pastebin_url = None
+pastebin_url: Optional[str] = None
class Distribution(enum.Enum):
@@ -535,6 +535,7 @@ class WebEngineVersions:
webengine: utils.VersionNumber
chromium: Optional[str]
source: str
+ chromium_security: Optional[str] = None
chromium_major: Optional[int] = dataclasses.field(init=False)
_CHROMIUM_VERSIONS: ClassVar[Dict[utils.VersionNumber, str]] = {
@@ -610,12 +611,21 @@ class WebEngineVersions:
# 6.5.0: Security fixes up to 110.0.5481.104 (2023-02-16)
# 6.5.1: Security fixes up to 112.0.5615.138 (2023-04-18)
# 6.5.2: Security fixes up to 114.0.5735.133 (2023-06-13)
+ # 6.5.3: Security fixes up to 117.0.5938.63 (2023-09-12)
utils.VersionNumber(6, 5): '108.0.5359.220',
# Qt 6.6: Chromium 112
# 112.0.5615.213 (~2023-04-18)
- # 6.6.0: Security fixes up to 116.0.5845.110 (?) (2023-08-22)
+ # 6.6.0: Security fixes up to 117.0.5938.63 (2023-09-12)
+ # 6.6.1: Security fixes up to 119.0.6045.123 (2023-11-07)
+ # 6.6.2: Security fixes up to 121.0.6167.160 (2024-02-06)
+ # 6.6.3: Security fixes up to 122.0.6261.128 (2024-03-12)
utils.VersionNumber(6, 6): '112.0.5615.213',
+
+ # Qt 6.7: Chromium 118
+ # 118.0.5993.220 (~2023-10-24)
+ # 6.6.0: Security fixes up to 122.0.6261.128 (?) (2024-03-12)
+ utils.VersionNumber(6, 7): '118.0.5993.220',
}
def __post_init__(self) -> None:
@@ -629,6 +639,8 @@ class WebEngineVersions:
s = f'QtWebEngine {self.webengine}'
if self.chromium is not None:
s += f', based on Chromium {self.chromium}'
+ if self.chromium_security is not None:
+ s += f', with security patches up to {self.chromium_security} (plus any distribution patches)'
if self.source != 'UA':
s += f' (from {self.source})'
return s
@@ -686,7 +698,12 @@ class WebEngineVersions:
return cls._CHROMIUM_VERSIONS.get(minor_version)
@classmethod
- def from_api(cls, qtwe_version: str, chromium_version: Optional[str]) -> 'WebEngineVersions':
+ def from_api(
+ cls,
+ qtwe_version: str,
+ chromium_version: Optional[str],
+ chromium_security: Optional[str] = None,
+ ) -> 'WebEngineVersions':
"""Get the versions based on the exact versions.
This is called if we have proper APIs to get the versions easily
@@ -696,6 +713,7 @@ class WebEngineVersions:
return cls(
webengine=parsed,
chromium=chromium_version,
+ chromium_security=chromium_security,
source='api',
)
@@ -796,11 +814,20 @@ def qtwebengine_versions(*, avoid_init: bool = False) -> WebEngineVersions:
except ImportError:
pass # Needs QtWebEngine 6.2+ with PyQtWebEngine 6.3.1+
else:
+ try:
+ from qutebrowser.qt.webenginecore import (
+ qWebEngineChromiumSecurityPatchVersion,
+ )
+ chromium_security = qWebEngineChromiumSecurityPatchVersion()
+ except ImportError:
+ chromium_security = None # Needs QtWebEngine 6.3+
+
qtwe_version = qWebEngineVersion()
assert qtwe_version is not None
return WebEngineVersions.from_api(
qtwe_version=qtwe_version,
chromium_version=qWebEngineChromiumVersion(),
+ chromium_security=chromium_security,
)
from qutebrowser.browser.webengine import webenginesettings
diff --git a/requirements.txt b/requirements.txt
index 81e0d0606..229f9e9c8 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -2,12 +2,12 @@
adblock==0.6.0
colorama==0.4.6
-importlib-resources==6.1.1 ; python_version=="3.8.*"
-Jinja2==3.1.2
-MarkupSafe==2.1.3
+importlib_resources==6.4.0 ; python_version=="3.8.*"
+Jinja2==3.1.3
+MarkupSafe==2.1.5
Pygments==2.17.2
PyYAML==6.0.1
-zipp==3.17.0
+zipp==3.18.1
# Unpinned due to recompile_requirements.py limitations
pyobjc-core ; sys_platform=="darwin"
pyobjc-framework-Cocoa ; sys_platform=="darwin"
diff --git a/scripts/dev/build_release.py b/scripts/dev/build_release.py
index 65eef720c..40cedc2e8 100755
--- a/scripts/dev/build_release.py
+++ b/scripts/dev/build_release.py
@@ -171,6 +171,9 @@ def smoke_test(executable: pathlib.Path, debug: bool, qt5: bool) -> None:
r'[0-9:]* WARNING: Qt WebEngine resources not found at .*',
(r'[0-9:]* WARNING: Installed Qt WebEngine locales directory not found at '
r'location /qtwebengine_locales\. Trying application directory\.\.\.'),
+ # Qt 6.7, only seen on macos for some reason
+ (r'.*Path override failed for key base::DIR_APP_DICTIONARIES '
+ r"and path '.*/qtwebengine_dictionaries'"),
])
elif IS_WINDOWS:
stderr_whitelist.extend([
diff --git a/scripts/dev/changelog_urls.json b/scripts/dev/changelog_urls.json
index 48158d339..645bb6385 100644
--- a/scripts/dev/changelog_urls.json
+++ b/scripts/dev/changelog_urls.json
@@ -45,12 +45,12 @@
"flake8": "https://github.com/PyCQA/flake8/tree/main/docs/source/release-notes",
"flake8-docstrings": "https://github.com/PyCQA/flake8-docstrings/blob/main/HISTORY.rst",
"flake8-debugger": "https://github.com/JBKahn/flake8-debugger/",
- "flake8-builtins": "https://github.com/gforcada/flake8-builtins/blob/master/CHANGES.rst",
+ "flake8-builtins": "https://github.com/gforcada/flake8-builtins/blob/main/CHANGES.rst",
"flake8-bugbear": "https://github.com/PyCQA/flake8-bugbear#change-log",
"flake8-tidy-imports": "https://github.com/adamchainz/flake8-tidy-imports/blob/main/CHANGELOG.rst",
"flake8-tuple": "https://github.com/ar4s/flake8_tuple/blob/master/HISTORY.rst",
"flake8-comprehensions": "https://github.com/adamchainz/flake8-comprehensions/blob/main/CHANGELOG.rst",
- "flake8-deprecated": "https://github.com/gforcada/flake8-deprecated/blob/master/CHANGES.rst",
+ "flake8-deprecated": "https://github.com/gforcada/flake8-deprecated/blob/main/CHANGES.rst",
"flake8-future-import": "https://github.com/xZise/flake8-future-import#changes",
"flake8-string-format": "https://github.com/xZise/flake8-string-format#changes",
"flake8-plugin-utils": "https://github.com/afonasev/flake8-plugin-utils#change-log",
@@ -78,7 +78,6 @@
"sphinxcontrib-jsmath": "https://www.sphinx-doc.org/en/master/changes.html",
"sphinxcontrib-qthelp": "https://www.sphinx-doc.org/en/master/changes.html",
"sphinxcontrib-serializinghtml": "https://www.sphinx-doc.org/en/master/changes.html",
- "jaraco.functools": "https://jaracofunctools.readthedocs.io/en/latest/history.html",
"parse": "https://github.com/r1chardj0n3s/parse#potential-gotchas",
"Pympler": "https://github.com/pympler/pympler/blob/master/CHANGELOG.md",
"pytest-mock": "https://github.com/pytest-dev/pytest-mock/blob/main/CHANGELOG.rst",
@@ -93,13 +92,12 @@
"urllib3": "https://github.com/urllib3/urllib3/blob/main/CHANGES.rst",
"lxml": "https://github.com/lxml/lxml/blob/master/CHANGES.txt",
"cryptography": "https://cryptography.io/en/latest/changelog.html",
- "toml": "https://github.com/uiri/toml/releases",
"tomli": "https://github.com/hukkin/tomli/blob/master/CHANGELOG.md",
"PyQt5": "https://www.riverbankcomputing.com/news",
"PyQt5-Qt5": "https://www.riverbankcomputing.com/news",
"PyQtWebEngine": "https://www.riverbankcomputing.com/news",
"PyQtWebEngine-Qt5": "https://www.riverbankcomputing.com/news",
- "PyQt-builder": "https://www.riverbankcomputing.com/news",
+ "PyQt-builder": "https://pyqt-builder.readthedocs.io/en/stable/releases.html",
"PyQt5-sip": "https://www.riverbankcomputing.com/news",
"PyQt5-stubs": "https://github.com/python-qt-tools/PyQt5-stubs/blob/master/CHANGELOG.md",
"sip": "https://www.riverbankcomputing.com/news",
@@ -120,33 +118,36 @@
"tldextract": "https://github.com/john-kurkowski/tldextract/blob/master/CHANGELOG.md",
"typing_extensions": "https://github.com/python/typing_extensions/blob/main/CHANGELOG.md",
"diff_cover": "https://github.com/Bachmann1234/diff_cover/blob/main/CHANGELOG",
- "beautifulsoup4": "https://bazaar.launchpad.net/~leonardr/beautifulsoup/bs4/view/head:/CHANGELOG",
+ "beautifulsoup4": "https://git.launchpad.net/beautifulsoup/tree/CHANGELOG",
"check-manifest": "https://github.com/mgedmin/check-manifest/blob/master/CHANGES.rst",
"yamllint": "https://github.com/adrienverge/yamllint/blob/master/CHANGELOG.rst",
"pathspec": "https://github.com/cpburnz/python-path-specification/blob/master/CHANGES.rst",
"filelock": "https://github.com/tox-dev/py-filelock/releases",
"github3.py": "https://github3.readthedocs.io/en/latest/release-notes/index.html",
"manhole": "https://github.com/ionelmc/python-manhole/blob/master/CHANGELOG.rst",
- "pycparser": "https://github.com/eliben/pycparser/blob/master/CHANGES",
+ "pycparser": "https://github.com/eliben/pycparser/releases",
"python-dateutil": "https://dateutil.readthedocs.io/en/stable/changelog.html",
"platformdirs": "https://github.com/platformdirs/platformdirs/releases",
"pluggy": "https://github.com/pytest-dev/pluggy/blob/main/CHANGELOG.rst",
"mypy-extensions": "https://github.com/python/mypy_extensions/commits/master",
"pyroma": "https://github.com/regebro/pyroma/blob/master/CHANGES.txt",
"adblock": "https://github.com/ArniDagur/python-adblock/blob/master/CHANGELOG.md",
- "importlib-resources": "https://importlib-resources.readthedocs.io/en/latest/history.html",
- "importlib-metadata": "https://github.com/python/importlib_metadata/blob/main/NEWS.rst",
- "zipp": "https://github.com/jaraco/zipp/blob/main/NEWS.rst",
+ "importlib_resources": "https://importlib-resources.readthedocs.io/en/latest/history.html",
+ "importlib_metadata": "https://github.com/python/importlib_metadata/blob/main/NEWS.rst",
+ "zipp": "https://zipp.readthedocs.io/en/latest/history.html",
"pip": "https://pip.pypa.io/en/stable/news/",
"wheel": "https://wheel.readthedocs.io/en/stable/news.html",
"setuptools": "https://setuptools.readthedocs.io/en/latest/history.html",
"pefile": "https://github.com/erocarrera/pefile/commits/master",
"SecretStorage": "https://github.com/mitya57/secretstorage/blob/master/changelog",
"jeepney": "https://gitlab.com/takluyver/jeepney/-/blob/master/docs/release-notes.rst",
- "keyring": "https://github.com/jaraco/keyring/blob/main/NEWS.rst",
- "jaraco.classes": "https://github.com/jaraco/jaraco.classes/blob/main/NEWS.rst",
+ "keyring": "https://keyring.readthedocs.io/en/latest/history.html",
+ "jaraco.classes": "https://jaracoclasses.readthedocs.io/en/latest/history.html",
+ "jaraco.context": "https://jaracocontext.readthedocs.io/en/latest/history.html",
+ "jaraco.functools": "https://jaracofunctools.readthedocs.io/en/latest/history.html",
+ "backports.tarfile": "https://github.com/jaraco/backports.tarfile/blob/main/NEWS.rst",
"pkginfo": "https://bazaar.launchpad.net/~tseaver/pkginfo/trunk/view/head:/CHANGES.txt",
- "readme-renderer": "https://github.com/pypa/readme_renderer/blob/main/CHANGES.rst",
+ "readme_renderer": "https://github.com/pypa/readme_renderer/blob/main/CHANGES.rst",
"requests-toolbelt": "https://github.com/requests/toolbelt/blob/master/HISTORY.rst",
"rfc3986": "https://rfc3986.readthedocs.io/en/latest/release-notes/index.html",
"twine": "https://twine.readthedocs.io/en/stable/changelog.html",
diff --git a/scripts/dev/check_coverage.py b/scripts/dev/check_coverage.py
index 38a8f6ca1..e1d0d8642 100644
--- a/scripts/dev/check_coverage.py
+++ b/scripts/dev/check_coverage.py
@@ -73,8 +73,8 @@ PERFECT_FILES = [
'qutebrowser/browser/history.py'),
('tests/unit/browser/test_pdfjs.py',
'qutebrowser/browser/pdfjs.py'),
- ('tests/unit/browser/webkit/http/test_http.py',
- 'qutebrowser/browser/webkit/http.py'),
+ ('tests/unit/browser/webkit/http/test_httpheaders.py',
+ 'qutebrowser/browser/webkit/httpheaders.py'),
# ('tests/unit/browser/webkit/test_webkitelem.py',
# 'qutebrowser/browser/webkit/webkitelem.py'),
# ('tests/unit/browser/webkit/test_webkitelem.py',
diff --git a/scripts/importer.py b/scripts/importer.py
index 1914e6976..cf084d178 100755
--- a/scripts/importer.py
+++ b/scripts/importer.py
@@ -209,7 +209,7 @@ def import_html_bookmarks(bookmarks_file, bookmark_types, output_format):
}
bookmarks = []
for typ in bookmark_types:
- tags = soup.findAll(bookmark_query[typ])
+ tags = soup.find_all(bookmark_query[typ])
for tag in tags:
if typ == 'search':
tag['href'] = search_escape(tag['href']).replace('%s', '{}')
diff --git a/tests/end2end/data/click_element.html b/tests/end2end/data/click_element.html
index b2a691e08..7fac2a381 100644
--- a/tests/end2end/data/click_element.html
+++ b/tests/end2end/data/click_element.html
@@ -7,7 +7,7 @@
<span onclick='console.log("click_element special chars")'>"Don't", he shouted</span>
<span>Duplicate</span>
<span class='clickable' onclick='console.log("click_element CSS selector")'>Duplicate</span>
- <form><input autofocus id='qute-input'></input></form>
+ <form><input id='qute-input' onfocus='console.log("qute-input focused")'></input></form>
<a href="/data/hello.txt" id='link'>link</a>
<span id='foo.bar' onclick='console.log("id with dot")'>ID with dot</span>
<span style='position: absolute; left: 20px;top: 42px; width:10px; height:10px;'
diff --git a/tests/end2end/data/insert_mode_settings/html/autofocus.html b/tests/end2end/data/insert_mode_settings/html/autofocus.html
index 366f436f6..ca189b016 100644
--- a/tests/end2end/data/insert_mode_settings/html/autofocus.html
+++ b/tests/end2end/data/insert_mode_settings/html/autofocus.html
@@ -10,6 +10,9 @@
elem.addEventListener('input', function() {
console.log("contents: " + elem.value);
});
+ elem.addEventListener('focus', function() {
+ console.log("autofocus element focused");
+ });
}
</script>
</head>
diff --git a/tests/end2end/features/caret.feature b/tests/end2end/features/caret.feature
index 1302a1e6d..d6e65440c 100644
--- a/tests/end2end/features/caret.feature
+++ b/tests/end2end/features/caret.feature
@@ -34,11 +34,11 @@ Feature: Caret mode
Scenario: :yank selection with --keep
When I run :selection-toggle
- And I run :move-to-end-of-word
+ And I run :move-to-next-word
And I run :yank selection --keep
And I run :move-to-end-of-word
And I run :yank selection --keep
- Then the message "3 chars yanked to clipboard" should be shown
+ Then the message "4 chars yanked to clipboard" should be shown
And the message "7 chars yanked to clipboard" should be shown
And the clipboard should contain "one two"
diff --git a/tests/end2end/features/conftest.py b/tests/end2end/features/conftest.py
index 9b7ffdeb5..ce31125b6 100644
--- a/tests/end2end/features/conftest.py
+++ b/tests/end2end/features/conftest.py
@@ -730,8 +730,15 @@ def should_quit(qtbot, quteproc):
def _get_scroll_values(quteproc):
data = quteproc.get_session()
- pos = data['windows'][0]['tabs'][0]['history'][-1]['scroll-pos']
- return (pos['x'], pos['y'])
+
+ def get_active(things):
+ return next(thing for thing in things if thing.get("active"))
+
+ active_window = get_active(data["windows"])
+ active_tab = get_active(active_window["tabs"])
+ current_entry = get_active(active_tab["history"])
+ pos = current_entry["scroll-pos"]
+ return (pos["x"], pos["y"])
@bdd.then(bdd.parsers.re(r"the page should be scrolled "
diff --git a/tests/end2end/features/misc.feature b/tests/end2end/features/misc.feature
index 1872ca5a6..90ce5334a 100644
--- a/tests/end2end/features/misc.feature
+++ b/tests/end2end/features/misc.feature
@@ -502,14 +502,13 @@ Feature: Various utility commands.
Scenario: Clicking on focused element when there is none
When I open data/click_element.html
- # Need to loose focus on input element
- And I run :click-element position 20,42
- And I wait for the javascript message "click_element position"
And I run :click-element focused
Then the error "No element found with focus!" should be shown
Scenario: Clicking on focused element
When I open data/click_element.html
+ And I run :fake-key <Tab>
+ And I wait for the javascript message "qute-input focused"
And I run :click-element focused
Then "Entering mode KeyMode.insert (reason: clicking input)" should be logged
diff --git a/tests/end2end/features/utilcmds.feature b/tests/end2end/features/utilcmds.feature
index 1b5306adb..ebacea890 100644
--- a/tests/end2end/features/utilcmds.feature
+++ b/tests/end2end/features/utilcmds.feature
@@ -3,6 +3,7 @@ Feature: Miscellaneous utility commands exposed to the user.
Background:
Given I open data/scroll/simple.html
And I run :tab-only
+ And I run :window-only
## :cmd-later
diff --git a/tests/end2end/fixtures/quteprocess.py b/tests/end2end/fixtures/quteprocess.py
index 748d1d19e..e77757880 100644
--- a/tests/end2end/fixtures/quteprocess.py
+++ b/tests/end2end/fixtures/quteprocess.py
@@ -75,6 +75,8 @@ def is_ignored_lowlevel_message(message):
'glx: failed to create drisw screen',
'failed to load driver: zink',
'DRI3 not available',
+ # Webkit on arch with a newer mesa
+ 'MESA: error: ZINK: failed to load libvulkan.so.1',
]
return any(testutils.pattern_match(pattern=pattern, value=message)
for pattern in ignored_messages)
@@ -213,6 +215,16 @@ def is_ignored_chromium_message(line):
# [9895:9983:0904/043039.500565:ERROR:gpu_memory_buffer_support_x11.cc(49)]
# dri3 extension not supported.
"dri3 extension not supported.",
+
+ # Qt 6.7 debug build
+ # [44513:44717:0325/173456.146759:WARNING:render_message_filter.cc(144)]
+ # Could not find tid
+ "Could not find tid",
+
+ # [127693:127748:0325/230155.835421:WARNING:discardable_shared_memory_manager.cc(438)]
+ # Some MojoDiscardableSharedMemoryManagerImpls are still alive. They
+ # will be leaked.
+ "Some MojoDiscardableSharedMemoryManagerImpls are still alive. They will be leaked.",
]
return any(testutils.pattern_match(pattern=pattern, value=message)
for pattern in ignored_messages)
@@ -395,9 +407,11 @@ class QuteProc(testprocess.Process):
backend = 'webengine' if self.request.config.webengine else 'webkit'
args = ['--debug', '--no-err-windows', '--temp-basedir',
'--json-logging', '--loglevel', 'vdebug',
- '--backend', backend, '--debug-flag', 'no-sql-history',
- '--debug-flag', 'werror', '--debug-flag',
- 'test-notification-service',
+ '--backend', backend,
+ '--debug-flag', 'no-sql-history',
+ '--debug-flag', 'werror',
+ '--debug-flag', 'test-notification-service',
+ '--debug-flag', 'caret',
'--qt-flag', 'disable-features=PaintHoldingCrossOrigin']
if self.request.config.webengine and testutils.disable_seccomp_bpf_sandbox():
diff --git a/tests/end2end/fixtures/webserver.py b/tests/end2end/fixtures/webserver.py
index 5051efa85..924cb520b 100644
--- a/tests/end2end/fixtures/webserver.py
+++ b/tests/end2end/fixtures/webserver.py
@@ -16,6 +16,7 @@ import pytest
from qutebrowser.qt.core import pyqtSignal, QUrl
from end2end.fixtures import testprocess
+from helpers import testutils
class Request(testprocess.Line):
@@ -111,6 +112,17 @@ class ExpectedRequest:
return NotImplemented
+def is_ignored_webserver_message(line: str) -> bool:
+ return testutils.pattern_match(
+ pattern=(
+ "Client ('127.0.0.1', *) lost — peer dropped the TLS connection suddenly, "
+ "during handshake: (1, '[SSL: SSLV3_ALERT_CERTIFICATE_UNKNOWN] ssl/tls "
+ "alert certificate unknown (_ssl.c:*)')"
+ ),
+ value=line,
+ )
+
+
class WebserverProcess(testprocess.Process):
"""Abstraction over a running Flask server process.
@@ -151,7 +163,13 @@ class WebserverProcess(testprocess.Process):
if started_re.fullmatch(line):
self.ready.emit()
return None
- return Request(line)
+
+ try:
+ return Request(line)
+ except testprocess.InvalidLine:
+ if is_ignored_webserver_message(line):
+ return None
+ raise
def _executable_args(self):
if hasattr(sys, 'frozen'):
diff --git a/tests/end2end/test_insert_mode.py b/tests/end2end/test_insert_mode.py
index abf32dbde..95757591d 100644
--- a/tests/end2end/test_insert_mode.py
+++ b/tests/end2end/test_insert_mode.py
@@ -43,7 +43,6 @@ def test_insert_mode(file_name, elem_id, source, input_text, zoom,
(True, False, True), # enabled and foreground tab
(True, True, False), # background tab
])
-@pytest.mark.flaky
def test_auto_load(quteproc, auto_load, background, insert_mode):
quteproc.set_setting('input.insert_mode.auto_load', str(auto_load))
url_path = 'data/insert_mode_settings/html/autofocus.html'
diff --git a/tests/unit/browser/test_caret.py b/tests/unit/browser/test_caret.py
index ba1da4145..d51cc69ff 100644
--- a/tests/unit/browser/test_caret.py
+++ b/tests/unit/browser/test_caret.py
@@ -9,7 +9,8 @@ import textwrap
import pytest
from qutebrowser.qt.core import QUrl
-from qutebrowser.utils import usertypes
+from qutebrowser.qt import machinery
+from qutebrowser.utils import utils, usertypes
from qutebrowser.browser import browsertab
@@ -241,6 +242,13 @@ class TestWord:
caret.move_to_end_of_word()
selection.check("one")
+ @pytest.mark.xfail(
+ machinery.IS_QT6 and utils.is_windows,
+ reason=(
+ "move-to-end-of-word is broken with Qt 6 and Windows: "
+ "https://github.com/qutebrowser/qutebrowser/issues/8146"
+ )
+ )
def test_moving_to_end_and_selecting_a_word(self, caret, selection):
caret.move_to_end_of_word()
selection.toggle()
diff --git a/tests/unit/browser/webkit/http/test_content_disposition.py b/tests/unit/browser/webkit/http/test_content_disposition.py
index 7cf80e3fd..4f3ef13c7 100644
--- a/tests/unit/browser/webkit/http/test_content_disposition.py
+++ b/tests/unit/browser/webkit/http/test_content_disposition.py
@@ -6,7 +6,7 @@ import logging
import pytest
-from qutebrowser.browser.webkit import http
+from qutebrowser.browser.webkit import httpheaders
DEFAULT_NAME = 'qutebrowser-download'
@@ -30,7 +30,7 @@ class HeaderChecker:
"""Check if the passed header has the given filename."""
reply = self.stubs.FakeNetworkReply(
headers={'Content-Disposition': header})
- cd_inline, cd_filename = http.parse_content_disposition(reply)
+ cd_inline, cd_filename = httpheaders.parse_content_disposition(reply)
assert cd_filename is not None
assert cd_filename == filename
assert cd_inline == expected_inline
@@ -40,7 +40,7 @@ class HeaderChecker:
reply = self.stubs.FakeNetworkReply(
headers={'Content-Disposition': header})
with self.caplog.at_level(logging.ERROR, 'network'):
- cd_inline, cd_filename = http.parse_content_disposition(reply)
+ cd_inline, cd_filename = httpheaders.parse_content_disposition(reply)
assert cd_filename == DEFAULT_NAME
assert cd_inline
@@ -48,7 +48,7 @@ class HeaderChecker:
"""Check if the passed header results in an unnamed attachment."""
reply = self.stubs.FakeNetworkReply(
headers={'Content-Disposition': header})
- cd_inline, cd_filename = http.parse_content_disposition(reply)
+ cd_inline, cd_filename = httpheaders.parse_content_disposition(reply)
assert cd_filename == DEFAULT_NAME
assert not cd_inline
@@ -164,7 +164,7 @@ class TestAttachment:
"""
reply = stubs.FakeNetworkReply(
headers={'Content-Disposition': 'attachment'})
- cd_inline, cd_filename = http.parse_content_disposition(reply)
+ cd_inline, cd_filename = httpheaders.parse_content_disposition(reply)
assert not cd_inline
assert cd_filename == DEFAULT_NAME
diff --git a/tests/unit/browser/webkit/http/test_http.py b/tests/unit/browser/webkit/http/test_httpheaders.py
index 210d79486..7368575e8 100644
--- a/tests/unit/browser/webkit/http/test_http.py
+++ b/tests/unit/browser/webkit/http/test_httpheaders.py
@@ -2,7 +2,7 @@
#
# SPDX-License-Identifier: GPL-3.0-or-later
-"""Tests for qutebrowser.browser.webkit.http."""
+"""Tests for qutebrowser.browser.webkit.httpheaders."""
import logging
@@ -11,7 +11,7 @@ import hypothesis
from hypothesis import strategies
from qutebrowser.qt.core import QUrl
-from qutebrowser.browser.webkit import http
+from qutebrowser.browser.webkit import httpheaders
@pytest.mark.parametrize('url, expected', [
@@ -24,7 +24,7 @@ from qutebrowser.browser.webkit import http
])
def test_no_content_disposition(stubs, url, expected):
reply = stubs.FakeNetworkReply(url=QUrl(url))
- inline, filename = http.parse_content_disposition(reply)
+ inline, filename = httpheaders.parse_content_disposition(reply)
assert inline
assert filename == expected
@@ -40,8 +40,8 @@ def test_no_content_disposition(stubs, url, expected):
# dropping QtWebKit.
])
def test_parse_content_disposition_invalid(value):
- with pytest.raises(http.ContentDispositionError):
- http.ContentDisposition.parse(value)
+ with pytest.raises(httpheaders.ContentDispositionError):
+ httpheaders.ContentDisposition.parse(value)
@pytest.mark.parametrize('template', [
@@ -58,16 +58,16 @@ def test_parse_content_disposition_hypothesis(caplog, template, stubs, s):
header = template.format(s)
reply = stubs.FakeNetworkReply(headers={'Content-Disposition': header})
with caplog.at_level(logging.ERROR, 'network'):
- http.parse_content_disposition(reply)
+ httpheaders.parse_content_disposition(reply)
@hypothesis.given(strategies.binary())
def test_content_disposition_directly_hypothesis(s):
"""Test rfc6266 parsing directly with binary data."""
try:
- cd = http.ContentDisposition.parse(s)
+ cd = httpheaders.ContentDisposition.parse(s)
cd.filename()
- except http.ContentDispositionError:
+ except httpheaders.ContentDispositionError:
pass
@@ -83,7 +83,7 @@ def test_parse_content_type(stubs, content_type, expected_mimetype,
reply = stubs.FakeNetworkReply()
else:
reply = stubs.FakeNetworkReply(headers={'Content-Type': content_type})
- mimetype, rest = http.parse_content_type(reply)
+ mimetype, rest = httpheaders.parse_content_type(reply)
assert mimetype == expected_mimetype
assert rest == expected_rest
@@ -91,4 +91,4 @@ def test_parse_content_type(stubs, content_type, expected_mimetype,
@hypothesis.given(strategies.text())
def test_parse_content_type_hypothesis(stubs, s):
reply = stubs.FakeNetworkReply(headers={'Content-Type': s})
- http.parse_content_type(reply)
+ httpheaders.parse_content_type(reply)
diff --git a/tests/unit/browser/webkit/test_certificateerror.py b/tests/unit/browser/webkit/test_certificateerror.py
index 2feb7dcf1..7f5cce9c6 100644
--- a/tests/unit/browser/webkit/test_certificateerror.py
+++ b/tests/unit/browser/webkit/test_certificateerror.py
@@ -18,15 +18,15 @@ class FakeError:
return self.msg
-@pytest.mark.parametrize('errors, expected', [
+@pytest.mark.parametrize('error_factories, expected', [
(
- [QSslError(QSslError.SslError.UnableToGetIssuerCertificate)],
+ [lambda: QSslError(QSslError.SslError.UnableToGetIssuerCertificate)],
['<p>The issuer certificate could not be found</p>'],
),
(
[
- QSslError(QSslError.SslError.UnableToGetIssuerCertificate),
- QSslError(QSslError.SslError.UnableToDecryptCertificateSignature),
+ lambda: QSslError(QSslError.SslError.UnableToGetIssuerCertificate),
+ lambda: QSslError(QSslError.SslError.UnableToDecryptCertificateSignature),
],
[
'<ul>',
@@ -37,13 +37,13 @@ class FakeError:
),
(
- [FakeError('Escaping test: <>')],
+ [lambda: FakeError('Escaping test: <>')],
['<p>Escaping test: &lt;&gt;</p>'],
),
(
[
- FakeError('Escaping test 1: <>'),
- FakeError('Escaping test 2: <>'),
+ lambda: FakeError('Escaping test 1: <>'),
+ lambda: FakeError('Escaping test 2: <>'),
],
[
'<ul>',
@@ -53,8 +53,9 @@ class FakeError:
],
),
])
-def test_html(stubs, errors, expected):
+def test_html(stubs, error_factories, expected):
reply = stubs.FakeNetworkReply(url=QUrl("https://example.com"))
+ errors = [factory() for factory in error_factories]
wrapper = certificateerror.CertificateErrorWrapper(reply=reply, errors=errors)
lines = [line.strip() for line in wrapper.html().splitlines() if line.strip()]
assert lines == expected
diff --git a/tests/unit/config/test_configtypes.py b/tests/unit/config/test_configtypes.py
index d908e3ac6..bcd257ed7 100644
--- a/tests/unit/config/test_configtypes.py
+++ b/tests/unit/config/test_configtypes.py
@@ -1874,24 +1874,24 @@ class TestProxy:
def klass(self):
return configtypes.Proxy
- @pytest.mark.parametrize('val, expected', [
- ('system', configtypes.SYSTEM_PROXY),
- ('none', QNetworkProxy(QNetworkProxy.ProxyType.NoProxy)),
+ @pytest.mark.parametrize('val, expected_factory', [
+ ('system', lambda: configtypes.SYSTEM_PROXY),
+ ('none', lambda: QNetworkProxy(QNetworkProxy.ProxyType.NoProxy)),
('socks://example.com/',
- QNetworkProxy(QNetworkProxy.ProxyType.Socks5Proxy, 'example.com')),
+ lambda: QNetworkProxy(QNetworkProxy.ProxyType.Socks5Proxy, 'example.com')),
('socks5://foo:bar@example.com:2323',
- QNetworkProxy(QNetworkProxy.ProxyType.Socks5Proxy, 'example.com', 2323,
- 'foo', 'bar')),
+ lambda: QNetworkProxy(
+ QNetworkProxy.ProxyType.Socks5Proxy, 'example.com', 2323, 'foo', 'bar')),
('pac+http://example.com/proxy.pac',
- pac.PACFetcher(QUrl('pac+http://example.com/proxy.pac'))),
+ lambda: pac.PACFetcher(QUrl('pac+http://example.com/proxy.pac'))),
('pac+file:///tmp/proxy.pac',
- pac.PACFetcher(QUrl('pac+file:///tmp/proxy.pac'))),
+ lambda: pac.PACFetcher(QUrl('pac+file:///tmp/proxy.pac'))),
])
- def test_to_py_valid(self, klass, val, expected):
+ def test_to_py_valid(self, klass, val, expected_factory):
actual = klass().to_py(val)
if isinstance(actual, QNetworkProxy):
actual = QNetworkProxy(actual)
- assert actual == expected
+ assert actual == expected_factory()
@pytest.mark.parametrize('val', [
'blah',
diff --git a/tests/unit/misc/test_crashsignal.py b/tests/unit/misc/test_crashsignal.py
new file mode 100644
index 000000000..7019118e5
--- /dev/null
+++ b/tests/unit/misc/test_crashsignal.py
@@ -0,0 +1,104 @@
+# SPDX-FileCopyrightText: Florian Bruhin (The Compiler) <mail@qutebrowser.org>
+#
+# SPDX-License-Identifier: GPL-3.0-or-later
+
+"""Tests for qutebrowser.misc.crashsignal."""
+
+import signal
+
+import pytest
+
+from qutebrowser.config import configexc
+from qutebrowser.qt.widgets import QApplication
+from qutebrowser.misc import crashsignal, quitter
+
+
+@pytest.fixture
+def read_config_mock(mocker):
+ # covers reload_config
+ mocker.patch.object(
+ crashsignal.standarddir,
+ "config_py",
+ return_value="config.py-unittest",
+ )
+ return mocker.patch.object(
+ crashsignal.configfiles,
+ "read_config_py",
+ autospec=True,
+ )
+
+
+@pytest.fixture
+def signal_handler(qtbot, mocker, read_config_mock):
+ """Signal handler instance with all external methods mocked out."""
+ # covers init
+ mocker.patch.object(crashsignal.sys, "exit", autospec=True)
+ signal_handler = crashsignal.SignalHandler(
+ app=mocker.Mock(spec=QApplication),
+ quitter=mocker.Mock(spec=quitter.Quitter),
+ )
+
+ return signal_handler
+
+
+def test_handlers_registered(signal_handler):
+ signal_handler.activate()
+
+ for sig, handler in signal_handler._handlers.items():
+ registered = signal.signal(sig, signal.SIG_DFL)
+ assert registered == handler
+
+
+def test_handlers_deregistered(signal_handler):
+ known_handler = lambda *_args: None
+ for sig in signal_handler._handlers:
+ signal.signal(sig, known_handler)
+
+ signal_handler.activate()
+ signal_handler.deactivate()
+
+ for sig in signal_handler._handlers:
+ registered = signal.signal(sig, signal.SIG_DFL)
+ assert registered == known_handler
+
+
+def test_interrupt_repeatedly(signal_handler):
+ signal_handler.activate()
+ test_signal = signal.SIGINT
+
+ expected_handlers = [
+ signal_handler.interrupt,
+ signal_handler.interrupt_forcefully,
+ signal_handler.interrupt_really_forcefully,
+ ]
+
+ # Call the SIGINT handler multiple times and make sure it calls the
+ # expected sequence of functions.
+ for expected in expected_handlers:
+ registered = signal.signal(test_signal, signal.SIG_DFL)
+ assert registered == expected
+ expected(test_signal, None)
+
+
+@pytest.mark.posix
+def test_reload_config_call_on_hup(signal_handler, read_config_mock):
+ signal_handler._handlers[signal.SIGHUP](None, None)
+
+ read_config_mock.assert_called_once_with("config.py-unittest")
+
+
+@pytest.mark.posix
+def test_reload_config_displays_errors(signal_handler, read_config_mock, mocker):
+ read_config_mock.side_effect = configexc.ConfigFileErrors(
+ "config.py",
+ [
+ configexc.ConfigErrorDesc("no config.py", ValueError("asdf"))
+ ]
+ )
+ message_mock = mocker.patch.object(crashsignal.message, "error")
+
+ signal_handler._handlers[signal.SIGHUP](None, None)
+
+ message_mock.assert_called_once_with(
+ "Errors occurred while reading config.py:\n no config.py: asdf"
+ )
diff --git a/tests/unit/utils/test_qtutils.py b/tests/unit/utils/test_qtutils.py
index 3aceca668..c7af3162c 100644
--- a/tests/unit/utils/test_qtutils.py
+++ b/tests/unit/utils/test_qtutils.py
@@ -208,6 +208,18 @@ def test_ensure_valid(obj, raising, exc_reason, exc_str):
"The data stream has read corrupt data."),
(QDataStream.Status.WriteFailed, True,
"The data stream cannot write to the underlying device."),
+ pytest.param(
+ getattr(QDataStream.Status, "SizeLimitExceeded", None),
+ True,
+ (
+ "The data stream cannot read or write the data because its size is larger "
+ "than supported by the current platform."
+ ),
+ marks=pytest.mark.skipif(
+ not hasattr(QDataStream.Status, "SizeLimitExceeded"),
+ reason="Added in Qt 6.7"
+ )
+ ),
])
def test_check_qdatastream(status, raising, message):
"""Test check_qdatastream.
@@ -226,10 +238,25 @@ def test_check_qdatastream(status, raising, message):
qtutils.check_qdatastream(stream)
-def test_qdatastream_status_count():
- """Make sure no new members are added to QDataStream.Status."""
- status_vals = testutils.enum_members(QDataStream, QDataStream.Status)
- assert len(status_vals) == 4
+def test_qdatastream_status_members():
+ """Make sure no new members are added to QDataStream.Status.
+
+ If this fails, qtutils.check_qdatastream will need to be updated with the
+ respective error documentation.
+ """
+ status_vals = set(testutils.enum_members(QDataStream, QDataStream.Status).values())
+ expected = {
+ QDataStream.Status.Ok,
+ QDataStream.Status.ReadPastEnd,
+ QDataStream.Status.ReadCorruptData,
+ QDataStream.Status.WriteFailed,
+ }
+ try:
+ expected.add(QDataStream.Status.SizeLimitExceeded)
+ except AttributeError:
+ # Added in Qt 6.7
+ pass
+ assert status_vals == expected
@pytest.mark.parametrize('color, expected', [
diff --git a/tests/unit/utils/test_version.py b/tests/unit/utils/test_version.py
index 38134b40e..f24bf2a7a 100644
--- a/tests/unit/utils/test_version.py
+++ b/tests/unit/utils/test_version.py
@@ -915,6 +915,17 @@ class TestWebEngineVersions:
source='faked'),
"QtWebEngine 5.15.2, based on Chromium 87.0.4280.144 (from faked)",
),
+ (
+ version.WebEngineVersions(
+ webengine=utils.VersionNumber(5, 15, 2),
+ chromium='87.0.4280.144',
+ chromium_security='9000.1',
+ source='faked'),
+ (
+ "QtWebEngine 5.15.2, based on Chromium 87.0.4280.144, with security "
+ "patches up to 9000.1 (plus any distribution patches) (from faked)"
+ ),
+ ),
])
def test_str(self, version, expected):
assert str(version) == expected
@@ -1024,6 +1035,20 @@ class TestWebEngineVersions:
assert inferred == real
+ def test_real_chromium_security_version(self, qapp):
+ """Check the API for reading the chromium security patch version."""
+ try:
+ from qutebrowser.qt.webenginecore import (
+ qWebEngineChromiumVersion,
+ qWebEngineChromiumSecurityPatchVersion,
+ )
+ except ImportError:
+ pytest.skip("Requires QtWebEngine 6.3+")
+
+ base = utils.VersionNumber.parse(qWebEngineChromiumVersion())
+ security = utils.VersionNumber.parse(qWebEngineChromiumSecurityPatchVersion())
+ assert security >= base
+
class FakeQSslSocket:
diff --git a/tox.ini b/tox.ini
index 238c532f3..31e06e396 100644
--- a/tox.ini
+++ b/tox.ini
@@ -16,8 +16,6 @@ setenv =
pyqt{515,5152}: PYTEST_QT_API=pyqt5
pyqt{515,5152}: QUTE_QT_WRAPPER=PyQt5
cov: PYTEST_ADDOPTS=--cov --cov-report xml --cov-report=html --cov-report=
- py312: VIRTUALENV_PIP=23.2
- py312: PIP_REQUIRE_VIRTUALENV=0
passenv =
PYTHON
DISPLAY
@@ -71,8 +69,8 @@ setenv =
pip_pre = true
deps = -r{toxinidir}/misc/requirements/requirements-tests-bleeding.txt
commands_pre =
- qt5: pip install --extra-index-url https://www.riverbankcomputing.com/pypi/simple/ --pre --upgrade PyQt5 PyQtWebEngine PyQt5-Qt5 PyQtWebEngine-Qt5 PyQt5-sip
- !qt5: pip install --extra-index-url https://www.riverbankcomputing.com/pypi/simple/ --pre --upgrade PyQt6 PyQt6-WebEngine PyQt6-Qt6 PyQt6-WebEngine-Qt6 PyQt6-sip
+ qt5: pip install --extra-index-url https://www.riverbankcomputing.com/pypi/simple/ --pre --upgrade --only-binary PyQt5,PyQtWebEngine PyQt5 PyQtWebEngine PyQt5-Qt5 PyQtWebEngine-Qt5 PyQt5-sip
+ !qt5: pip install --extra-index-url https://www.riverbankcomputing.com/pypi/simple/ --pre --upgrade --only-binary PyQt6,PyQt6-WebEngine PyQt6 PyQt6-WebEngine PyQt6-Qt6 PyQt6-WebEngine-Qt6 PyQt6-sip
commands = {envpython} -bb -m pytest {posargs:tests}
# other envs