diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 2d4f0bd47..e381121a7 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -91,8 +91,6 @@ jobs: fail-fast: false matrix: include: - - testenv: py-qt5 - image: archlinux-webkit - testenv: py-qt5 image: archlinux-webengine - testenv: py-qt5 @@ -194,28 +192,29 @@ jobs: os: ubuntu-24.04 python: "3.13" ### PyQt 6.8 (Python 3.14) - # Pinned to Alpha 6: - # https://www.riverbankcomputing.com/pipermail/pyqt/2025-April/046210.html - testenv: py314-pyqt68 os: ubuntu-24.04 - python: "3.14.0-alpha.6" + python: "3.14-dev" ### PyQt 6.9 (Python 3.13) - testenv: py313-pyqt69 os: ubuntu-24.04 python: "3.13" ### macOS Ventura - - testenv: py313-pyqt68 + - testenv: py313-pyqt69 os: macos-13 python: "3.13" args: "tests/unit" # Only run unit tests on macOS ### macOS Sonoma (M1 runner) - - testenv: py313-pyqt68 + - testenv: py313-pyqt69 os: macos-14 python: "3.13" args: "tests/unit" # Only run unit tests on macOS ### Windows - - testenv: py313-pyqt68 - os: windows-2019 + - testenv: py313-pyqt69 + os: windows-2022 + python: "3.13" + - testenv: py313-pyqt69 + os: windows-2025 python: "3.13" runs-on: "${{ matrix.os }}" steps: diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml index 75f9c9082..b83951c00 100644 --- a/.github/workflows/docker.yml +++ b/.github/workflows/docker.yml @@ -13,7 +13,6 @@ jobs: fail-fast: false matrix: image: - - archlinux-webkit - archlinux-webengine - archlinux-webengine-unstable - archlinux-webengine-unstable-qt6 diff --git a/doc/changelog.asciidoc b/doc/changelog.asciidoc index 8ce0eb020..cf9b82a32 100644 --- a/doc/changelog.asciidoc +++ b/doc/changelog.asciidoc @@ -15,6 +15,45 @@ breaking changes (such as renamed commands) can happen in minor releases. // `Fixed` for any bug fixes. // `Security` to invite users to upgrade in case of vulnerabilities. +[[v3.5.1]] +v3.5.1 (unreleased) +------------------- + +Deprecated +~~~~~~~~~~ + +- QtWebKit (legacy) support got removed from CI and is now untested. If it + breaks, it's not going to be fixed, and support will be removed over the next + releases. +- Qt 5 support is currently still tested, but is also planned to get removed + over the next releases. Same goes for support for older Qt 6 versions (likely + 6.2/6.3/6.4 and perhaps 6.5, see https://github.com/qutebrowser/qutebrowser/issues/8464[#8464]). + +Changed +~~~~~~~ + +- Windows/macOS releases now bundle Qt 6.9.1, including many graphics-related + bugfixes, as well as security patches up to Chromium 136.0.7103.114. + +Fixed +~~~~~ + +- A bogus "wildcard call disconnects from destroyed signal" warning from Qt is + now suppressed. +- PDF.js now loads correctly on Windows installations with broken mimetype + configurations. +- A "Ignoring new child ..." debug log message which got spammy with Qt 6.9 is + now removed. +- A unknown crash (possibly related to using devtools) due to weird (Py)Qt + behavior now has a workaround. +- No "QtWebEngine version mismatch" warning is now logged anymore with newer Qt + 5.15 releases (but you should still stop using Qt 5). +- The PDF.js version can now correctly be extracted/displayed with newer PDF.js + versions. +- The `qute-bitwarden`, `-lastpass` and `-pass` userscripts now properly avoid + a `DeprecationWarning` from the upcoming 6.0 release of `tldextract`. The + previous fix in v3.5.1 was insufficient. + [[v3.5.0]] v3.5.0 (unreleased) ------------------- @@ -25,6 +64,7 @@ Changed - Windows/macOS releases are now built with Qt 6.9.0 * Based on Chromium 130.0.6723.192 * Security fixes up to Chromium 133.0.6943.141 + * Also fixes issues with opening links on macOS - The `content.headers.user_agent` setting now has a new `{upstream_browser_version_short}` template field, which is the upstream/Chromium version but shortened to only major version. diff --git a/doc/help/configuring.asciidoc b/doc/help/configuring.asciidoc index 7f4b2396c..a1acc9d47 100644 --- a/doc/help/configuring.asciidoc +++ b/doc/help/configuring.asciidoc @@ -417,6 +417,7 @@ Pre-built colorschemes - https://github.com/catppuccin/qutebrowser[Catppuccin] - https://github.com/iruzo/matrix-qutebrowser[Matrix] - https://github.com/harmtemolder/qutebrowser-solarized[Solarized] +- https://github.com/Rehpotsirhc-z/qutebrowser-doom-one[Doom One] Avoiding flake8 errors ^^^^^^^^^^^^^^^^^^^^^^ diff --git a/misc/requirements/requirements-check-manifest.txt b/misc/requirements/requirements-check-manifest.txt index 1aede53ae..7b9da018b 100644 --- a/misc/requirements/requirements-check-manifest.txt +++ b/misc/requirements/requirements-check-manifest.txt @@ -2,8 +2,8 @@ build==1.2.2.post1 check-manifest==0.50 -importlib_metadata==8.6.1 -packaging==24.2 +importlib_metadata==8.7.0 +packaging==25.0 pyproject_hooks==1.2.0 tomli==2.2.1 -zipp==3.21.0 +zipp==3.22.0 diff --git a/misc/requirements/requirements-dev.txt b/misc/requirements/requirements-dev.txt index 4cd7ba6ab..e061bfbe7 100644 --- a/misc/requirements/requirements-dev.txt +++ b/misc/requirements/requirements-dev.txt @@ -6,22 +6,22 @@ autocommand==2.2.2 backports.tarfile==1.2.0 bracex==2.5.post1 build==1.2.2.post1 -bump-my-version==1.1.1 -certifi==2025.1.31 +bump-my-version==1.1.4 +certifi==2025.4.26 cffi==1.17.1 -charset-normalizer==3.4.1 +charset-normalizer==3.4.2 click==8.1.8 -cryptography==44.0.2 +cryptography==45.0.3 docutils==0.21.2 -exceptiongroup==1.2.2 +exceptiongroup==1.3.0 github3.py==4.0.1 -h11==0.14.0 -httpcore==1.0.7 +h11==0.16.0 +httpcore==1.0.9 httpx==0.28.1 hunter==3.7.0 id==1.5.0 idna==3.10 -importlib_metadata==8.6.1 +importlib_metadata==8.7.0 importlib_resources==6.5.2 inflect==7.3.1 jaraco.classes==3.4.0 @@ -34,20 +34,20 @@ keyring==25.6.0 manhole==1.8.1 markdown-it-py==3.0.0 mdurl==0.1.2 -more-itertools==10.6.0 +more-itertools==10.7.0 nh3==0.2.21 -packaging==24.2 -platformdirs==4.3.7 -prompt_toolkit==3.0.50 +packaging==25.0 +platformdirs==4.3.8 +prompt_toolkit==3.0.51 pycparser==2.22 -pydantic==2.11.1 -pydantic-settings==2.8.1 -pydantic_core==2.33.0 +pydantic==2.11.5 +pydantic-settings==2.9.1 +pydantic_core==2.33.2 Pygments==2.19.1 PyJWT==2.10.1 Pympler==1.1 pyproject_hooks==1.2.0 -PyQt-builder==1.18.1 +PyQt-builder==1.18.2 python-dateutil==2.9.0.post0 python-dotenv==1.1.0 questionary==2.1.0 @@ -56,19 +56,19 @@ requests==2.32.3 requests-toolbelt==1.0.0 rfc3986==2.0.0 rich==14.0.0 -rich-click==1.8.8 +rich-click==1.8.9 SecretStorage==3.3.3 -sip==6.10.0 +sip==6.12.0 six==1.17.0 sniffio==1.3.1 tomli==2.2.1 tomlkit==0.13.2 twine==6.1.0 typeguard==4.3.0 -typing-inspection==0.4.0 -typing_extensions==4.13.0 -uritemplate==4.1.1 -# urllib3==2.3.0 +typing-inspection==0.4.1 +typing_extensions==4.14.0 +uritemplate==4.2.0 +# urllib3==2.4.0 wcmatch==10.0 wcwidth==0.2.13 -zipp==3.21.0 +zipp==3.22.0 diff --git a/misc/requirements/requirements-flake8.txt b/misc/requirements/requirements-flake8.txt index 33638de7e..07948461d 100644 --- a/misc/requirements/requirements-flake8.txt +++ b/misc/requirements/requirements-flake8.txt @@ -15,9 +15,9 @@ flake8-string-format==0.3.0 flake8-tidy-imports==4.11.0 flake8-tuple==0.4.1 mccabe==0.7.0 -pep8-naming==0.14.1 +pep8-naming==0.15.1 pycodestyle==2.13.0 pydocstyle==6.3.0 -pyflakes==3.3.1 +pyflakes==3.3.2 six==1.17.0 -snowballstemmer==2.2.0 +snowballstemmer==3.0.1 diff --git a/misc/requirements/requirements-mypy.txt b/misc/requirements/requirements-mypy.txt index 893736057..4fbc833e8 100644 --- a/misc/requirements/requirements-mypy.txt +++ b/misc/requirements/requirements-mypy.txt @@ -1,18 +1,19 @@ # This file is automatically generated by scripts/dev/recompile_requirements.py chardet==5.2.0 -diff_cover==9.2.4 +diff_cover==9.3.2 Jinja2==3.1.6 -lxml==5.3.1 +lxml==5.4.0 MarkupSafe==3.0.2 -mypy==1.15.0 -mypy-extensions==1.0.0 -pluggy==1.5.0 +mypy==1.16.0 +mypy_extensions==1.1.0 +pathspec==0.12.1 +pluggy==1.6.0 Pygments==2.19.1 PyQt5-stubs==5.15.6.0 tomli==2.2.1 types-colorama==0.4.15.20240311 -types-docutils==0.21.0.20241128 -types-Pygments==2.19.0.20250305 -types-PyYAML==6.0.12.20250326 -typing_extensions==4.13.0 +types-docutils==0.21.0.20250604 +types-Pygments==2.19.0.20250516 +types-PyYAML==6.0.12.20250516 +typing_extensions==4.14.0 diff --git a/misc/requirements/requirements-pyinstaller.txt b/misc/requirements/requirements-pyinstaller.txt index b309a56d9..06450c6d5 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==8.6.1 -packaging==24.2 -pyinstaller==6.12.0 -pyinstaller-hooks-contrib==2025.2 -zipp==3.21.0 +importlib_metadata==8.7.0 +packaging==25.0 +pyinstaller==6.14.0 +pyinstaller-hooks-contrib==2025.4 +zipp==3.22.0 diff --git a/misc/requirements/requirements-pylint.txt b/misc/requirements/requirements-pylint.txt index 3d7a9d4ac..970dda16a 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.3.9 -certifi==2025.1.31 +astroid==3.3.10 +certifi==2025.4.26 cffi==1.17.1 -charset-normalizer==3.4.1 -cryptography==44.0.2 -dill==0.3.9 +charset-normalizer==3.4.2 +cryptography==45.0.3 +dill==0.4.0 github3.py==4.0.1 idna==3.10 isort==6.0.1 mccabe==0.7.0 pefile==2024.8.26 -platformdirs==4.3.7 +platformdirs==4.3.8 pycparser==2.22 PyJWT==2.10.1 -pylint==3.3.6 +pylint==3.3.7 python-dateutil==2.9.0.post0 ./scripts/dev/pylint_checkers requests==2.32.3 six==1.17.0 tomli==2.2.1 tomlkit==0.13.2 -typing_extensions==4.13.0 -uritemplate==4.1.1 -# urllib3==2.3.0 +typing_extensions==4.14.0 +uritemplate==4.2.0 +# urllib3==2.4.0 diff --git a/misc/requirements/requirements-pyqt-5.15.txt b/misc/requirements/requirements-pyqt-5.15.txt index 927508839..b4d82580c 100644 --- a/misc/requirements/requirements-pyqt-5.15.txt +++ b/misc/requirements/requirements-pyqt-5.15.txt @@ -1,7 +1,7 @@ # This file is automatically generated by scripts/dev/recompile_requirements.py PyQt5==5.15.11 # rq.filter: < 5.16 -PyQt5-Qt5==5.15.16 +PyQt5-Qt5==5.15.17 PyQt5_sip==12.17.0 PyQtWebEngine==5.15.7 # rq.filter: < 5.16 -PyQtWebEngine-Qt5==5.15.16 +PyQtWebEngine-Qt5==5.15.17 diff --git a/misc/requirements/requirements-pyqt-5.txt b/misc/requirements/requirements-pyqt-5.txt index 292f35e7d..0f6357bda 100644 --- a/misc/requirements/requirements-pyqt-5.txt +++ b/misc/requirements/requirements-pyqt-5.txt @@ -1,7 +1,7 @@ # This file is automatically generated by scripts/dev/recompile_requirements.py PyQt5==5.15.11 -PyQt5-Qt5==5.15.16 +PyQt5-Qt5==5.15.17 PyQt5_sip==12.17.0 PyQtWebEngine==5.15.7 -PyQtWebEngine-Qt5==5.15.16 +PyQtWebEngine-Qt5==5.15.17 diff --git a/misc/requirements/requirements-pyqt-6.2.txt b/misc/requirements/requirements-pyqt-6.2.txt index 49a247a16..8847801ea 100644 --- a/misc/requirements/requirements-pyqt-6.2.txt +++ b/misc/requirements/requirements-pyqt-6.2.txt @@ -4,4 +4,4 @@ PyQt6==6.2.3 PyQt6-Qt6==6.2.4 PyQt6-WebEngine==6.2.1 PyQt6-WebEngine-Qt6==6.2.4 -PyQt6_sip==13.10.0 +PyQt6_sip==13.10.2 diff --git a/misc/requirements/requirements-pyqt-6.3.txt b/misc/requirements/requirements-pyqt-6.3.txt index aa42e285b..60e16e4f5 100644 --- a/misc/requirements/requirements-pyqt-6.3.txt +++ b/misc/requirements/requirements-pyqt-6.3.txt @@ -4,4 +4,4 @@ PyQt6==6.3.1 PyQt6-Qt6==6.3.2 PyQt6-WebEngine==6.3.1 PyQt6-WebEngine-Qt6==6.3.2 -PyQt6_sip==13.10.0 +PyQt6_sip==13.10.2 diff --git a/misc/requirements/requirements-pyqt-6.4.txt b/misc/requirements/requirements-pyqt-6.4.txt index 134840e15..fc87b99f5 100644 --- a/misc/requirements/requirements-pyqt-6.4.txt +++ b/misc/requirements/requirements-pyqt-6.4.txt @@ -4,4 +4,4 @@ PyQt6==6.4.2 PyQt6-Qt6==6.4.3 PyQt6-WebEngine==6.4.0 PyQt6-WebEngine-Qt6==6.4.3 -PyQt6_sip==13.10.0 +PyQt6_sip==13.10.2 diff --git a/misc/requirements/requirements-pyqt-6.5.txt b/misc/requirements/requirements-pyqt-6.5.txt index 47d2626a8..d599b2ed4 100644 --- a/misc/requirements/requirements-pyqt-6.5.txt +++ b/misc/requirements/requirements-pyqt-6.5.txt @@ -4,4 +4,4 @@ PyQt6==6.5.3 PyQt6-Qt6==6.5.3 PyQt6-WebEngine==6.5.0 PyQt6-WebEngine-Qt6==6.5.3 -PyQt6_sip==13.10.0 +PyQt6_sip==13.10.2 diff --git a/misc/requirements/requirements-pyqt-6.6.txt b/misc/requirements/requirements-pyqt-6.6.txt index da1b2dc3c..67da9711d 100644 --- a/misc/requirements/requirements-pyqt-6.6.txt +++ b/misc/requirements/requirements-pyqt-6.6.txt @@ -4,4 +4,4 @@ PyQt6==6.6.1 PyQt6-Qt6==6.6.3 PyQt6-WebEngine==6.6.0 PyQt6-WebEngine-Qt6==6.6.3 -PyQt6_sip==13.10.0 +PyQt6_sip==13.10.2 diff --git a/misc/requirements/requirements-pyqt-6.7.txt b/misc/requirements/requirements-pyqt-6.7.txt index 463f8f6b9..b99759404 100644 --- a/misc/requirements/requirements-pyqt-6.7.txt +++ b/misc/requirements/requirements-pyqt-6.7.txt @@ -5,4 +5,4 @@ PyQt6-Qt6==6.7.3 PyQt6-WebEngine==6.7.0 PyQt6-WebEngine-Qt6==6.7.3 PyQt6-WebEngineSubwheel-Qt6==6.7.3 -PyQt6_sip==13.10.0 +PyQt6_sip==13.10.2 diff --git a/misc/requirements/requirements-pyqt-6.8.txt b/misc/requirements/requirements-pyqt-6.8.txt index 84c0d21f7..6669da8b2 100644 --- a/misc/requirements/requirements-pyqt-6.8.txt +++ b/misc/requirements/requirements-pyqt-6.8.txt @@ -4,4 +4,4 @@ PyQt6==6.8.1 PyQt6-Qt6==6.8.2 PyQt6-WebEngine==6.8.0 PyQt6-WebEngine-Qt6==6.8.2 -PyQt6_sip==13.10.0 +PyQt6_sip==13.10.2 diff --git a/misc/requirements/requirements-pyqt-6.9.txt b/misc/requirements/requirements-pyqt-6.9.txt index a70577da1..c6a8eb9bf 100644 --- a/misc/requirements/requirements-pyqt-6.9.txt +++ b/misc/requirements/requirements-pyqt-6.9.txt @@ -1,7 +1,7 @@ # This file is automatically generated by scripts/dev/recompile_requirements.py PyQt6==6.9.0 -PyQt6-Qt6==6.9.0 +PyQt6-Qt6==6.9.1 PyQt6-WebEngine==6.9.0 -PyQt6-WebEngine-Qt6==6.9.0 -PyQt6_sip==13.10.0 +PyQt6-WebEngine-Qt6==6.9.1 +PyQt6_sip==13.10.2 diff --git a/misc/requirements/requirements-pyqt-6.txt b/misc/requirements/requirements-pyqt-6.txt index a70577da1..c6a8eb9bf 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.9.0 -PyQt6-Qt6==6.9.0 +PyQt6-Qt6==6.9.1 PyQt6-WebEngine==6.9.0 -PyQt6-WebEngine-Qt6==6.9.0 -PyQt6_sip==13.10.0 +PyQt6-WebEngine-Qt6==6.9.1 +PyQt6_sip==13.10.2 diff --git a/misc/requirements/requirements-pyqt.txt b/misc/requirements/requirements-pyqt.txt index a70577da1..c6a8eb9bf 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.9.0 -PyQt6-Qt6==6.9.0 +PyQt6-Qt6==6.9.1 PyQt6-WebEngine==6.9.0 -PyQt6-WebEngine-Qt6==6.9.0 -PyQt6_sip==13.10.0 +PyQt6-WebEngine-Qt6==6.9.1 +PyQt6_sip==13.10.2 diff --git a/misc/requirements/requirements-pyroma.txt b/misc/requirements/requirements-pyroma.txt index a2b9519d3..1dc373b3d 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.2.2.post1 -certifi==2025.1.31 -charset-normalizer==3.4.1 +certifi==2025.4.26 +charset-normalizer==3.4.2 docutils==0.21.2 idna==3.10 -importlib_metadata==8.6.1 -packaging==24.2 +importlib_metadata==8.7.0 +packaging==25.0 Pygments==2.19.1 pyproject_hooks==1.2.0 pyroma==4.2 requests==2.32.3 tomli==2.2.1 -trove-classifiers==2025.3.19.19 -urllib3==2.3.0 -zipp==3.21.0 +trove-classifiers==2025.5.9.12 +urllib3==2.4.0 +zipp==3.22.0 diff --git a/misc/requirements/requirements-sphinx.txt b/misc/requirements/requirements-sphinx.txt index abe6d9c69..6d0e7b09c 100644 --- a/misc/requirements/requirements-sphinx.txt +++ b/misc/requirements/requirements-sphinx.txt @@ -2,18 +2,18 @@ alabaster==0.7.16 babel==2.17.0 -certifi==2025.1.31 -charset-normalizer==3.4.1 +certifi==2025.4.26 +charset-normalizer==3.4.2 docutils==0.21.2 idna==3.10 imagesize==1.4.1 -importlib_metadata==8.6.1 +importlib_metadata==8.7.0 Jinja2==3.1.6 MarkupSafe==3.0.2 -packaging==24.2 +packaging==25.0 Pygments==2.19.1 requests==2.32.3 -snowballstemmer==2.2.0 +snowballstemmer==3.0.1 Sphinx==7.4.7 sphinxcontrib-applehelp==2.0.0 sphinxcontrib-devhelp==2.0.0 @@ -22,5 +22,5 @@ sphinxcontrib-jsmath==1.0.1 sphinxcontrib-qthelp==2.0.0 sphinxcontrib-serializinghtml==2.0.0 tomli==2.2.1 -urllib3==2.3.0 -zipp==3.21.0 +urllib3==2.4.0 +zipp==3.22.0 diff --git a/misc/requirements/requirements-tests.txt b/misc/requirements/requirements-tests.txt index 8075e5d8e..e323f7429 100644 --- a/misc/requirements/requirements-tests.txt +++ b/misc/requirements/requirements-tests.txt @@ -3,22 +3,22 @@ attrs==25.3.0 autocommand==2.2.2 backports.tarfile==1.2.0 -beautifulsoup4==4.13.3 +beautifulsoup4==4.13.4 blinker==1.9.0 -certifi==2025.1.31 -charset-normalizer==3.4.1 +certifi==2025.4.26 +charset-normalizer==3.4.2 cheroot==10.0.1 click==8.1.8 -coverage==7.8.0 -exceptiongroup==1.2.2 +coverage==7.8.2 +exceptiongroup==1.3.0 execnet==2.1.1 filelock==3.18.0 -Flask==3.1.0 +Flask==3.1.1 gherkin-official==29.0.0 hunter==3.7.0 -hypothesis==6.130.5 +hypothesis==6.135.0 idna==3.10 -importlib_metadata==8.6.1 +importlib_metadata==8.7.0 importlib_resources==6.5.2 inflect==7.3.1 iniconfig==2.1.0 @@ -28,40 +28,40 @@ jaraco.context==6.0.1 jaraco.functools==4.1.0 jaraco.text==3.12.1 # Jinja2==3.1.6 -Mako==1.3.9 +Mako==1.3.10 manhole==1.8.1 # MarkupSafe==3.0.2 -more-itertools==10.6.0 -packaging==24.2 +more-itertools==10.7.0 +packaging==25.0 parse==1.20.2 parse_type==0.6.4 -pillow==11.1.0 -platformdirs==4.3.7 -pluggy==1.5.0 +pillow==11.2.1 +platformdirs==4.3.8 +pluggy==1.6.0 py-cpuinfo==9.0.0 Pygments==2.19.1 -pytest==8.3.5 +pytest==8.4.0 pytest-bdd==8.1.0 pytest-benchmark==5.1.0 -pytest-cov==6.0.0 +pytest-cov==6.1.1 pytest-instafail==0.5.0 -pytest-mock==3.14.0 +pytest-mock==3.14.1 pytest-qt==4.4.0 -pytest-repeat==0.9.3 -pytest-rerunfailures==15.0 -pytest-xdist==3.6.1 +pytest-repeat==0.9.4 +pytest-rerunfailures==15.1 +pytest-xdist==3.7.0 pytest-xvfb==3.1.1 PyVirtualDisplay==3.0 requests==2.32.3 requests-file==2.1.0 six==1.17.0 sortedcontainers==2.4.0 -soupsieve==2.6 -tldextract==5.1.3 +soupsieve==2.7 +tldextract==5.3.0 tomli==2.2.1 typeguard==4.3.0 -typing_extensions==4.13.0 -urllib3==2.3.0 +typing_extensions==4.14.0 +urllib3==2.4.0 vulture==2.14 Werkzeug==3.1.3 -zipp==3.21.0 +zipp==3.22.0 diff --git a/misc/requirements/requirements-tox.txt b/misc/requirements/requirements-tox.txt index f036395fb..fd7a59f5a 100644 --- a/misc/requirements/requirements-tox.txt +++ b/misc/requirements/requirements-tox.txt @@ -1,18 +1,19 @@ # This file is automatically generated by scripts/dev/recompile_requirements.py -cachetools==5.5.2 +cachetools==6.0.0 chardet==5.2.0 colorama==0.4.6 distlib==0.3.9 filelock==3.18.0 -packaging==24.2 -pip==25.0.1 -platformdirs==4.3.7 -pluggy==1.5.0 -pyproject-api==1.9.0 -setuptools==78.1.0 +packaging==25.0 +pip==25.1.1 +platformdirs==4.3.8 +pluggy==1.6.0 +pyproject-api==1.9.1 +setuptools==80.9.0 tomli==2.2.1 -tox==4.25.0 -typing_extensions==4.13.0 -virtualenv==20.29.3 +tox==4.26.0 ; python_full_version!="3.14.0b1" +typing_extensions==4.14.0 +virtualenv==20.31.2 wheel==0.45.1 +tox @ git+https://github.com/tox-dev/tox ; python_full_version=="3.14.0b1" diff --git a/misc/requirements/requirements-tox.txt-raw b/misc/requirements/requirements-tox.txt-raw index 27d58e1f4..face7016d 100644 --- a/misc/requirements/requirements-tox.txt-raw +++ b/misc/requirements/requirements-tox.txt-raw @@ -1,2 +1,5 @@ tox wheel + +#@ markers: tox python_full_version!="3.14.0b1" +#@ add: tox @ git+https://github.com/tox-dev/tox ; python_full_version=="3.14.0b1" diff --git a/misc/requirements/requirements-yamllint.txt b/misc/requirements/requirements-yamllint.txt index 67862705e..bdddcc2b1 100644 --- a/misc/requirements/requirements-yamllint.txt +++ b/misc/requirements/requirements-yamllint.txt @@ -2,4 +2,4 @@ pathspec==0.12.1 PyYAML==6.0.2 -yamllint==1.37.0 +yamllint==1.37.1 diff --git a/pytest.ini b/pytest.ini index 54df9528c..ad2a689a5 100644 --- a/pytest.ini +++ b/pytest.ini @@ -89,6 +89,8 @@ qt_log_ignore = ^QRhiGles2: Failed to create (temporary )?context$ ^QVulkanInstance: Failed to initialize Vulkan$ ^Unable to detect GPU vendor\.$ + # Qt 5 on CI with WebKit + ^qglx_findConfig: Failed to finding matching FBConfig for QSurfaceFormat\(version 2\.0, options QFlags\(\), depthBufferSize -1, redBufferSize 1, greenBufferSize 1, blueBufferSize 1, alphaBufferSize -1, stencilBufferSize -1, samples -1, swapBehavior QSurfaceFormat::SingleBuffer, swapInterval 1, colorSpace QSurfaceFormat::DefaultColorSpace, profile QSurfaceFormat::NoProfile\)$ xfail_strict = true filterwarnings = error diff --git a/qutebrowser/browser/eventfilter.py b/qutebrowser/browser/eventfilter.py index 1cff11ac4..2e6cc3a2c 100644 --- a/qutebrowser/browser/eventfilter.py +++ b/qutebrowser/browser/eventfilter.py @@ -6,11 +6,12 @@ from qutebrowser.qt import machinery from qutebrowser.qt.core import QObject, QEvent, Qt, QTimer +from qutebrowser.qt.gui import QKeyEvent from qutebrowser.qt.widgets import QWidget from qutebrowser.config import config from qutebrowser.utils import log, message, usertypes, qtutils -from qutebrowser.keyinput import modeman +from qutebrowser.keyinput import modeman, keyutils class ChildEventFilter(QObject): @@ -37,8 +38,8 @@ class ChildEventFilter(QObject): if event.type() == QEvent.Type.ChildAdded: child = event.child() if not isinstance(child, QWidget): - # Can e.g. happen when dragging text - log.misc.debug(f"Ignoring new child {qtutils.qobj_repr(child)}") + # Can e.g. happen when dragging text, or accessibility tree + # nodes since Qt 6.9 return False log.misc.debug( @@ -69,6 +70,15 @@ class ChildEventFilter(QObject): child.installEventFilter(self._filter) elif event.type() == QEvent.Type.ChildRemoved: + if isinstance(event, QKeyEvent): + # WORKAROUND for unknown (Py)Qt bug + info = keyutils.KeyInfo.from_event(event) + log.misc.warning( + f"ChildEventFilter: ignoring key event {info} " + f"on {qtutils.qobj_repr(obj)}" + ) + return False + child = event.child() log.misc.debug( f"{qtutils.qobj_repr(obj)}: removed child {qtutils.qobj_repr(child)}") diff --git a/qutebrowser/browser/webengine/webenginesettings.py b/qutebrowser/browser/webengine/webenginesettings.py index 2d2d3c188..d16750bf4 100644 --- a/qutebrowser/browser/webengine/webenginesettings.py +++ b/qutebrowser/browser/webengine/webenginesettings.py @@ -453,8 +453,19 @@ def _init_default_profile(): init_user_agent() ua_version = version.qtwebengine_versions() + + logger = log.init.warning + if machinery.IS_QT5: + # With Qt 5.15, we can't quite be sure about which QtWebEngine patch version + # we're getting, as ELF parsing might be broken and there's no other way. + # For most of the code, we don't really care about the patch version though. + assert ( + non_ua_version.webengine.strip_patch() == ua_version.webengine.strip_patch() + ), (non_ua_version, ua_version) + logger = log.init.debug + if ua_version.webengine != non_ua_version.webengine: - log.init.warning( + logger( "QtWebEngine version mismatch - unexpected behavior might occur, " "please open a bug about this.\n" f" Early version: {non_ua_version}\n" diff --git a/qutebrowser/browser/webengine/webenginetab.py b/qutebrowser/browser/webengine/webenginetab.py index cebc77767..98b6e275c 100644 --- a/qutebrowser/browser/webengine/webenginetab.py +++ b/qutebrowser/browser/webengine/webenginetab.py @@ -626,7 +626,14 @@ class WebEngineHistoryPrivate(browsertab.AbstractHistoryPrivate): return data def deserialize(self, data): - qtutils.deserialize(data, self._history) + try: + qtutils.deserialize(data, self._history) + except OSError: + dump = "\n".join( + bytes(line).hex(" ") for line in utils.chunk(bytes(data), 16) + ) + log.webview.debug(f"Failed to deserialize history data:\n{dump}") + raise def _load_items_workaround(self, items): """WORKAROUND for session loading not working on Qt 5.15. diff --git a/qutebrowser/completion/models/completionmodel.py b/qutebrowser/completion/models/completionmodel.py index a4eed93d1..c0f6a86ff 100644 --- a/qutebrowser/completion/models/completionmodel.py +++ b/qutebrowser/completion/models/completionmodel.py @@ -4,7 +4,7 @@ """A model that proxies access to one or more completion categories.""" -from typing import overload, Optional, Any, cast +from typing import overload, Optional, Any from collections.abc import MutableSequence from qutebrowser.qt import machinery @@ -91,14 +91,14 @@ class CompletionModel(QAbstractItemModel): Return: The item flags, or Qt.ItemFlag.NoItemFlags on error. """ if not index.isValid(): - return cast(_FlagType, Qt.ItemFlag.NoItemFlags) + return qtutils.maybe_cast(_FlagType, machinery.IS_QT5, Qt.ItemFlag.NoItemFlags) if index.parent().isValid(): # item return (Qt.ItemFlag.ItemIsEnabled | Qt.ItemFlag.ItemIsSelectable | Qt.ItemFlag.ItemNeverHasChildren) else: # category - return cast(_FlagType, Qt.ItemFlag.NoItemFlags) + return qtutils.maybe_cast(_FlagType, machinery.IS_QT5, Qt.ItemFlag.NoItemFlags) def index(self, row: int, col: int, parent: QModelIndex = QModelIndex()) -> QModelIndex: """Get an index into the model. diff --git a/qutebrowser/misc/nativeeventfilter.py b/qutebrowser/misc/nativeeventfilter.py index 1933dc63f..e93b3c6d1 100644 --- a/qutebrowser/misc/nativeeventfilter.py +++ b/qutebrowser/misc/nativeeventfilter.py @@ -7,7 +7,7 @@ This entire file is a giant WORKAROUND for https://bugreports.qt.io/browse/QTBUG-114334. """ -from typing import Union, cast, Optional +from typing import Union, Optional import enum import ctypes import ctypes.util @@ -16,7 +16,7 @@ from qutebrowser.qt import sip, machinery from qutebrowser.qt.core import QAbstractNativeEventFilter, QByteArray, qVersion from qutebrowser.misc import objects -from qutebrowser.utils import log +from qutebrowser.utils import log, qtutils # Needs to be saved to avoid garbage collection @@ -104,8 +104,8 @@ class NativeEventFilter(QAbstractNativeEventFilter): # # Tuple because PyQt uses the second value as the *result out-pointer, which # according to the Qt documentation is only used on Windows. - _PASS_EVENT_RET = (False, cast(_PointerRetType, 0)) - _FILTER_EVENT_RET = (True, cast(_PointerRetType, 0)) + _PASS_EVENT_RET = (False, qtutils.maybe_cast(_PointerRetType, machinery.IS_QT6, 0)) + _FILTER_EVENT_RET = (True, qtutils.maybe_cast(_PointerRetType, machinery.IS_QT6, 0)) def __init__(self) -> None: super().__init__() diff --git a/qutebrowser/utils/debug.py b/qutebrowser/utils/debug.py index de7f87f1e..131639127 100644 --- a/qutebrowser/utils/debug.py +++ b/qutebrowser/utils/debug.py @@ -16,6 +16,7 @@ from typing import ( from collections.abc import Mapping, MutableSequence, Sequence, Callable from qutebrowser.qt.core import Qt, QEvent, QMetaMethod, QObject, pyqtBoundSignal +from qutebrowser.qt.widgets import QApplication from qutebrowser.utils import log, utils, qtutils, objreg from qutebrowser.misc import objects @@ -345,9 +346,9 @@ class log_time: # noqa: N801,N806 pylint: disable=invalid-name return wrapped -def _get_widgets() -> Sequence[str]: +def _get_widgets(qapp: QApplication) -> Sequence[str]: """Get a string list of all widgets.""" - widgets = objects.qapp.allWidgets() + widgets = qapp.allWidgets() widgets.sort(key=repr) return [repr(w) for w in widgets] @@ -361,17 +362,20 @@ def _get_pyqt_objects(lines: MutableSequence[str], _get_pyqt_objects(lines, kid, depth + 1) -def get_all_objects(start_obj: QObject = None) -> str: +def get_all_objects(start_obj: QObject = None, *, qapp: QApplication = None) -> str: """Get all children of an object recursively as a string.""" + if qapp is None: + assert objects.qapp is not None + qapp = objects.qapp output = [''] - widget_lines = _get_widgets() + widget_lines = _get_widgets(qapp) widget_lines = [' ' + e for e in widget_lines] widget_lines.insert(0, "Qt widgets - {} objects:".format( len(widget_lines))) output += widget_lines if start_obj is None: - start_obj = objects.qapp + start_obj = qapp pyqt_lines: list[str] = [] _get_pyqt_objects(pyqt_lines, start_obj) diff --git a/qutebrowser/utils/qtlog.py b/qutebrowser/utils/qtlog.py index 215123f4a..775895ed3 100644 --- a/qutebrowser/utils/qtlog.py +++ b/qutebrowser/utils/qtlog.py @@ -127,6 +127,9 @@ def qt_message_handler(msg_type: qtcore.QtMsgType, "QCoreApplication is created.", # Qt 6.4 beta 1: https://bugreports.qt.io/browse/QTBUG-104741 "GL format 0 is not supported", + # WORKAROUND https://bugreports.qt.io/browse/QTBUG-137424 + ("QObject::disconnect: wildcard call disconnects from destroyed signal of " + "QNativeSocketEngine::unnamed"), ] # not using utils.is_mac here, because we can't be sure we can successfully # import the utils module here. diff --git a/qutebrowser/utils/qtutils.py b/qutebrowser/utils/qtutils.py index a027db74a..d55f9bc2f 100644 --- a/qutebrowser/utils/qtutils.py +++ b/qutebrowser/utils/qtutils.py @@ -749,3 +749,14 @@ else: return obj QT_NONE: None = None + + +def maybe_cast(to_type: type[_T], do_cast: bool, value: Any) -> _T: + """Cast `value` to `to_type` only if `do_cast` is true.""" + if do_cast: + return cast( + to_type, # type: ignore[valid-type] + value, + ) + + return value diff --git a/qutebrowser/utils/utils.py b/qutebrowser/utils/utils.py index 9c506471d..37a72c07b 100644 --- a/qutebrowser/utils/utils.py +++ b/qutebrowser/utils/utils.py @@ -722,6 +722,11 @@ def guess_mimetype(filename: str, fallback: bool = False) -> str: fallback: Fall back to application/octet-stream if unknown. """ mimetype, _encoding = mimetypes.guess_type(filename) + if os.path.splitext(filename)[1] == '.mjs' and mimetype == "text/plain": + # Windows can sometimes have .mjs registered wrongly as text/plain: + # https://github.com/golang/go/issues/68591 + return "text/javascript" + if mimetype is None: if fallback: return 'application/octet-stream' diff --git a/qutebrowser/utils/version.py b/qutebrowser/utils/version.py index eaa3662be..854268eaf 100644 --- a/qutebrowser/utils/version.py +++ b/qutebrowser/utils/version.py @@ -487,7 +487,7 @@ def _pdfjs_version() -> str: else: pdfjs_file = pdfjs_file.decode('utf-8') version_re = re.compile( - r"""^ *(PDFJS\.version|(var|const) pdfjsVersion) = ['"](?P[^'"]+)['"];$""", + r"""^ *(PDFJS\.version|(var|const|\*) pdfjsVersion) = ['"]?(?P[^'"\n]+)['"]?;?$""", re.MULTILINE) match = version_re.search(pdfjs_file) @@ -594,6 +594,7 @@ class WebEngineVersions: utils.VersionNumber(5, 15, 16): (_BASES[87], '119.0.6045.123'), # 2023-11-07 utils.VersionNumber(5, 15, 17): (_BASES[87], '123.0.6312.58'), # 2024-03-19 utils.VersionNumber(5, 15, 18): (_BASES[87], '130.0.6723.59'), # 2024-10-14 + utils.VersionNumber(5, 15, 19): (_BASES[87], '135.0.7049.95'), # 2025-04-14 ## Qt 6.2 @@ -643,6 +644,7 @@ class WebEngineVersions: ## Qt 6.9 utils.VersionNumber(6, 9): (_BASES[130], '133.0.6943.141'), # 2025-02-25 + utils.VersionNumber(6, 9, 1): (_BASES[130], '136.0.7103.114'), # 2025-05-13 } def __post_init__(self) -> None: diff --git a/scripts/dev/build_release.py b/scripts/dev/build_release.py index 2f1ab26c0..2310658fa 100755 --- a/scripts/dev/build_release.py +++ b/scripts/dev/build_release.py @@ -173,6 +173,9 @@ def smoke_test(executable: pathlib.Path, debug: bool) -> None: # 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'"), + + # Qt 6.9 on macOS + r'Compositor returned null texture', ]) elif IS_WINDOWS: stderr_whitelist.extend([ diff --git a/scripts/dev/changelog_urls.json b/scripts/dev/changelog_urls.json index 5029eb9c1..0c8df04d5 100644 --- a/scripts/dev/changelog_urls.json +++ b/scripts/dev/changelog_urls.json @@ -146,7 +146,7 @@ "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", + "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", diff --git a/scripts/dev/ci/docker/Dockerfile.j2 b/scripts/dev/ci/docker/Dockerfile.j2 index 7a27ea813..ead701da3 100644 --- a/scripts/dev/ci/docker/Dockerfile.j2 +++ b/scripts/dev/ci/docker/Dockerfile.j2 @@ -7,27 +7,23 @@ RUN sed -i '/^# after the header/a[kde-unstable]\nInclude = /etc/pacman.d/mirror RUN pacman -Sy --noconfirm archlinux-keyring RUN pacman -Su --noconfirm \ git \ - {% if webengine %} python-tox \ python-distlib \ - {% endif %} + libxml2-legacy \ {% if qt6 %} qt6-base \ qt6-declarative \ - {% if webengine %} - qt6-webengine python-pyqt6-webengine \ - pdfjs \ - {% else %}{{ 1/0 }}{% endif %} + qt6-webengine \ + python-pyqt6-webengine \ + pdfjs \ python-pyqt6 \ {% else %} qt5-base \ qt5-declarative \ openssl-1.1 \ - {% if webengine %} - qt5-webengine \ - python-pyqtwebengine \ - python-pyqt5 \ - {% endif %} + qt5-webengine \ + python-pyqtwebengine \ + python-pyqt5 \ {% endif %} xorg-xinit \ xorg-server-xvfb \ @@ -40,44 +36,12 @@ RUN useradd user -u 1001 && \ mkdir /home/user && \ chown user:users /home/user -{% if not webengine %} -RUN pacman -U --noconfirm \ - https://archive.archlinux.org/packages/q/qt5-webkit/qt5-webkit-5.212.0alpha4-18-x86_64.pkg.tar.zst \ - https://archive.archlinux.org/packages/p/python-pyqt5/python-pyqt5-5.15.7-2-x86_64.pkg.tar.zst \ - https://archive.archlinux.org/packages/i/icu/icu-72.1-2-x86_64.pkg.tar.zst \ - https://archive.archlinux.org/packages/l/libxml2/libxml2-2.10.4-4-x86_64.pkg.tar.zst \ - https://archive.archlinux.org/packages/l/libxslt/libxslt-1.1.42-2-x86_64.pkg.tar.zst \ - https://archive.archlinux.org/packages/q/qt5-base/qt5-base-5.15.10%2Bkde%2Br129-3-x86_64.pkg.tar.zst \ - https://archive.archlinux.org/packages/q/qt5-declarative/qt5-declarative-5.15.10%2Bkde%2Br31-1-x86_64.pkg.tar.zst \ - https://archive.archlinux.org/packages/q/qt5-translations/qt5-translations-5.15.10-1-any.pkg.tar.zst \ - https://archive.archlinux.org/packages/q/qt5-sensors/qt5-sensors-5.15.10-1-x86_64.pkg.tar.zst \ - https://archive.archlinux.org/packages/q/qt5-location/qt5-location-5.15.10%2Bkde%2Br5-1-x86_64.pkg.tar.zst \ - https://archive.archlinux.org/packages/q/qt5-webchannel/qt5-webchannel-5.15.10%2Bkde%2Br3-1-x86_64.pkg.tar.zst -RUN pacman -S --noconfirm base-devel - -RUN echo 'user ALL=(ALL) NOPASSWD:ALL' >> /etc/sudoers # needed for makepkg -USER user -RUN cd ~ && \ - git clone https://aur.archlinux.org/python310.git && \ - cd python310 && \ - makepkg -si --noconfirm -USER root -RUN python3.10 -m ensurepip -RUN python3.10 -m pip install tox pyqt5-sip -{% endif %} - {% if qt6 %} {% set pyqt_module = 'PyQt6' %} {% else %} {% set pyqt_module = 'PyQt5' %} {% endif %} -{% if webengine %} - {% set python = 'python3' %} - RUN {{ python }} -c "from {{ pyqt_module }} import QtWebEngineCore, QtWebEngineWidgets" -{% else %} - {% set python = 'python3.10' %} - RUN {{ python }} -c "from {{ pyqt_module }} import QtWebKit, QtWebKitWidgets" -{% endif %} +RUN python3 -c "from {{ pyqt_module }} import QtWebEngineCore, QtWebEngineWidgets" USER user WORKDIR /home/user diff --git a/scripts/dev/ci/docker/generate.py b/scripts/dev/ci/docker/generate.py index 0f538e582..1f27ddd00 100644 --- a/scripts/dev/ci/docker/generate.py +++ b/scripts/dev/ci/docker/generate.py @@ -14,11 +14,10 @@ import jinja2 CONFIGS = { - 'archlinux-webkit': {'webengine': False, 'unstable': False, 'qt6': False}, - 'archlinux-webengine': {'webengine': True, 'unstable': False, 'qt6': False}, - 'archlinux-webengine-qt6': {'webengine': True, 'unstable': False, 'qt6': True}, - 'archlinux-webengine-unstable': {'webengine': True, 'unstable': True, 'qt6': False}, - 'archlinux-webengine-unstable-qt6': {'webengine': True, 'unstable': True, 'qt6': True}, + 'archlinux-webengine': {'unstable': False, 'qt6': False}, + 'archlinux-webengine-qt6': {'unstable': False, 'qt6': True}, + 'archlinux-webengine-unstable': {'unstable': True, 'qt6': False}, + 'archlinux-webengine-unstable-qt6': {'unstable': True, 'qt6': True}, } diff --git a/tests/end2end/fixtures/quteprocess.py b/tests/end2end/fixtures/quteprocess.py index 6db4771e3..f36cc2c8e 100644 --- a/tests/end2end/fixtures/quteprocess.py +++ b/tests/end2end/fixtures/quteprocess.py @@ -83,6 +83,8 @@ def is_ignored_lowlevel_message(message): # GitHub Actions with Archlinux unstable packages 'libEGL warning: DRI3: Screen seems not DRI3 capable', 'libEGL warning: egl: failed to create dri2 screen', + 'libEGL warning: DRI3 error: Could not get DRI3 device', + 'libEGL warning: Activate DRI3 at Xorg or build mesa with DRI2', ] return any(testutils.pattern_match(pattern=pattern, value=message) for pattern in ignored_messages) @@ -131,6 +133,8 @@ def is_ignored_chromium_message(line): # Qt 6.2: # [503633:503650:0509/185222.442798:ERROR:ssl_client_socket_impl.cc(959)] handshake failed; returned -1, SSL error code 1, net_error -202 'handshake failed; returned -1, SSL error code 1, net_error -202', + # Qt 6.8 + Python 3.14 + 'handshake failed; returned -1, SSL error code 1, net_error -101', # Qt 6.2: # [2432160:7:0429/195800.168435:ERROR:command_buffer_proxy_impl.cc(140)] ContextResult::kTransientFailure: Failed to send GpuChannelMsg_CreateCommandBuffer. diff --git a/tests/helpers/fixtures.py b/tests/helpers/fixtures.py index eb4474991..02dc2d548 100644 --- a/tests/helpers/fixtures.py +++ b/tests/helpers/fixtures.py @@ -533,6 +533,7 @@ def mode_manager(win_registry, config_stub, key_config_stub, qapp): mm = modeman.init(win_id=0, parent=qapp) yield mm objreg.delete('mode-manager', scope='window', window=0) + mm.deleteLater() def standarddir_tmpdir(folder, monkeypatch, tmpdir): diff --git a/tests/unit/browser/webengine/test_webenginedownloads.py b/tests/unit/browser/webengine/test_webenginedownloads.py index 3b2e8e973..2fcf7f7c1 100644 --- a/tests/unit/browser/webengine/test_webenginedownloads.py +++ b/tests/unit/browser/webengine/test_webenginedownloads.py @@ -100,6 +100,7 @@ class TestDataUrlWorkaround: manager.install(webengine_profile) yield manager webengine_profile.downloadRequested.disconnect() + manager.deleteLater() def test_workaround(self, webengine_tab, message_mock, qtbot, pdf_url, download_manager, expected_names): diff --git a/tests/unit/misc/test_elf.py b/tests/unit/misc/test_elf.py index 6ae23357c..5be9a2a12 100644 --- a/tests/unit/misc/test_elf.py +++ b/tests/unit/misc/test_elf.py @@ -11,6 +11,7 @@ from hypothesis import strategies as hst from qutebrowser.misc import elf, binparsing from qutebrowser.utils import utils +from qutebrowser.utils.utils import VersionNumber @pytest.mark.parametrize('fmt, expected', [ @@ -45,7 +46,8 @@ def test_result(webengine_versions, qapp, caplog): pytest.importorskip('qutebrowser.qt.webenginecore') versions = elf.parse_webenginecore() - if webengine_versions.webengine >= utils.VersionNumber(6, 5): + qtwe_version = webengine_versions.webengine + if qtwe_version == VersionNumber(5, 15, 19) or qtwe_version >= VersionNumber(6, 5): assert versions is None pytest.xfail("ELF file structure not supported") diff --git a/tests/unit/utils/test_utils.py b/tests/unit/utils/test_utils.py index fc4a3e652..12dd070de 100644 --- a/tests/unit/utils/test_utils.py +++ b/tests/unit/utils/test_utils.py @@ -858,6 +858,7 @@ def test_chunk_invalid(n): @pytest.mark.parametrize('filename, expected', [ ('test.jpg', 'image/jpeg'), ('test.blabla', 'application/octet-stream'), + ('test.mjs', 'text/javascript'), ]) def test_guess_mimetype(filename, expected): assert utils.guess_mimetype(filename, fallback=True) == expected diff --git a/tests/unit/utils/usertypes/test_timer.py b/tests/unit/utils/usertypes/test_timer.py index 6aabc8c04..addd8fb8f 100644 --- a/tests/unit/utils/usertypes/test_timer.py +++ b/tests/unit/utils/usertypes/test_timer.py @@ -8,7 +8,9 @@ import logging import fnmatch import pytest -from qutebrowser.qt.core import QObject +import pytest_mock +from qutebrowser.qt.core import QObject, QTimer +from qutebrowser.qt.widgets import QApplication from qutebrowser.utils import usertypes @@ -70,6 +72,17 @@ def test_timeout_set_interval(qtbot): t.start() +@pytest.fixture +def time_mock(qapp: QApplication, mocker: pytest_mock.MockerFixture) -> None: + """Patch time.monotonic() to return a fixed value.""" + # Check if there are any stray timers still alive. + # If previous tests didn't clean up a QApplication-wide QTimer correctly, this + # will point us at the issue instead of test_early_timeout_check getting flaky + # because of it. + assert not qapp.findChildren(QTimer) + return mocker.patch("time.monotonic", autospec=True) + + @pytest.mark.parametrize( "elapsed_ms, expected", [ @@ -80,9 +93,7 @@ def test_timeout_set_interval(qtbot): (1000, True), ], ) -def test_early_timeout_check(qtbot, mocker, elapsed_ms, expected): - time_mock = mocker.patch("time.monotonic", autospec=True) - +def test_early_timeout_check(time_mock, elapsed_ms, expected): t = usertypes.Timer() t.setInterval(1000) # anything long enough to not actually fire time_mock.return_value = 0 # assigned to _start_time in start() @@ -94,9 +105,7 @@ def test_early_timeout_check(qtbot, mocker, elapsed_ms, expected): t.stop() -def test_early_timeout_handler(qtbot, mocker, caplog): - time_mock = mocker.patch("time.monotonic", autospec=True) - +def test_early_timeout_handler(qtbot, time_mock, caplog): t = usertypes.Timer(name="t") t.setInterval(3) t.setSingleShot(True) @@ -113,10 +122,8 @@ def test_early_timeout_handler(qtbot, mocker, caplog): ) -def test_early_manual_fire(qtbot, mocker, caplog): +def test_early_manual_fire(qtbot, time_mock, caplog): """Same as above but start() never gets called.""" - time_mock = mocker.patch("time.monotonic", autospec=True) - t = usertypes.Timer(name="t") t.setInterval(3) t.setSingleShot(True)