Merge remote-tracking branch 'upstream/main' into tree-tabs-integration

This commit is contained in:
toofar 2024-04-21 17:31:39 +12:00
commit 9bf2bae60f
83 changed files with 601 additions and 269 deletions

View File

@ -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

View File

@ -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

View File

@ -10,6 +10,7 @@ jobs:
if: "github.repository == 'qutebrowser/qutebrowser'"
runs-on: ubuntu-20.04
strategy:
fail-fast: false
matrix:
image:
- archlinux-webkit

View File

@ -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

View File

@ -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>

View File

@ -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

View File

@ -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

View File

@ -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)

View File

@ -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;\]]+

View File

@ -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
----

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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.*"

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -2,4 +2,4 @@
pathspec==0.12.1
PyYAML==6.0.1
yamllint==1.33.0
yamllint==1.35.1

View File

@ -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.')

View File

@ -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

View File

@ -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()

View File

@ -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

View File

@ -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():

View File

@ -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)

View File

@ -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):

View File

@ -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)

View File

@ -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)

View File

@ -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')

View File

@ -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

View File

@ -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):

View File

@ -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)

View File

@ -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()

View File

@ -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.

View File

@ -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:

View File

@ -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)

View File

@ -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]

View File

@ -739,6 +739,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.
@ -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 = () => {

View File

@ -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

View File

@ -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):

View File

@ -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,

View File

@ -28,7 +28,7 @@ PROTOCOL_VERSION = 1
# The ipc server instance
server = None
server: Optional["IPCServer"] = None
def _get_socketname_windows(basedir):

View File

@ -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

View File

@ -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()

View File

@ -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

View File

@ -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:

View File

@ -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:

View File

@ -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

View File

@ -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]

View File

@ -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):

View File

@ -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

View File

@ -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

View File

@ -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"

View File

@ -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([

View File

@ -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",

View File

@ -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',

View File

@ -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', '{}')

View File

@ -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;'

View File

@ -10,6 +10,9 @@
elem.addEventListener('input', function() {
console.log("contents: " + elem.value);
});
elem.addEventListener('focus', function() {
console.log("autofocus element focused");
});
}
</script>
</head>

View File

@ -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"

View File

@ -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 "

View File

@ -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

View File

@ -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

View File

@ -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():

View File

@ -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'):

View File

@ -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'

View File

@ -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()

View File

@ -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

View File

@ -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)

View File

@ -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

View File

@ -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',

View File

@ -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"
)

View File

@ -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', [

View File

@ -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:

View File

@ -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