summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAnder Punnar <ander@kvlt.ee>2021-05-20 20:52:42 +0300
committerAnder Punnar <ander@kvlt.ee>2021-05-20 20:52:42 +0300
commit4e47af21586aad3846c2458dc0c8243e9d6eb792 (patch)
tree11b2073411b7decc68ba049bdfd4d97e8ab48343
parent2fc588358b27153721cc83e728e0d15b0e4d0d22 (diff)
parent90fbe1e181463379f38b8f0b01d488d1f7a586e0 (diff)
downloadqutebrowser-4e47af21586aad3846c2458dc0c8243e9d6eb792.tar.gz
qutebrowser-4e47af21586aad3846c2458dc0c8243e9d6eb792.zip
Merge remote-tracking branch 'origin/master' into 4nd3r/hostblock_subdomains
-rw-r--r--.bumpversion.cfg2
-rw-r--r--.github/workflows/bleeding.yml3
-rw-r--r--.mypy.ini6
-rw-r--r--doc/changelog.asciidoc20
-rw-r--r--doc/help/settings.asciidoc21
-rw-r--r--misc/org.qutebrowser.qutebrowser.appdata.xml1
-rw-r--r--misc/requirements/requirements-dev.txt8
-rw-r--r--misc/requirements/requirements-flake8.txt10
-rw-r--r--misc/requirements/requirements-mypy.txt10
-rw-r--r--misc/requirements/requirements-pylint.txt4
-rw-r--r--misc/requirements/requirements-pyqt-5.12.txt2
-rw-r--r--misc/requirements/requirements-pyqt-5.13.txt2
-rw-r--r--misc/requirements/requirements-pyqt-5.14.txt2
-rw-r--r--misc/requirements/requirements-pyqt-5.15.0.txt2
-rw-r--r--misc/requirements/requirements-pyqt-5.15.txt2
-rw-r--r--misc/requirements/requirements-pyqt-pyinstaller.txt2
-rw-r--r--misc/requirements/requirements-pyqt.txt2
-rw-r--r--misc/requirements/requirements-pyroma.txt2
-rw-r--r--misc/requirements/requirements-sphinx.txt6
-rw-r--r--misc/requirements/requirements-tests.txt30
-rw-r--r--misc/requirements/requirements-tox.txt8
-rw-r--r--pytest.ini2
-rw-r--r--qutebrowser/__init__.py2
-rw-r--r--qutebrowser/api/cmdutils.py7
-rw-r--r--qutebrowser/api/hook.py11
-rw-r--r--qutebrowser/browser/browsertab.py11
-rw-r--r--qutebrowser/browser/commands.py2
-rw-r--r--qutebrowser/browser/qutescheme.py5
-rw-r--r--qutebrowser/browser/webelem.py3
-rw-r--r--qutebrowser/browser/webengine/notification.py3
-rw-r--r--qutebrowser/browser/webengine/tabhistory.py8
-rw-r--r--qutebrowser/browser/webengine/webenginetab.py13
-rw-r--r--qutebrowser/browser/webkit/tabhistory.py4
-rw-r--r--qutebrowser/config/config.py13
-rw-r--r--qutebrowser/config/configcommands.py4
-rw-r--r--qutebrowser/config/configdata.yml11
-rw-r--r--qutebrowser/config/configfiles.py38
-rw-r--r--qutebrowser/config/websettings.py39
-rw-r--r--qutebrowser/extensions/loader.py13
-rw-r--r--qutebrowser/javascript/quirks/googledocs.user.js14
-rw-r--r--qutebrowser/keyinput/basekeyparser.py2
-rw-r--r--qutebrowser/keyinput/keyutils.py2
-rw-r--r--qutebrowser/misc/backendproblem.py3
-rw-r--r--qutebrowser/misc/debugcachestats.py2
-rw-r--r--qutebrowser/misc/elf.py4
-rw-r--r--qutebrowser/misc/throttle.py2
-rw-r--r--qutebrowser/utils/debug.py34
-rw-r--r--qutebrowser/utils/docutils.py4
-rw-r--r--qutebrowser/utils/jinja.py8
-rw-r--r--qutebrowser/utils/objreg.py3
-rw-r--r--qutebrowser/utils/qtutils.py13
-rw-r--r--qutebrowser/utils/urlmatch.py9
-rw-r--r--qutebrowser/utils/utils.py9
-rw-r--r--qutebrowser/utils/version.py2
-rw-r--r--requirements.txt8
-rw-r--r--scripts/dev/recompile_requirements.py12
-rwxr-xr-xscripts/mkvenv.py8
-rw-r--r--tests/end2end/fixtures/webserver_sub_ssl.py2
-rw-r--r--tests/unit/config/test_config.py1
-rw-r--r--tests/unit/config/test_configfiles.py61
-rw-r--r--tests/unit/scripts/test_check_coverage.py6
-rw-r--r--tests/unit/utils/test_version.py4
62 files changed, 385 insertions, 162 deletions
diff --git a/.bumpversion.cfg b/.bumpversion.cfg
index 6b05fd3bc..c0e16391b 100644
--- a/.bumpversion.cfg
+++ b/.bumpversion.cfg
@@ -1,5 +1,5 @@
[bumpversion]
-current_version = 2.2.1
+current_version = 2.2.2
commit = True
message = Release v{new_version}
tag = True
diff --git a/.github/workflows/bleeding.yml b/.github/workflows/bleeding.yml
index 5d464e3ac..746810cb8 100644
--- a/.github/workflows/bleeding.yml
+++ b/.github/workflows/bleeding.yml
@@ -78,6 +78,9 @@ jobs:
sed -i '' '/.-d., .--debug.,/s/$/ default=True,/' qutebrowser/qutebrowser.py
- name: Run tox
run: "tox -e build-release -- --asciidoc ../asciidoc/asciidoc.py --gh-token ${{ secrets.GITHUB_TOKEN }} ${{ matrix.args }}"
+ - name: Wait 90s to avoid upload errors
+ if: "contains(matrix.args, '--32bit')"
+ run: "sleep 90"
- name: Upload artifacts
uses: actions/upload-artifact@v2
with:
diff --git a/.mypy.ini b/.mypy.ini
index b1cd8967c..289f3eb87 100644
--- a/.mypy.ini
+++ b/.mypy.ini
@@ -3,7 +3,7 @@ python_version = 3.6
### --strict
warn_unused_configs = True
-# disallow_any_generics = True
+disallow_any_generics = True
disallow_subclassing_any = True
# disallow_untyped_calls = True
# disallow_untyped_defs = True
@@ -83,6 +83,10 @@ disallow_untyped_defs = True
[mypy-qutebrowser.config.*]
disallow_untyped_defs = True
+[mypy-qutebrowser.config.configtypes]
+# Needs some major work to use specific generics
+disallow_any_generics = False
+
[mypy-qutebrowser.api.*]
disallow_untyped_defs = True
diff --git a/doc/changelog.asciidoc b/doc/changelog.asciidoc
index 149b4f9ee..428cf22f8 100644
--- a/doc/changelog.asciidoc
+++ b/doc/changelog.asciidoc
@@ -15,8 +15,18 @@ 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.
+[[v2.3.0]]
+v2.3.0 (unreleased)
+-------------------
+
+Changed
+~~~~~~~
+
+The `fonts.web.*` settings now support URL patterns.
+
+
[[v2.2.2]]
-v2.2.2 (unreleased)
+v2.2.2 (2021-05-20)
-------------------
Fixed
@@ -25,6 +35,14 @@ Fixed
- When awesomewm's "naughty" notification daemon was used with a development
version of AwesomeWM and an unknown version number, qutebrowser would crash
when trying to parse the version string. This is now fixed.
+- Due to a bug with QtWebEngine 5.15.4, old Service Worker data could cause
+ renderer process crashes. This is now worked around by qutebrowser.
+- When an (broken) binding to `set-cmd-text` without any argument existed,
+ using `:` would crash, which is now fixed.
+- New site-specific quirk (again) working around not being able to type
+ accented/composed characters on Google Docs.
+- When running with `python -OO` (which is not recommended), a notification
+ being shown would result in a crash, which is now fixed.
[[v2.2.1]]
v2.2.1 (2021-04-29)
diff --git a/doc/help/settings.asciidoc b/doc/help/settings.asciidoc
index 42eacd175..fb208e48c 100644
--- a/doc/help/settings.asciidoc
+++ b/doc/help/settings.asciidoc
@@ -2692,6 +2692,7 @@ Valid values:
* +ua-whatsapp+
* +ua-google+
* +ua-slack+
+ * +ua-googledocs+
* +js-whatsapp-web+
* +js-discord+
* +js-string-replaceall+
@@ -3088,6 +3089,8 @@ Default: +pass:[default_size default_family]+
=== fonts.web.family.cursive
Font family for cursive fonts.
+This setting supports URL patterns.
+
Type: <<types,FontFamily>>
Default: empty
@@ -3096,6 +3099,8 @@ Default: empty
=== fonts.web.family.fantasy
Font family for fantasy fonts.
+This setting supports URL patterns.
+
Type: <<types,FontFamily>>
Default: empty
@@ -3104,6 +3109,8 @@ Default: empty
=== fonts.web.family.fixed
Font family for fixed fonts.
+This setting supports URL patterns.
+
Type: <<types,FontFamily>>
Default: empty
@@ -3112,6 +3119,8 @@ Default: empty
=== fonts.web.family.sans_serif
Font family for sans-serif fonts.
+This setting supports URL patterns.
+
Type: <<types,FontFamily>>
Default: empty
@@ -3120,6 +3129,8 @@ Default: empty
=== fonts.web.family.serif
Font family for serif fonts.
+This setting supports URL patterns.
+
Type: <<types,FontFamily>>
Default: empty
@@ -3128,6 +3139,8 @@ Default: empty
=== fonts.web.family.standard
Font family for standard fonts.
+This setting supports URL patterns.
+
Type: <<types,FontFamily>>
Default: empty
@@ -3136,6 +3149,8 @@ Default: empty
=== fonts.web.size.default
Default font size (in pixels) for regular text.
+This setting supports URL patterns.
+
Type: <<types,Int>>
Default: +pass:[16]+
@@ -3144,6 +3159,8 @@ Default: +pass:[16]+
=== fonts.web.size.default_fixed
Default font size (in pixels) for fixed-pitch text.
+This setting supports URL patterns.
+
Type: <<types,Int>>
Default: +pass:[13]+
@@ -3152,6 +3169,8 @@ Default: +pass:[13]+
=== fonts.web.size.minimum
Hard minimum font size (in pixels).
+This setting supports URL patterns.
+
Type: <<types,Int>>
Default: +pass:[0]+
@@ -3160,6 +3179,8 @@ Default: +pass:[0]+
=== fonts.web.size.minimum_logical
Minimum logical font size (in pixels) that is applied when zooming out.
+This setting supports URL patterns.
+
Type: <<types,Int>>
Default: +pass:[6]+
diff --git a/misc/org.qutebrowser.qutebrowser.appdata.xml b/misc/org.qutebrowser.qutebrowser.appdata.xml
index 6950c45eb..95602b476 100644
--- a/misc/org.qutebrowser.qutebrowser.appdata.xml
+++ b/misc/org.qutebrowser.qutebrowser.appdata.xml
@@ -44,6 +44,7 @@
</content_rating>
<releases>
<!-- Add new releases here -->
+<release version="2.2.2" date="2021-05-20"/>
<release version="2.2.1" date="2021-04-29"/>
<release version="2.2.0" date="2021-04-13"/>
<release version="2.1.1" date="2021-04-01"/>
diff --git a/misc/requirements/requirements-dev.txt b/misc/requirements/requirements-dev.txt
index 48417717b..2621579f6 100644
--- a/misc/requirements/requirements-dev.txt
+++ b/misc/requirements/requirements-dev.txt
@@ -6,7 +6,7 @@ cffi==1.14.5
chardet==4.0.0
cryptography==3.4.7
github3.py==2.0.0
-hunter==3.3.2
+hunter==3.3.3
idna==2.10
jwcrypto==0.8
manhole==1.8.0
@@ -14,11 +14,11 @@ packaging==20.9
pycparser==2.20
Pympler==0.9
pyparsing==2.4.7
-PyQt-builder==1.9.1
+PyQt-builder==1.10.0
python-dateutil==2.8.1
requests==2.25.1
-sip==6.0.3
-six==1.15.0
+sip==6.1.0
+six==1.16.0
toml==0.10.2
uritemplate==3.0.1
# urllib3==1.26.4
diff --git a/misc/requirements/requirements-flake8.txt b/misc/requirements/requirements-flake8.txt
index 7f6c5feaf..429d04dfe 100644
--- a/misc/requirements/requirements-flake8.txt
+++ b/misc/requirements/requirements-flake8.txt
@@ -1,10 +1,10 @@
# This file is automatically generated by scripts/dev/recompile_requirements.py
-attrs==20.3.0
-flake8==3.9.1
+attrs==21.2.0
+flake8==3.9.2
flake8-bugbear==21.4.3
flake8-builtins==1.5.3
-flake8-comprehensions==3.4.0
+flake8-comprehensions==3.5.0
flake8-copyright==0.2.2
flake8-debugger==4.0.0
flake8-deprecated==1.3
@@ -13,12 +13,12 @@ flake8-future-import==0.4.6
flake8-mock==0.3
flake8-polyfill==1.0.2
flake8-string-format==0.3.0
-flake8-tidy-imports==4.2.1
+flake8-tidy-imports==4.3.0
flake8-tuple==0.4.1
mccabe==0.6.1
pep8-naming==0.11.1
pycodestyle==2.7.0
pydocstyle==6.0.0
pyflakes==2.3.1
-six==1.15.0
+six==1.16.0
snowballstemmer==2.1.0
diff --git a/misc/requirements/requirements-mypy.txt b/misc/requirements/requirements-mypy.txt
index f43386414..7d3d29e63 100644
--- a/misc/requirements/requirements-mypy.txt
+++ b/misc/requirements/requirements-mypy.txt
@@ -1,18 +1,18 @@
# This file is automatically generated by scripts/dev/recompile_requirements.py
chardet==4.0.0
-diff-cover==5.0.1
+diff-cover==5.1.1
importlib-metadata==4.0.1
-importlib-resources==5.1.2
+importlib-resources==5.1.3
inflect==5.3.0
-Jinja2==2.11.3
+Jinja2==3.0.0
jinja2-pluralize==0.3.0
lxml==4.6.3
-MarkupSafe==1.1.1
+MarkupSafe==2.0.0
mypy==0.812
mypy-extensions==0.4.3
pluggy==0.13.1
-Pygments==2.8.1
+Pygments==2.9.0
PyQt5-stubs==5.15.2.0
typed-ast==1.4.3
typing-extensions==3.10.0.0
diff --git a/misc/requirements/requirements-pylint.txt b/misc/requirements/requirements-pylint.txt
index ac3215255..13358f6d5 100644
--- a/misc/requirements/requirements-pylint.txt
+++ b/misc/requirements/requirements-pylint.txt
@@ -12,13 +12,13 @@ isort==4.3.21
jwcrypto==0.8
lazy-object-proxy==1.4.3
mccabe==0.6.1
-pefile==2019.4.18
+pefile==2021.5.13
pycparser==2.20
pylint==2.4.4 # rq.filter: < 2.5
python-dateutil==2.8.1
./scripts/dev/pylint_checkers
requests==2.25.1
-six==1.15.0
+six==1.16.0
typed-ast==1.4.3 ; python_version<"3.8"
uritemplate==3.0.1
# urllib3==1.26.4
diff --git a/misc/requirements/requirements-pyqt-5.12.txt b/misc/requirements/requirements-pyqt-5.12.txt
index 80a700f09..890306127 100644
--- a/misc/requirements/requirements-pyqt-5.12.txt
+++ b/misc/requirements/requirements-pyqt-5.12.txt
@@ -1,5 +1,5 @@
# This file is automatically generated by scripts/dev/recompile_requirements.py
PyQt5==5.12.3 # rq.filter: < 5.13
-PyQt5-sip==12.8.1
+PyQt5-sip==12.9.0
PyQtWebEngine==5.12.1 # rq.filter: < 5.13
diff --git a/misc/requirements/requirements-pyqt-5.13.txt b/misc/requirements/requirements-pyqt-5.13.txt
index 438c600da..5f4da4758 100644
--- a/misc/requirements/requirements-pyqt-5.13.txt
+++ b/misc/requirements/requirements-pyqt-5.13.txt
@@ -1,5 +1,5 @@
# This file is automatically generated by scripts/dev/recompile_requirements.py
PyQt5==5.13.2 # rq.filter: < 5.14
-PyQt5-sip==12.8.1
+PyQt5-sip==12.9.0
PyQtWebEngine==5.13.2 # rq.filter: < 5.14
diff --git a/misc/requirements/requirements-pyqt-5.14.txt b/misc/requirements/requirements-pyqt-5.14.txt
index d515e717f..9ce643666 100644
--- a/misc/requirements/requirements-pyqt-5.14.txt
+++ b/misc/requirements/requirements-pyqt-5.14.txt
@@ -1,5 +1,5 @@
# This file is automatically generated by scripts/dev/recompile_requirements.py
PyQt5==5.14.2 # rq.filter: < 5.15
-PyQt5-sip==12.8.1
+PyQt5-sip==12.9.0
PyQtWebEngine==5.14.0 # rq.filter: < 5.15
diff --git a/misc/requirements/requirements-pyqt-5.15.0.txt b/misc/requirements/requirements-pyqt-5.15.0.txt
index b9ee53f65..b111a93f3 100644
--- a/misc/requirements/requirements-pyqt-5.15.0.txt
+++ b/misc/requirements/requirements-pyqt-5.15.0.txt
@@ -1,5 +1,5 @@
# This file is automatically generated by scripts/dev/recompile_requirements.py
PyQt5==5.15.0 # rq.filter: == 5.15.0
-PyQt5-sip==12.8.1
+PyQt5-sip==12.9.0
PyQtWebEngine==5.15.0 # rq.filter: == 5.15.0
diff --git a/misc/requirements/requirements-pyqt-5.15.txt b/misc/requirements/requirements-pyqt-5.15.txt
index a5b3a5787..8b7a53c44 100644
--- a/misc/requirements/requirements-pyqt-5.15.txt
+++ b/misc/requirements/requirements-pyqt-5.15.txt
@@ -2,6 +2,6 @@
PyQt5==5.15.4 # rq.filter: < 5.16
PyQt5-Qt5==5.15.2
-PyQt5-sip==12.8.1
+PyQt5-sip==12.9.0
PyQtWebEngine==5.15.4 # rq.filter: < 5.16
PyQtWebEngine-Qt5==5.15.2
diff --git a/misc/requirements/requirements-pyqt-pyinstaller.txt b/misc/requirements/requirements-pyqt-pyinstaller.txt
index 31ecefad5..678a1d7ea 100644
--- a/misc/requirements/requirements-pyqt-pyinstaller.txt
+++ b/misc/requirements/requirements-pyqt-pyinstaller.txt
@@ -2,6 +2,6 @@
PyQt5==5.15.3
PyQt5-Qt==5.15.2
-PyQt5-sip==12.8.1
+PyQt5-sip==12.9.0
PyQtWebEngine==5.15.3
PyQtWebEngine-Qt==5.15.2
diff --git a/misc/requirements/requirements-pyqt.txt b/misc/requirements/requirements-pyqt.txt
index 7e28f7dc2..75ef27bf4 100644
--- a/misc/requirements/requirements-pyqt.txt
+++ b/misc/requirements/requirements-pyqt.txt
@@ -2,6 +2,6 @@
PyQt5==5.15.4
PyQt5-Qt5==5.15.2
-PyQt5-sip==12.8.1
+PyQt5-sip==12.9.0
PyQtWebEngine==5.15.4
PyQtWebEngine-Qt5==5.15.2
diff --git a/misc/requirements/requirements-pyroma.txt b/misc/requirements/requirements-pyroma.txt
index 16d92bd0e..feceac972 100644
--- a/misc/requirements/requirements-pyroma.txt
+++ b/misc/requirements/requirements-pyroma.txt
@@ -1,5 +1,5 @@
# This file is automatically generated by scripts/dev/recompile_requirements.py
docutils==0.17.1
-Pygments==2.8.1
+Pygments==2.9.0
pyroma==3.1
diff --git a/misc/requirements/requirements-sphinx.txt b/misc/requirements/requirements-sphinx.txt
index 403fc2903..2aa55e0a3 100644
--- a/misc/requirements/requirements-sphinx.txt
+++ b/misc/requirements/requirements-sphinx.txt
@@ -4,18 +4,18 @@ alabaster==0.7.12
Babel==2.9.1
certifi==2020.12.5
chardet==4.0.0
-docutils==0.16
+docutils==0.17.1
idna==2.10
imagesize==1.2.0
Jinja2==2.11.3
MarkupSafe==1.1.1
packaging==20.9
-Pygments==2.8.1
+Pygments==2.9.0
pyparsing==2.4.7
pytz==2021.1
requests==2.25.1
snowballstemmer==2.1.0
-Sphinx==3.5.4
+Sphinx==4.0.1
sphinxcontrib-applehelp==1.0.2
sphinxcontrib-devhelp==1.0.2
sphinxcontrib-htmlhelp==1.0.3
diff --git a/misc/requirements/requirements-tests.txt b/misc/requirements/requirements-tests.txt
index 5adf6a324..6c83c4fb7 100644
--- a/misc/requirements/requirements-tests.txt
+++ b/misc/requirements/requirements-tests.txt
@@ -1,29 +1,29 @@
# This file is automatically generated by scripts/dev/recompile_requirements.py
apipkg==1.5
-attrs==20.3.0
+attrs==21.2.0
beautifulsoup4==4.9.3
certifi==2020.12.5
chardet==4.0.0
cheroot==8.5.2
-click==7.1.2
+click==8.0.0
coverage==5.5
EasyProcess==0.3
execnet==1.8.0
filelock==3.0.12
-Flask==1.1.2
+Flask==2.0.0
glob2==0.7
-hunter==3.3.2
-hypothesis==6.10.1
+hunter==3.3.3
+hypothesis==6.12.0
icdiff==1.9.1
idna==2.10
iniconfig==1.1.1
-itsdangerous==1.1.0
+itsdangerous==2.0.0
jaraco.functools==3.3.0
-# Jinja2==2.11.3
+# Jinja2==3.0.0
Mako==1.1.4
manhole==1.8.0
-# MarkupSafe==1.1.1
+# MarkupSafe==2.0.0
more-itertools==8.7.0
packaging==20.9
parse==1.19.0
@@ -32,16 +32,16 @@ pluggy==0.13.1
pprintpp==0.4.0
py==1.10.0
py-cpuinfo==8.0.0
-Pygments==2.8.1
+Pygments==2.9.0
pyparsing==2.4.7
-pytest==6.2.3
+pytest==6.2.4
pytest-bdd==4.0.2
pytest-benchmark==3.4.1
-pytest-cov==2.11.1
+pytest-cov==2.12.0
pytest-forked==1.3.0
pytest-icdiff==0.5
pytest-instafail==0.4.2
-pytest-mock==3.6.0
+pytest-mock==3.6.1
pytest-qt==3.3.0
pytest-repeat==0.9.1
pytest-rerunfailures==9.1.1
@@ -50,11 +50,11 @@ pytest-xvfb==2.0.0
PyVirtualDisplay==2.1
requests==2.25.1
requests-file==1.5.1
-six==1.15.0
-sortedcontainers==2.3.0
+six==1.16.0
+sortedcontainers==2.4.0
soupsieve==2.2.1
tldextract==3.1.0
toml==0.10.2
urllib3==1.26.4
vulture==2.3
-Werkzeug==1.0.1
+Werkzeug==2.0.0
diff --git a/misc/requirements/requirements-tox.txt b/misc/requirements/requirements-tox.txt
index 6361d4f14..e8fd3f633 100644
--- a/misc/requirements/requirements-tox.txt
+++ b/misc/requirements/requirements-tox.txt
@@ -8,9 +8,9 @@ pip==21.1.1
pluggy==0.13.1
py==1.10.0
pyparsing==2.4.7
-setuptools==56.0.0
-six==1.15.0
+setuptools==56.2.0
+six==1.16.0
toml==0.10.2
-tox==3.23.0
-virtualenv==20.4.4
+tox==3.23.1
+virtualenv==20.4.6
wheel==0.36.2
diff --git a/pytest.ini b/pytest.ini
index 8aa49b446..72ad24002 100644
--- a/pytest.ini
+++ b/pytest.ini
@@ -84,4 +84,6 @@ filterwarnings =
ignore:_SixMetaPathImporter\.exec_module\(\) not found; falling back to load_module\(\):ImportWarning
ignore:VendorImporter\.find_spec\(\) not found; falling back to find_module\(\):ImportWarning
ignore:_SixMetaPathImporter\.find_spec\(\) not found; falling back to find_module\(\):ImportWarning
+ # https://github.com/ionelmc/python-hunter/issues/97
+ ignore:The distutils\.sysconfig module is deprecated, use sysconfig instead:DeprecationWarning
faulthandler_timeout = 90
diff --git a/qutebrowser/__init__.py b/qutebrowser/__init__.py
index 91a49d5b5..96062d9bd 100644
--- a/qutebrowser/__init__.py
+++ b/qutebrowser/__init__.py
@@ -26,7 +26,7 @@ __copyright__ = "Copyright 2014-2021 Florian Bruhin (The Compiler)"
__license__ = "GPL"
__maintainer__ = __author__
__email__ = "mail@qutebrowser.org"
-__version__ = "2.2.1"
+__version__ = "2.2.2"
__version_info__ = tuple(int(part) for part in __version__.split('.'))
__description__ = "A keyboard-driven, vim-like browser based on PyQt5."
diff --git a/qutebrowser/api/cmdutils.py b/qutebrowser/api/cmdutils.py
index 85a700c43..73c6a1bc5 100644
--- a/qutebrowser/api/cmdutils.py
+++ b/qutebrowser/api/cmdutils.py
@@ -105,6 +105,9 @@ def check_exclusive(flags: Iterable[bool], names: Iterable[str]) -> None:
raise CommandError("Only one of {} can be given!".format(argstr))
+_CmdHandlerType = Callable[..., Any]
+
+
class register: # noqa: N801,N806 pylint: disable=invalid-name
"""Decorator to register a new command handler."""
@@ -130,7 +133,7 @@ class register: # noqa: N801,N806 pylint: disable=invalid-name
# The arguments to pass to Command.
self._kwargs = kwargs
- def __call__(self, func: Callable) -> Callable:
+ def __call__(self, func: _CmdHandlerType) -> _CmdHandlerType:
"""Register the command before running the function.
Gets called when a function should be decorated.
@@ -222,7 +225,7 @@ class argument: # noqa: N801,N806 pylint: disable=invalid-name
self._argname = argname # The name of the argument to handle.
self._kwargs = kwargs # Valid ArgInfo members.
- def __call__(self, func: Callable) -> Callable:
+ def __call__(self, func: _CmdHandlerType) -> _CmdHandlerType:
funcname = func.__name__
if self._argname not in inspect.signature(func).parameters:
diff --git a/qutebrowser/api/hook.py b/qutebrowser/api/hook.py
index eadc310f3..884d6c67f 100644
--- a/qutebrowser/api/hook.py
+++ b/qutebrowser/api/hook.py
@@ -22,13 +22,13 @@
"""Hooks for extensions."""
import importlib
-from typing import Callable
+from typing import Callable, Any
from qutebrowser.extensions import loader
-def _add_module_info(func: Callable) -> loader.ModuleInfo:
+def _add_module_info(func: Callable[..., Any]) -> loader.ModuleInfo:
"""Add module info to the given function."""
module = importlib.import_module(func.__module__)
return loader.add_module_info(module)
@@ -48,7 +48,7 @@ class init:
message.info("Extension initialized.")
"""
- def __call__(self, func: Callable) -> Callable:
+ def __call__(self, func: loader.InitHookType) -> loader.InitHookType:
info = _add_module_info(func)
if info.init_hook is not None:
raise ValueError("init hook is already registered!")
@@ -86,7 +86,10 @@ class config_changed:
def __init__(self, option_filter: str = None) -> None:
self._filter = option_filter
- def __call__(self, func: Callable) -> Callable:
+ def __call__(
+ self,
+ func: loader.ConfigChangedHookType,
+ ) -> loader.ConfigChangedHookType:
info = _add_module_info(func)
info.config_changed_hooks.append((self._filter, func))
return func
diff --git a/qutebrowser/browser/browsertab.py b/qutebrowser/browser/browsertab.py
index cbe698009..b3d1e85f7 100644
--- a/qutebrowser/browser/browsertab.py
+++ b/qutebrowser/browser/browsertab.py
@@ -34,9 +34,10 @@ from PyQt5.QtPrintSupport import QPrintDialog, QPrinter
from PyQt5.QtNetwork import QNetworkAccessManager
if TYPE_CHECKING:
- from PyQt5.QtWebKit import QWebHistory
+ from PyQt5.QtWebKit import QWebHistory, QWebHistoryItem
from PyQt5.QtWebKitWidgets import QWebPage
- from PyQt5.QtWebEngineWidgets import QWebEngineHistory, QWebEnginePage
+ from PyQt5.QtWebEngineWidgets import (
+ QWebEngineHistory, QWebEngineHistoryItem, QWebEnginePage)
from qutebrowser.keyinput import modeman
from qutebrowser.config import config
@@ -634,8 +635,8 @@ class AbstractHistoryPrivate:
"""Deserialize from a format produced by self.serialize."""
raise NotImplementedError
- def load_items(self, items: Sequence) -> None:
- """Deserialize from a list of WebHistoryItems."""
+ def load_items(self, items: Sequence[sessions.TabHistoryItem]) -> None:
+ """Deserialize from a list of TabHistoryItems."""
raise NotImplementedError
@@ -651,7 +652,7 @@ class AbstractHistory:
def __len__(self) -> int:
raise NotImplementedError
- def __iter__(self) -> Iterable:
+ def __iter__(self) -> Iterable[Union['QWebHistoryItem', 'QWebEngineHistoryItem']]:
raise NotImplementedError
def _check_count(self, count: int) -> None:
diff --git a/qutebrowser/browser/commands.py b/qutebrowser/browser/commands.py
index e9653ae19..8cd73ae4f 100644
--- a/qutebrowser/browser/commands.py
+++ b/qutebrowser/browser/commands.py
@@ -602,7 +602,7 @@ class CommandDispatcher:
widget = self._current_widget()
url = self._current_url()
- handlers: Dict[str, Callable] = {
+ handlers: Dict[str, Callable[..., QUrl]] = {
'prev': functools.partial(navigate.prevnext, prev=True),
'next': functools.partial(navigate.prevnext, prev=False),
'up': navigate.path_up,
diff --git a/qutebrowser/browser/qutescheme.py b/qutebrowser/browser/qutescheme.py
index cfb238188..031b6fe06 100644
--- a/qutebrowser/browser/qutescheme.py
+++ b/qutebrowser/browser/qutescheme.py
@@ -92,7 +92,8 @@ class Redirect(Exception):
# Return value: (mimetype, data) (encoded as utf-8 if a str is returned)
_HandlerRet = Tuple[str, Union[str, bytes]]
-_Handler = TypeVar('_Handler', bound=Callable[[QUrl], _HandlerRet])
+_HandlerCallable = Callable[[QUrl], _HandlerRet]
+_Handler = TypeVar('_Handler', bound=_HandlerCallable)
class add_handler: # noqa: N801,N806 pylint: disable=invalid-name
@@ -105,7 +106,7 @@ class add_handler: # noqa: N801,N806 pylint: disable=invalid-name
def __init__(self, name: str) -> None:
self._name = name
- self._function: Optional[Callable] = None
+ self._function: Optional[_HandlerCallable] = None
def __call__(self, function: _Handler) -> _Handler:
self._function = function
diff --git a/qutebrowser/browser/webelem.py b/qutebrowser/browser/webelem.py
index f53ad1afb..9ec29ce07 100644
--- a/qutebrowser/browser/webelem.py
+++ b/qutebrowser/browser/webelem.py
@@ -58,7 +58,8 @@ def css_selector(group: str, url: QUrl) -> str:
return ','.join(selectors[group])
-class AbstractWebElement(collections.abc.MutableMapping):
+# MutableMapping is only generic in Python 3.9+
+class AbstractWebElement(collections.abc.MutableMapping): # type: ignore[type-arg]
"""A wrapper around QtWebKit/QtWebEngine web element."""
diff --git a/qutebrowser/browser/webengine/notification.py b/qutebrowser/browser/webengine/notification.py
index 50f73fca4..d8387e6d4 100644
--- a/qutebrowser/browser/webengine/notification.py
+++ b/qutebrowser/browser/webengine/notification.py
@@ -669,7 +669,8 @@ class _ServerCapabilities:
def _as_uint32(x: int) -> QVariant:
"""Convert the given int to an uint32 for DBus."""
variant = QVariant(x)
- assert variant.convert(QVariant.UInt)
+ successful = variant.convert(QVariant.UInt)
+ assert successful
return variant
diff --git a/qutebrowser/browser/webengine/tabhistory.py b/qutebrowser/browser/webengine/tabhistory.py
index d6c795fb2..ab4b05fe9 100644
--- a/qutebrowser/browser/webengine/tabhistory.py
+++ b/qutebrowser/browser/webengine/tabhistory.py
@@ -35,10 +35,10 @@ HISTORY_STREAM_VERSION = 3
def _serialize_item(item, stream):
- """Serialize a single WebHistoryItem into a QDataStream.
+ """Serialize a single TabHistoryItem into a QDataStream.
Args:
- item: The WebHistoryItem to write.
+ item: The TabHistoryItem to write.
stream: The QDataStream to write to.
"""
# Thanks to Otter Browser:
@@ -108,10 +108,10 @@ def _serialize_item(item, stream):
def serialize(items):
- """Serialize a list of WebHistoryItems to a data stream.
+ """Serialize a list of TabHistoryItems to a data stream.
Args:
- items: An iterable of WebHistoryItems.
+ items: An iterable of TabHistoryItems.
Return:
A (stream, data, user_data) tuple.
diff --git a/qutebrowser/browser/webengine/webenginetab.py b/qutebrowser/browser/webengine/webenginetab.py
index 15784d6bf..c793a1929 100644
--- a/qutebrowser/browser/webengine/webenginetab.py
+++ b/qutebrowser/browser/webengine/webenginetab.py
@@ -983,6 +983,11 @@ class _Quirk:
QWebEngineScript.DocumentCreation)
world: QWebEngineScript.ScriptWorldId = QWebEngineScript.MainWorld
predicate: bool = True
+ name: Optional[str] = None
+
+ def __post_init__(self):
+ if self.name is None:
+ self.name = f"js-{self.filename.replace('_', '-')}"
class _WebEngineScripts(QObject):
@@ -1154,6 +1159,11 @@ class _WebEngineScripts(QObject):
),
_Quirk('discord'),
_Quirk(
+ 'googledocs',
+ # will be an UA quirk once we set the JS UA as well
+ name='ua-googledocs',
+ ),
+ _Quirk(
'string_replaceall',
predicate=versions.webengine < utils.VersionNumber(5, 15, 3),
),
@@ -1171,8 +1181,7 @@ class _WebEngineScripts(QObject):
if not quirk.predicate:
continue
src = resources.read_file(f'javascript/quirks/{quirk.filename}.user.js')
- name = f"js-{quirk.filename.replace('_', '-')}"
- if name not in config.val.content.site_specific_quirks.skip:
+ if quirk.name not in config.val.content.site_specific_quirks.skip:
self._inject_js(
f'quirk_{quirk.filename}',
src,
diff --git a/qutebrowser/browser/webkit/tabhistory.py b/qutebrowser/browser/webkit/tabhistory.py
index c38968b62..a707030d1 100644
--- a/qutebrowser/browser/webkit/tabhistory.py
+++ b/qutebrowser/browser/webkit/tabhistory.py
@@ -64,10 +64,10 @@ def _serialize_item(item):
def serialize(items):
- """Serialize a list of WebHistoryItems to a data stream.
+ """Serialize a list of TabHistoryItems to a data stream.
Args:
- items: An iterable of WebHistoryItems.
+ items: An iterable of TabHistoryItems.
Return:
A (stream, data, user_data) tuple.
diff --git a/qutebrowser/config/config.py b/qutebrowser/config/config.py
index 374019677..437a54a33 100644
--- a/qutebrowser/config/config.py
+++ b/qutebrowser/config/config.py
@@ -97,7 +97,10 @@ class change_filter: # noqa: N801,N806 pylint: disable=invalid-name
else:
return False
- def __call__(self, func: Callable) -> Callable:
+ def __call__(
+ self,
+ func: Callable[..., None],
+ ) -> Callable[..., None]:
"""Filter calls to the decorated function.
Gets called when a function should be decorated.
@@ -105,7 +108,9 @@ class change_filter: # noqa: N801,N806 pylint: disable=invalid-name
Adds a filter which returns if we're not interested in the change-event
and calls the wrapped function if we are.
- We assume the function passed doesn't take any parameters.
+ We assume the function passed doesn't take any parameters. However, it
+ could take a "self" argument, so we can't cleary express this in the
+ type above.
Args:
func: The function to be decorated.
@@ -173,6 +178,8 @@ class KeyConfig:
result = results[0]
if result.cmd.name != "set-cmd-text":
return cmdline
+ if not result.args:
+ return None # doesn't look like this sets a command
*flags, cmd = result.args
if "-a" in flags or "--append" in flags or not cmd.startswith(":"):
return None # doesn't look like this sets a command
@@ -307,7 +314,7 @@ class Config(QObject):
def _init_values(self) -> None:
"""Populate the self._values dict."""
- self._values: Mapping = {}
+ self._values: Mapping[str, configutils.Values] = {}
for name, opt in configdata.DATA.items():
self._values[name] = configutils.Values(opt)
diff --git a/qutebrowser/config/configcommands.py b/qutebrowser/config/configcommands.py
index 2084556da..143b02fca 100644
--- a/qutebrowser/config/configcommands.py
+++ b/qutebrowser/config/configcommands.py
@@ -21,7 +21,7 @@
import os.path
import contextlib
-from typing import TYPE_CHECKING, Iterator, List, Optional
+from typing import TYPE_CHECKING, Iterator, List, Optional, Any, Tuple
from PyQt5.QtCore import QUrl
@@ -475,7 +475,7 @@ class ConfigCommands:
raise cmdutils.CommandError("{} already exists - use --force to "
"overwrite!".format(filename))
- options: List = []
+ options: List[Tuple[Optional[urlmatch.UrlPattern], configdata.Option, Any]] = []
if defaults:
options = [(None, opt, opt.default)
for _name, opt in sorted(configdata.DATA.items())]
diff --git a/qutebrowser/config/configdata.yml b/qutebrowser/config/configdata.yml
index 6cb277e5c..80732d43d 100644
--- a/qutebrowser/config/configdata.yml
+++ b/qutebrowser/config/configdata.yml
@@ -545,6 +545,7 @@ content.site_specific_quirks.skip:
- ua-whatsapp
- ua-google
- ua-slack
+ - ua-googledocs
- js-whatsapp-web
- js-discord
- js-string-replaceall
@@ -3269,6 +3270,7 @@ fonts.tabs.unselected:
fonts.web.family.standard:
default: ''
+ supports_pattern: true
type:
name: FontFamily
none_ok: true
@@ -3276,6 +3278,7 @@ fonts.web.family.standard:
fonts.web.family.fixed:
default: ''
+ supports_pattern: true
type:
name: FontFamily
none_ok: true
@@ -3283,6 +3286,7 @@ fonts.web.family.fixed:
fonts.web.family.serif:
default: ''
+ supports_pattern: true
type:
name: FontFamily
none_ok: true
@@ -3290,6 +3294,7 @@ fonts.web.family.serif:
fonts.web.family.sans_serif:
default: ''
+ supports_pattern: true
type:
name: FontFamily
none_ok: true
@@ -3297,6 +3302,7 @@ fonts.web.family.sans_serif:
fonts.web.family.cursive:
default: ''
+ supports_pattern: true
type:
name: FontFamily
none_ok: true
@@ -3304,6 +3310,7 @@ fonts.web.family.cursive:
fonts.web.family.fantasy:
default: ''
+ supports_pattern: true
type:
name: FontFamily
none_ok: true
@@ -3316,6 +3323,7 @@ fonts.web.family.fantasy:
fonts.web.size.default:
default: 16
+ supports_pattern: true
type:
name: Int
minval: 1
@@ -3324,6 +3332,7 @@ fonts.web.size.default:
fonts.web.size.default_fixed:
default: 13
+ supports_pattern: true
type:
name: Int
minval: 1
@@ -3332,6 +3341,7 @@ fonts.web.size.default_fixed:
fonts.web.size.minimum:
default: 0
+ supports_pattern: true
type:
name: Int
minval: 0
@@ -3342,6 +3352,7 @@ fonts.web.size.minimum_logical:
# This is 0 as default on QtWebKit, and 6 on QtWebEngine - so let's
# just go for 6 here.
default: 6
+ supports_pattern: true
type:
name: Int
minval: 0
diff --git a/qutebrowser/config/configfiles.py b/qutebrowser/config/configfiles.py
index f8566e2d0..6f0d0b13c 100644
--- a/qutebrowser/config/configfiles.py
+++ b/qutebrowser/config/configfiles.py
@@ -30,7 +30,7 @@ import configparser
import contextlib
import re
from typing import (TYPE_CHECKING, Any, Dict, Iterable, Iterator, List, Mapping,
- MutableMapping, Optional, cast)
+ MutableMapping, Optional, Tuple, cast)
import yaml
from PyQt5.QtCore import pyqtSignal, pyqtSlot, QObject, QSettings, qVersion
@@ -39,7 +39,7 @@ import qutebrowser
from qutebrowser.config import (configexc, config, configdata, configutils,
configtypes)
from qutebrowser.keyinput import keyutils
-from qutebrowser.utils import standarddir, utils, qtutils, log, urlmatch
+from qutebrowser.utils import standarddir, utils, qtutils, log, urlmatch, version
if TYPE_CHECKING:
from qutebrowser.misc import savemanager
@@ -89,6 +89,7 @@ class StateConfig(configparser.ConfigParser):
self.read(self._filename, encoding='utf-8')
self.qt_version_changed = False
+ self.qtwe_version_changed = False
self.qutebrowser_version_changed = VersionChange.unknown
self._set_changed_attributes()
@@ -108,8 +109,20 @@ class StateConfig(configparser.ConfigParser):
self[sect].pop(key, None)
self['general']['qt_version'] = qVersion()
+ self['general']['qtwe_version'] = self._qtwe_version_str()
self['general']['version'] = qutebrowser.__version__
+ def _qtwe_version_str(self) -> str:
+ """Get the QtWebEngine version string.
+
+ Note that it's too early to use objects.backend here...
+ """
+ try:
+ import PyQt5.QtWebEngineWidgets # pylint: disable=unused-import
+ except ImportError:
+ return 'no'
+ return str(version.qtwebengine_versions(avoid_init=True).webengine)
+
def _set_changed_attributes(self) -> None:
"""Set qt_version_changed/qutebrowser_version_changed attributes.
@@ -123,6 +136,9 @@ class StateConfig(configparser.ConfigParser):
old_qt_version = self['general'].get('qt_version', None)
self.qt_version_changed = old_qt_version != qVersion()
+ old_qtwe_version = self['general'].get('qtwe_version', None)
+ self.qtwe_version_changed = old_qtwe_version != self._qtwe_version_str()
+
old_qutebrowser_version = self['general'].get('version', None)
if old_qutebrowser_version is None:
# https://github.com/python/typeshed/issues/2093
@@ -286,18 +302,18 @@ class YamlConfig(QObject):
self._validate_names(settings)
self._build_values(settings)
- def _load_settings_object(self, yaml_data: Any) -> '_SettingsType':
+ def _load_settings_object(self, yaml_data: Any) -> _SettingsType:
"""Load the settings from the settings: key."""
return self._pop_object(yaml_data, 'settings', dict)
- def _load_legacy_settings_object(self, yaml_data: Any) -> '_SettingsType':
+ def _load_legacy_settings_object(self, yaml_data: Any) -> _SettingsType:
data = self._pop_object(yaml_data, 'global', dict)
settings = {}
for name, value in data.items():
settings[name] = {'global': value}
return settings
- def _build_values(self, settings: Mapping) -> None:
+ def _build_values(self, settings: Mapping[str, Any]) -> None:
"""Build up self._values from the values in the given dict."""
errors = []
for name, yaml_values in settings.items():
@@ -724,9 +740,17 @@ class ConfigPyWriter:
def __init__(
self,
- options: List,
+ options: List[
+ Tuple[
+ Optional[urlmatch.UrlPattern],
+ configdata.Option,
+ Any
+ ]
+ ],
bindings: MutableMapping[str, Mapping[str, Optional[str]]],
- *, commented: bool) -> None:
+ *,
+ commented: bool,
+ ) -> None:
self._options = options
self._bindings = bindings
self._commented = commented
diff --git a/qutebrowser/config/websettings.py b/qutebrowser/config/websettings.py
index 1b07baab7..7556d2b6d 100644
--- a/qutebrowser/config/websettings.py
+++ b/qutebrowser/config/websettings.py
@@ -23,7 +23,7 @@ import re
import argparse
import functools
import dataclasses
-from typing import Any, Callable, Dict, Optional
+from typing import Any, Callable, Dict, Optional, Union
from PyQt5.QtCore import QUrl, pyqtSlot, qVersion
from PyQt5.QtGui import QFont
@@ -85,7 +85,11 @@ class AttributeInfo:
"""Info about a settings attribute."""
- def __init__(self, *attributes: Any, converter: Callable = None) -> None:
+ def __init__(
+ self,
+ *attributes: Any,
+ converter: Callable[[Any], bool] = None,
+ ) -> None:
self.attributes = attributes
if converter is None:
self.converter = lambda val: val
@@ -105,9 +109,6 @@ class AbstractSettings:
def __init__(self, settings: Any) -> None:
self._settings = settings
- def _assert_not_unset(self, value: Any) -> None:
- assert value is not usertypes.UNSET
-
def set_attribute(self, name: str, value: Any) -> None:
"""Set the given QWebSettings/QWebEngineSettings attribute.
@@ -129,30 +130,38 @@ class AbstractSettings:
info = self._ATTRIBUTES[name]
return self._settings.testAttribute(info.attributes[0])
- def set_font_size(self, name: str, value: int) -> None:
+ def set_font_size(self, name: str, value: Union[int, usertypes.Unset]) -> None:
"""Set the given QWebSettings/QWebEngineSettings font size."""
- self._assert_not_unset(value)
family = self._FONT_SIZES[name]
- self._settings.setFontSize(family, value)
+ if value is usertypes.UNSET:
+ self._settings.resetFontSize(family)
+ else:
+ self._settings.setFontSize(family, value)
- def set_font_family(self, name: str, value: Optional[str]) -> None:
+ def set_font_family(
+ self,
+ name: str,
+ value: Union[str, None, usertypes.Unset],
+ ) -> None:
"""Set the given QWebSettings/QWebEngineSettings font family.
With None (the default), QFont is used to get the default font for the
family.
"""
- self._assert_not_unset(value)
family = self._FONT_FAMILIES[name]
- if value is None:
+ if value is usertypes.UNSET:
+ self._settings.resetFontFamily(family)
+ elif value is None:
font = QFont()
font.setStyleHint(self._FONT_TO_QFONT[family])
value = font.defaultFamily()
+ self._settings.setFontFamily(family, value)
+ else:
+ self._settings.setFontFamily(family, value)
- self._settings.setFontFamily(family, value)
-
- def set_default_text_encoding(self, encoding: str) -> None:
+ def set_default_text_encoding(self, encoding: Union[str, usertypes.Unset]) -> None:
"""Set the default text encoding to use."""
- self._assert_not_unset(encoding)
+ assert encoding is not usertypes.UNSET # unclear how to reset
self._settings.setDefaultTextEncoding(encoding)
def _update_setting(self, setting: str, value: Any) -> bool:
diff --git a/qutebrowser/extensions/loader.py b/qutebrowser/extensions/loader.py
index 9b704c94d..7ae45023b 100644
--- a/qutebrowser/extensions/loader.py
+++ b/qutebrowser/extensions/loader.py
@@ -39,6 +39,9 @@ from qutebrowser.misc import objects
# ModuleInfo objects for all loaded plugins
_module_infos = []
+InitHookType = Callable[['InitContext'], None]
+ConfigChangedHookType = Callable[[], None]
+
@dataclasses.dataclass
class InitContext:
@@ -59,9 +62,13 @@ class ModuleInfo:
"""
skip_hooks: bool = False
- init_hook: Optional[Callable] = None
- config_changed_hooks: List[Tuple[Optional[str], Callable]] = dataclasses.field(
- default_factory=list)
+ init_hook: Optional[InitHookType] = None
+ config_changed_hooks: List[
+ Tuple[
+ Optional[str],
+ ConfigChangedHookType,
+ ]
+ ] = dataclasses.field(default_factory=list)
@dataclasses.dataclass
diff --git a/qutebrowser/javascript/quirks/googledocs.user.js b/qutebrowser/javascript/quirks/googledocs.user.js
new file mode 100644
index 000000000..7ec47f70d
--- /dev/null
+++ b/qutebrowser/javascript/quirks/googledocs.user.js
@@ -0,0 +1,14 @@
+// ==UserScript==
+// @include https://docs.google.com/*
+// ==/UserScript==
+
+// Workaround for typing dead keys on Google Docs
+// See https://bugreports.qt.io/browse/QTBUG-69652
+
+"use strict";
+
+Object.defineProperty(navigator, "userAgent", {
+ get() {
+ return "Mozilla/5.0 (X11; Linux x86_64; rv:88.0) Gecko/20100101 Firefox/88.0";
+ },
+});
diff --git a/qutebrowser/keyinput/basekeyparser.py b/qutebrowser/keyinput/basekeyparser.py
index d1828791c..7e688dab1 100644
--- a/qutebrowser/keyinput/basekeyparser.py
+++ b/qutebrowser/keyinput/basekeyparser.py
@@ -112,7 +112,7 @@ class BindingTrie:
return lines
- def update(self, mapping: Mapping) -> None:
+ def update(self, mapping: Mapping[keyutils.KeySequence, str]) -> None:
"""Add data from the given mapping to the trie."""
for key in mapping:
self[key] = mapping[key]
diff --git a/qutebrowser/keyinput/keyutils.py b/qutebrowser/keyinput/keyutils.py
index ddf818708..6bd8c99b8 100644
--- a/qutebrowser/keyinput/keyutils.py
+++ b/qutebrowser/keyinput/keyutils.py
@@ -458,7 +458,7 @@ class KeySequence:
assert self
self._validate()
- def _convert_key(self, key: Qt.Key) -> int:
+ def _convert_key(self, key: Union[int, Qt.KeyboardModifiers]) -> int:
"""Convert a single key for QKeySequence."""
assert isinstance(key, (int, Qt.KeyboardModifiers)), key
return int(key)
diff --git a/qutebrowser/misc/backendproblem.py b/qutebrowser/misc/backendproblem.py
index 001aa3047..3e14719e0 100644
--- a/qutebrowser/misc/backendproblem.py
+++ b/qutebrowser/misc/backendproblem.py
@@ -389,6 +389,7 @@ class _BackendProblemChecker:
WORKAROUND for:
https://bugreports.qt.io/browse/QTBUG-72532
https://bugreports.qt.io/browse/QTBUG-82105
+ https://bugreports.qt.io/browse/QTBUG-93744
"""
if ('serviceworker_workaround' not in configfiles.state['general'] and
qtutils.version_check('5.14', compiled=False)):
@@ -398,6 +399,8 @@ class _BackendProblemChecker:
reason = 'Qt 5.14'
elif configfiles.state.qt_version_changed:
reason = 'Qt version changed'
+ elif configfiles.state.qtwe_version_changed:
+ reason = 'QtWebEngine version changed'
elif config.val.qt.workarounds.remove_service_workers:
reason = 'Explicitly enabled'
else:
diff --git a/qutebrowser/misc/debugcachestats.py b/qutebrowser/misc/debugcachestats.py
index f172f0854..2004ad7ab 100644
--- a/qutebrowser/misc/debugcachestats.py
+++ b/qutebrowser/misc/debugcachestats.py
@@ -30,7 +30,7 @@ from typing import Any, Callable, List, Optional, Tuple, TypeVar
_CACHE_FUNCTIONS: List[Tuple[str, Any]] = []
-_T = TypeVar('_T', bound=Callable)
+_T = TypeVar('_T', bound=Callable[..., Any])
def register(name: Optional[str] = None) -> Callable[[_T], _T]:
diff --git a/qutebrowser/misc/elf.py b/qutebrowser/misc/elf.py
index 307d3b4ac..bf824880a 100644
--- a/qutebrowser/misc/elf.py
+++ b/qutebrowser/misc/elf.py
@@ -65,7 +65,7 @@ import re
import dataclasses
import mmap
import pathlib
-from typing import IO, ClassVar, Dict, Optional, Tuple, cast
+from typing import Any, IO, ClassVar, Dict, Optional, Tuple, cast
from PyQt5.QtCore import QLibraryInfo
@@ -93,7 +93,7 @@ class Endianness(enum.Enum):
big = 2
-def _unpack(fmt: str, fobj: IO[bytes]) -> Tuple:
+def _unpack(fmt: str, fobj: IO[bytes]) -> Tuple[Any, ...]:
"""Unpack the given struct format from the given file."""
size = struct.calcsize(fmt)
data = _safe_read(fobj, size)
diff --git a/qutebrowser/misc/throttle.py b/qutebrowser/misc/throttle.py
index 1beebe1aa..ac565b68d 100644
--- a/qutebrowser/misc/throttle.py
+++ b/qutebrowser/misc/throttle.py
@@ -45,7 +45,7 @@ class Throttle(QObject):
"""
def __init__(self,
- func: Callable,
+ func: Callable[..., None],
delay_ms: int,
parent: QObject = None) -> None:
"""Constructor.
diff --git a/qutebrowser/utils/debug.py b/qutebrowser/utils/debug.py
index 54fcd5aa9..7d069909a 100644
--- a/qutebrowser/utils/debug.py
+++ b/qutebrowser/utils/debug.py
@@ -35,7 +35,7 @@ from qutebrowser.misc import objects
from qutebrowser.qt import sip
-def log_events(klass: Type) -> Type:
+def log_events(klass: Type[QObject]) -> Type[QObject]:
"""Class decorator to log Qt events."""
old_event = klass.event
@@ -46,7 +46,7 @@ def log_events(klass: Type) -> Type:
qenum_key(QEvent, e.type())))
return old_event(self, e)
- klass.event = new_event
+ klass.event = new_event # type: ignore[assignment]
return klass
@@ -96,10 +96,13 @@ def log_signals(obj: QObject) -> QObject:
return obj
-def qenum_key(base: Type,
- value: Union[int, sip.simplewrapper],
+_EnumValueType = Union[sip.simplewrapper, int]
+
+
+def qenum_key(base: Type[_EnumValueType],
+ value: _EnumValueType,
add_base: bool = False,
- klass: Type = None) -> str:
+ klass: Type[_EnumValueType] = None) -> str:
"""Convert a Qt Enum value to its key as a string.
Args:
@@ -119,8 +122,9 @@ def qenum_key(base: Type,
raise TypeError("Can't guess enum class of an int!")
try:
- idx = base.staticMetaObject.indexOfEnumerator(klass.__name__)
- meta_enum = base.staticMetaObject.enumerator(idx)
+ meta_obj = base.staticMetaObject # type: ignore[union-attr]
+ idx = meta_obj.indexOfEnumerator(klass.__name__)
+ meta_enum = meta_obj.enumerator(idx)
ret = meta_enum.valueToKey(int(value)) # type: ignore[arg-type]
except AttributeError:
ret = None
@@ -139,10 +143,10 @@ def qenum_key(base: Type,
return ret
-def qflags_key(base: Type,
- value: Union[int, sip.simplewrapper],
+def qflags_key(base: Type[_EnumValueType],
+ value: _EnumValueType,
add_base: bool = False,
- klass: Type = None) -> str:
+ klass: Type[_EnumValueType] = None) -> str:
"""Convert a Qt QFlags value to its keys as string.
Note: Passing a combined value (such as Qt.AlignCenter) will get the names
@@ -220,7 +224,7 @@ def signal_name(sig: pyqtBoundSignal) -> str:
return m.group('name')
-def format_args(args: Sequence = None, kwargs: Mapping = None) -> str:
+def format_args(args: Sequence[Any] = None, kwargs: Mapping[str, Any] = None) -> str:
"""Format a list of arguments/kwargs to a function-call like string."""
if args is not None:
arglist = [utils.compact_text(repr(arg), 200) for arg in args]
@@ -245,9 +249,9 @@ def dbg_signal(sig: pyqtBoundSignal, args: Any) -> str:
return '{}({})'.format(signal_name(sig), format_args(args))
-def format_call(func: Callable,
- args: Sequence = None,
- kwargs: Mapping = None,
+def format_call(func: Callable[..., Any],
+ args: Sequence[Any] = None,
+ kwargs: Mapping[str, Any] = None,
full: bool = True) -> str:
"""Get a string representation of a function calls with the given args.
@@ -302,7 +306,7 @@ class log_time: # noqa: N801,N806 pylint: disable=invalid-name
self._logger.debug("{} took {} seconds.".format(
self._action.capitalize(), delta))
- def __call__(self, func: Callable) -> Callable:
+ def __call__(self, func: Callable[..., Any]) -> Callable[..., Any]:
@functools.wraps(func)
def wrapped(*args: Any, **kwargs: Any) -> Any:
"""Call the original function."""
diff --git a/qutebrowser/utils/docutils.py b/qutebrowser/utils/docutils.py
index 202fcba95..89e799c89 100644
--- a/qutebrowser/utils/docutils.py
+++ b/qutebrowser/utils/docutils.py
@@ -25,7 +25,7 @@ import inspect
import os.path
import collections
import enum
-from typing import Callable, MutableMapping, Optional, List, Union
+from typing import Any, Callable, MutableMapping, Optional, List, Union
import qutebrowser
from qutebrowser.utils import log, utils
@@ -88,7 +88,7 @@ class DocstringParser:
arg_inside = enum.auto()
misc = enum.auto()
- def __init__(self, func: Callable) -> None:
+ def __init__(self, func: Callable[..., Any]) -> None:
"""Constructor.
Args:
diff --git a/qutebrowser/utils/jinja.py b/qutebrowser/utils/jinja.py
index 61d8ccdad..a44a0235e 100644
--- a/qutebrowser/utils/jinja.py
+++ b/qutebrowser/utils/jinja.py
@@ -145,7 +145,7 @@ js_environment = jinja2.Environment(loader=Loader('javascript'))
@functools.lru_cache()
def template_config_variables(template: str) -> FrozenSet[str]:
"""Return the config variables used in the template."""
- unvisted_nodes = [environment.parse(template)]
+ unvisted_nodes: List[jinja2.nodes.Node] = [environment.parse(template)]
result: Set[str] = set()
while unvisted_nodes:
node = unvisted_nodes.pop()
@@ -157,11 +157,11 @@ def template_config_variables(template: str) -> FrozenSet[str]:
# For example it's ['ab', 'c', 'd'] for 'conf.d.c.ab'.
attrlist: List[str] = []
while isinstance(node, jinja2.nodes.Getattr):
- attrlist.append(node.attr) # type: ignore[attr-defined]
- node = node.node # type: ignore[attr-defined]
+ attrlist.append(node.attr)
+ node = node.node
if isinstance(node, jinja2.nodes.Name):
- if node.name == 'conf': # type: ignore[attr-defined]
+ if node.name == 'conf':
result.add('.'.join(reversed(attrlist)))
# otherwise, the node is a Name node so it doesn't have any
# child nodes
diff --git a/qutebrowser/utils/objreg.py b/qutebrowser/utils/objreg.py
index 99d8a0936..0819a5d0a 100644
--- a/qutebrowser/utils/objreg.py
+++ b/qutebrowser/utils/objreg.py
@@ -55,7 +55,8 @@ class CommandOnlyError(Exception):
_IndexType = Union[str, int]
-class ObjectRegistry(collections.UserDict):
+# UserDict is only generic in Python 3.9+
+class ObjectRegistry(collections.UserDict): # type: ignore[type-arg]
"""A registry of long-living objects in qutebrowser.
diff --git a/qutebrowser/utils/qtutils.py b/qutebrowser/utils/qtutils.py
index 01234a42b..ff8983c50 100644
--- a/qutebrowser/utils/qtutils.py
+++ b/qutebrowser/utils/qtutils.py
@@ -31,7 +31,8 @@ Module attributes:
import io
import operator
import contextlib
-from typing import TYPE_CHECKING, BinaryIO, IO, Iterator, Optional, Union, Tuple, cast
+from typing import (Any, AnyStr, TYPE_CHECKING, BinaryIO, IO, Iterator,
+ Optional, Union, Tuple, cast)
from PyQt5.QtCore import (qVersion, QEventLoop, QDataStream, QByteArray,
QIODevice, QFileDevice, QSaveFile, QT_VERSION_STR,
@@ -227,7 +228,7 @@ def savefile_open(
filename: str,
binary: bool = False,
encoding: str = 'utf-8'
-) -> Iterator[IO]:
+) -> Iterator[IO[AnyStr]]:
"""Context manager to easily use a QSaveFile."""
f = QSaveFile(filename)
cancelled = False
@@ -239,7 +240,7 @@ def savefile_open(
dev = cast(BinaryIO, PyQIODevice(f))
if binary:
- new_f: IO = dev
+ new_f: IO[Any] = dev # FIXME:mypy Why doesn't AnyStr work?
else:
new_f = io.TextIOWrapper(dev, encoding=encoding)
@@ -298,7 +299,11 @@ class PyQIODevice(io.BufferedIOBase):
if not self.writable():
raise OSError("Trying to write to unwritable file!")
- def open(self, mode: QIODevice.OpenMode) -> contextlib.closing:
+ # contextlib.closing is only generic in Python 3.9+
+ def open(
+ self,
+ mode: QIODevice.OpenMode,
+ ) -> contextlib.closing: # type: ignore[type-arg]
"""Open the underlying device and ensure opening succeeded.
Raises OSError if opening failed.
diff --git a/qutebrowser/utils/urlmatch.py b/qutebrowser/utils/urlmatch.py
index 8dfd6d273..f14c2083d 100644
--- a/qutebrowser/utils/urlmatch.py
+++ b/qutebrowser/utils/urlmatch.py
@@ -104,7 +104,14 @@ class UrlPattern:
self._init_path(parsed)
self._init_port(parsed)
- def _to_tuple(self) -> Tuple:
+ def _to_tuple(self) -> Tuple[
+ bool, # _match_all
+ bool, # _match_subdomains
+ Optional[str], # _scheme
+ Optional[str], # host
+ Optional[str], # _path
+ Optional[int], # _port
+ ]:
"""Get a pattern with information used for __eq__/__hash__."""
return (self._match_all, self._match_subdomains, self._scheme,
self.host, self._path, self._port)
diff --git a/qutebrowser/utils/utils.py b/qutebrowser/utils/utils.py
index 56ebe45c4..fb0165de2 100644
--- a/qutebrowser/utils/utils.py
+++ b/qutebrowser/utils/utils.py
@@ -341,7 +341,7 @@ class prevent_exceptions: # noqa: N801,N806 pylint: disable=invalid-name
self._retval = retval
self._predicate = predicate
- def __call__(self, func: Callable) -> Callable:
+ def __call__(self, func: Callable[..., Any]) -> Callable[..., Any]:
"""Called when a function should be decorated.
Args:
@@ -447,7 +447,7 @@ def qualname(obj: Any) -> str:
_ExceptionType = Union[Type[BaseException], Tuple[Type[BaseException]]]
-def raises(exc: _ExceptionType, func: Callable, *args: Any) -> bool:
+def raises(exc: _ExceptionType, func: Callable[..., Any], *args: Any) -> bool:
"""Check if a function raises a given exception.
Args:
@@ -725,7 +725,10 @@ def yaml_dump(data: Any, f: IO[str] = None) -> Optional[str]:
return yaml_data.decode('utf-8')
-def chunk(elems: Sequence, n: int) -> Iterator[Sequence]:
+_T = TypeVar('_T')
+
+
+def chunk(elems: Sequence[_T], n: int) -> Iterator[Sequence[_T]]:
"""Yield successive n-sized chunks from elems.
If elems % n != 0, the last chunk will be smaller.
diff --git a/qutebrowser/utils/version.py b/qutebrowser/utils/version.py
index f5bc5eab2..8cd244fca 100644
--- a/qutebrowser/utils/version.py
+++ b/qutebrowser/utils/version.py
@@ -591,7 +591,7 @@ class WebEngineVersions:
def __str__(self) -> str:
s = f'QtWebEngine {self.webengine}'
if self.chromium is not None:
- s += f', Chromium {self.chromium}'
+ s += f', based on Chromium {self.chromium}'
if self.source != 'UA':
s += f' (from {self.source})'
return s
diff --git a/requirements.txt b/requirements.txt
index 9fbd22da3..88033d3e7 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -4,10 +4,10 @@ adblock==0.4.4
colorama==0.4.4
dataclasses==0.6 ; python_version<"3.7"
importlib-metadata==4.0.1 ; python_version<"3.8"
-importlib-resources==5.1.2 ; python_version<"3.9"
-Jinja2==2.11.3
-MarkupSafe==1.1.1
-Pygments==2.8.1
+importlib-resources==5.1.3 ; python_version<"3.9"
+Jinja2==3.0.0
+MarkupSafe==2.0.0
+Pygments==2.9.0
PyYAML==5.4.1
typing-extensions==3.10.0.0
zipp==3.4.1
diff --git a/scripts/dev/recompile_requirements.py b/scripts/dev/recompile_requirements.py
index b545dbc5f..8dd9e4c09 100644
--- a/scripts/dev/recompile_requirements.py
+++ b/scripts/dev/recompile_requirements.py
@@ -55,13 +55,13 @@ CHANGELOG_URLS = {
'pytest-repeat': 'https://github.com/pytest-dev/pytest-repeat/blob/master/CHANGES.rst',
'requests': 'https://github.com/psf/requests/blob/master/HISTORY.md',
'requests-file': 'https://github.com/dashea/requests-file/blob/master/CHANGES.rst',
- 'Werkzeug': 'https://github.com/pallets/werkzeug/blob/master/CHANGES.rst',
- 'click': 'https://click.palletsprojects.com/en/7.x/changelog/',
- 'itsdangerous': 'https://itsdangerous.palletsprojects.com/en/1.1.x/changes/',
+ 'Werkzeug': 'https://werkzeug.palletsprojects.com/en/latest/changes/',
+ 'click': 'https://click.palletsprojects.com/en/latest/changes/',
+ 'itsdangerous': 'https://itsdangerous.palletsprojects.com/en/latest/changes/',
'parse-type': 'https://github.com/jenisys/parse_type/blob/master/CHANGES.txt',
'sortedcontainers': 'https://github.com/grantjenks/python-sortedcontainers/blob/master/HISTORY.rst',
'soupsieve': 'https://facelessuser.github.io/soupsieve/about/changelog/',
- 'Flask': 'https://flask.palletsprojects.com/en/1.1.x/changelog/',
+ 'Flask': 'https://flask.palletsprojects.com/en/latest/changes/',
'Mako': 'https://docs.makotemplates.org/en/latest/changelog.html',
'glob2': 'https://github.com/miracle2k/python-glob2/blob/master/CHANGES',
'hypothesis': 'https://hypothesis.readthedocs.io/en/latest/changes.html',
@@ -76,8 +76,8 @@ CHANGELOG_URLS = {
'packaging': 'https://packaging.pypa.io/en/latest/changelog.html',
'build': 'https://github.com/pypa/build/blob/main/CHANGELOG.rst',
'attrs': 'https://www.attrs.org/en/stable/changelog.html',
- 'Jinja2': 'https://github.com/pallets/jinja/blob/master/CHANGES.rst',
- 'MarkupSafe': 'https://markupsafe.palletsprojects.com/en/1.1.x/changes/',
+ 'Jinja2': 'https://jinja.palletsprojects.com/en/latest/changes/',
+ 'MarkupSafe': 'https://markupsafe.palletsprojects.com/en/latest/changes/',
'flake8': 'https://gitlab.com/pycqa/flake8/tree/master/docs/source/release-notes',
'flake8-docstrings': 'https://pypi.org/project/flake8-docstrings/',
'flake8-debugger': 'https://github.com/JBKahn/flake8-debugger/',
diff --git a/scripts/mkvenv.py b/scripts/mkvenv.py
index 31b185fe1..58131bc05 100755
--- a/scripts/mkvenv.py
+++ b/scripts/mkvenv.py
@@ -391,7 +391,13 @@ def install_dev_requirements(venv_dir: pathlib.Path) -> None:
utils.print_title("Installing dev dependencies")
pip_install(venv_dir,
'-r', str(requirements_file('dev')),
- '-r', requirements_file('tests'))
+ '-r', str(requirements_file('check-manifest')),
+ '-r', str(requirements_file('flake8')),
+ '-r', str(requirements_file('mypy')),
+ '-r', str(requirements_file('pyroma')),
+ '-r', str(requirements_file('vulture')),
+ '-r', str(requirements_file('yamllint')),
+ '-r', str(requirements_file('tests')))
def install_qutebrowser(venv_dir: pathlib.Path) -> None:
diff --git a/tests/end2end/fixtures/webserver_sub_ssl.py b/tests/end2end/fixtures/webserver_sub_ssl.py
index 241261194..4c54d767b 100644
--- a/tests/end2end/fixtures/webserver_sub_ssl.py
+++ b/tests/end2end/fixtures/webserver_sub_ssl.py
@@ -64,7 +64,7 @@ def turn_off_logging():
def main():
end2end_dir = pathlib.Path(__file__).resolve().parents[1]
ssl_dir = end2end_dir / 'data' / 'ssl'
- context = ssl.SSLContext(ssl.PROTOCOL_TLSv1_2)
+ context = ssl.SSLContext(ssl.PROTOCOL_TLS_SERVER)
context.load_cert_chain((ssl_dir / 'cert.pem'),
(ssl_dir / 'key.pem'))
app.run(port=int(sys.argv[1]), debug=False, ssl_context=context)
diff --git a/tests/unit/config/test_config.py b/tests/unit/config/test_config.py
index dd6ef54fa..b88bc2f8d 100644
--- a/tests/unit/config/test_config.py
+++ b/tests/unit/config/test_config.py
@@ -211,6 +211,7 @@ class TestKeyConfig:
"a": "set-cmd-text no_leading_colon",
"b": "set-cmd-text -s -a :skip_cuz_append",
"c": "set-cmd-text --append :skip_cuz_append",
+ "x": "set-cmd-text",
},
{
"open": ["o"],
diff --git a/tests/unit/config/test_configfiles.py b/tests/unit/config/test_configfiles.py
index e0d64bffc..65952ddb4 100644
--- a/tests/unit/config/test_configfiles.py
+++ b/tests/unit/config/test_configfiles.py
@@ -29,7 +29,7 @@ from PyQt5.QtCore import QSettings
from qutebrowser.config import (config, configfiles, configexc, configdata,
configtypes)
-from qutebrowser.utils import utils, usertypes, urlmatch, standarddir
+from qutebrowser.utils import utils, usertypes, urlmatch, standarddir, version
from qutebrowser.keyinput import keyutils
@@ -81,6 +81,7 @@ def autoconfig(config_tmpdir):
False,
'[general]\n'
'qt_version = 5.6.7\n'
+ 'qtwe_version = 7.8.9\n'
'version = 1.2.3\n'
'\n'
'[geometry]\n'
@@ -92,6 +93,7 @@ def autoconfig(config_tmpdir):
False,
'[general]\n'
'qt_version = 5.6.7\n'
+ 'qtwe_version = 7.8.9\n'
'version = 1.2.3\n'
'\n'
'[geometry]\n'
@@ -104,6 +106,7 @@ def autoconfig(config_tmpdir):
'[general]\n'
'foobar = 42\n'
'qt_version = 5.6.7\n'
+ 'qtwe_version = 7.8.9\n'
'version = 1.2.3\n'
'\n'
'[geometry]\n'
@@ -114,6 +117,7 @@ def autoconfig(config_tmpdir):
True,
'[general]\n'
'qt_version = 5.6.7\n'
+ 'qtwe_version = 7.8.9\n'
'version = 1.2.3\n'
'newval = 23\n'
'\n'
@@ -122,10 +126,13 @@ def autoconfig(config_tmpdir):
'[inspector]\n'
'\n'),
])
-def test_state_config(fake_save_manager, data_tmpdir, monkeypatch,
- old_data, insert, new_data):
+def test_state_config(
+ fake_save_manager, data_tmpdir, monkeypatch, qtwe_version_patcher,
+ old_data, insert, new_data
+):
monkeypatch.setattr(configfiles.qutebrowser, '__version__', '1.2.3')
monkeypatch.setattr(configfiles, 'qVersion', lambda: '5.6.7')
+ qtwe_version_patcher('7.8.9')
statefile = data_tmpdir / 'state'
if old_data is not None:
@@ -157,6 +164,28 @@ def state_writer(data_tmpdir):
return _write
+@pytest.fixture
+def qtwe_version_patcher(monkeypatch):
+ try:
+ from PyQt5 import QtWebEngineWidgets # pylint: disable=unused-import
+ except ImportError:
+ pytest.skip("QtWebEngine not available")
+
+ def patch(ver):
+ monkeypatch.setattr(
+ configfiles.version,
+ 'qtwebengine_versions',
+ lambda avoid_init=False:
+ version.WebEngineVersions(
+ webengine=utils.VersionNumber.parse(ver),
+ chromium=None,
+ source='test',
+ )
+ )
+
+ return patch
+
+
@pytest.mark.parametrize('old_version, new_version, changed', [
(None, '5.12.1', False),
('5.12.1', '5.12.1', False),
@@ -176,6 +205,32 @@ def test_qt_version_changed(state_writer, monkeypatch,
assert state.qt_version_changed == changed
+@pytest.mark.parametrize('old_version, new_version, changed', [
+ (None, '5.15.1', False),
+ ('5.15.1', '5.15.1', False),
+ ('5.15.1', '5.15.2', True),
+ ('5.14.0', '5.15.2', True),
+])
+def test_qtwe_version_changed(state_writer, qtwe_version_patcher,
+ old_version, new_version, changed):
+ qtwe_version_patcher(new_version)
+
+ if old_version is not None:
+ state_writer('qtwe_version', old_version)
+
+ state = configfiles.StateConfig()
+ assert state.qtwe_version_changed == changed
+
+
+def test_qtwe_version_changed_webkit(stubs, monkeypatch, state_writer):
+ fake = stubs.ImportFake({'PyQt5.QtWebEngineWidgets': False}, monkeypatch)
+ fake.patch()
+
+ state_writer('qtwe_version', 'no')
+ state = configfiles.StateConfig()
+ assert not state.qtwe_version_changed
+
+
@pytest.mark.parametrize('old_version, new_version, expected', [
(None, '2.0.0', configfiles.VersionChange.unknown),
('1.14.1', '1.14.1', configfiles.VersionChange.equal),
diff --git a/tests/unit/scripts/test_check_coverage.py b/tests/unit/scripts/test_check_coverage.py
index d2a0b409f..efd35ce82 100644
--- a/tests/unit/scripts/test_check_coverage.py
+++ b/tests/unit/scripts/test_check_coverage.py
@@ -176,9 +176,6 @@ def test_untested_floats(covtest):
assert covtest.check() == [expected]
-@pytest.mark.skipif(
- sys.version_info[:4] == (3, 10, 0, 'alpha'),
- reason='Different results, see https://github.com/nedbat/coveragepy/issues/1106')
def test_untested_branches(covtest):
covtest.makefile("""
def func2(arg):
@@ -191,10 +188,11 @@ def test_untested_branches(covtest):
func2(True)
""")
covtest.run()
+ line_coverage = "83.33%" if sys.version_info[:2] >= (3, 10) else "100.00%"
expected = check_coverage.Message(
check_coverage.MsgType.insufficient_coverage,
'module.py',
- 'module.py has 100.00% line and 50.00% branch coverage!')
+ f'module.py has {line_coverage} line and 50.00% branch coverage!')
assert covtest.check() == [expected]
diff --git a/tests/unit/utils/test_version.py b/tests/unit/utils/test_version.py
index 734b70468..6c57cb3d3 100644
--- a/tests/unit/utils/test_version.py
+++ b/tests/unit/utils/test_version.py
@@ -917,14 +917,14 @@ class TestWebEngineVersions:
webengine=utils.VersionNumber(5, 15, 2),
chromium='87.0.4280.144',
source='UA'),
- "QtWebEngine 5.15.2, Chromium 87.0.4280.144",
+ "QtWebEngine 5.15.2, based on Chromium 87.0.4280.144",
),
(
version.WebEngineVersions(
webengine=utils.VersionNumber(5, 15, 2),
chromium='87.0.4280.144',
source='faked'),
- "QtWebEngine 5.15.2, Chromium 87.0.4280.144 (from faked)",
+ "QtWebEngine 5.15.2, based on Chromium 87.0.4280.144 (from faked)",
),
])
def test_str(self, version, expected):