Merge remote-tracking branch 'upstream/main' into feat/mac_sandbox_pre_release_pyinstaller
Only conflict was the removal of support for 32bit builds in build_release.py
This commit is contained in:
commit
0f2d34623c
|
|
@ -33,6 +33,7 @@ jobs:
|
||||||
args: "-f gcc" # For problem matchers
|
args: "-f gcc" # For problem matchers
|
||||||
- testenv: yamllint
|
- testenv: yamllint
|
||||||
- testenv: actionlint
|
- testenv: actionlint
|
||||||
|
- testenv: package
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v3
|
||||||
with:
|
with:
|
||||||
|
|
|
||||||
|
|
@ -19,47 +19,34 @@ jobs:
|
||||||
toxenv: build-release-qt5
|
toxenv: build-release-qt5
|
||||||
name: qt5-macos
|
name: qt5-macos
|
||||||
- os: windows-2019
|
- os: windows-2019
|
||||||
args: --64bit
|
|
||||||
branch: main
|
branch: main
|
||||||
toxenv: build-release-qt5
|
toxenv: build-release-qt5
|
||||||
name: qt5-windows-64bit
|
name: qt5-windows
|
||||||
- os: windows-2019
|
|
||||||
args: --32bit
|
|
||||||
branch: main
|
|
||||||
toxenv: build-release-qt5
|
|
||||||
name: qt5-windows-32bit
|
|
||||||
|
|
||||||
- os: macos-11
|
- os: macos-11
|
||||||
args: --debug
|
args: --debug
|
||||||
branch: main
|
branch: main
|
||||||
toxenv: build-release-qt5
|
toxenv: build-release-qt5
|
||||||
name: qt5-macos-debug
|
name: qt5-macos-debug
|
||||||
- os: windows-2019
|
- os: windows-2019
|
||||||
args: --64bit --debug
|
args: --debug
|
||||||
branch: main
|
branch: main
|
||||||
toxenv: build-release-qt5
|
toxenv: build-release-qt5
|
||||||
name: qt5-windows-64bit-debug
|
name: qt5-windows-debug
|
||||||
- os: windows-2019
|
|
||||||
args: --32bit --debug
|
|
||||||
branch: main
|
|
||||||
toxenv: build-release-qt5
|
|
||||||
name: qt5-windows-32bit-debug
|
|
||||||
|
|
||||||
- os: macos-11
|
- os: macos-11
|
||||||
toxenv: build-release
|
toxenv: build-release
|
||||||
name: macos
|
name: macos
|
||||||
- os: windows-2019
|
- os: windows-2019
|
||||||
args: --64bit
|
|
||||||
toxenv: build-release
|
toxenv: build-release
|
||||||
name: windows-64bit
|
name: windows
|
||||||
- os: macos-11
|
- os: macos-11
|
||||||
args: --debug
|
args: --debug
|
||||||
toxenv: build-release
|
toxenv: build-release
|
||||||
name: macos-debug
|
name: macos-debug
|
||||||
- os: windows-2019
|
- os: windows-2019
|
||||||
args: --64bit --debug
|
args: --debug
|
||||||
toxenv: build-release
|
toxenv: build-release
|
||||||
name: windows-64bit-debug
|
name: windows-debug
|
||||||
runs-on: "${{ matrix.os }}"
|
runs-on: "${{ matrix.os }}"
|
||||||
timeout-minutes: 45
|
timeout-minutes: 45
|
||||||
steps:
|
steps:
|
||||||
|
|
|
||||||
|
|
@ -220,6 +220,7 @@ Active
|
||||||
* https://nyxt.atlas.engineer/[Nyxt browser] (formerly "Next browser", Lisp, Emacs-like but also offers Vim bindings, QtWebEngine or GTK+/WebKit2 - note there was a https://jgkamat.gitlab.io/blog/next-rce.html[critical remote code execution in 2019] which was handled quite badly)
|
* https://nyxt.atlas.engineer/[Nyxt browser] (formerly "Next browser", Lisp, Emacs-like but also offers Vim bindings, QtWebEngine or GTK+/WebKit2 - note there was a https://jgkamat.gitlab.io/blog/next-rce.html[critical remote code execution in 2019] which was handled quite badly)
|
||||||
* https://vieb.dev/[Vieb] (JavaScript, Electron)
|
* https://vieb.dev/[Vieb] (JavaScript, Electron)
|
||||||
* https://surf.suckless.org/[surf] (C, GTK+ with WebKit1/WebKit2)
|
* https://surf.suckless.org/[surf] (C, GTK+ with WebKit1/WebKit2)
|
||||||
|
* https://github.com/jun7/wyeb[wyeb] (C, GTK+ with WebKit2)
|
||||||
* Chrome/Chromium addons:
|
* Chrome/Chromium addons:
|
||||||
https://vimium.github.io/[Vimium]
|
https://vimium.github.io/[Vimium]
|
||||||
* Firefox addons (based on WebExtensions):
|
* Firefox addons (based on WebExtensions):
|
||||||
|
|
@ -227,9 +228,8 @@ Active
|
||||||
https://addons.mozilla.org/en-GB/firefox/addon/vimium-ff/[Vimium-FF]
|
https://addons.mozilla.org/en-GB/firefox/addon/vimium-ff/[Vimium-FF]
|
||||||
* Addons for Firefox and Chrome:
|
* Addons for Firefox and Chrome:
|
||||||
https://github.com/brookhong/Surfingkeys[Surfingkeys],
|
https://github.com/brookhong/Surfingkeys[Surfingkeys],
|
||||||
https://lydell.github.io/LinkHints/[Link Hints] (hinting only)
|
https://lydell.github.io/LinkHints/[Link Hints] (hinting only),
|
||||||
* Addons for Safari:
|
https://github.com/ueokande/vimmatic[Vimmatic]
|
||||||
https://televator.net/vimari/[Vimari]
|
|
||||||
|
|
||||||
Inactive
|
Inactive
|
||||||
~~~~~~~~
|
~~~~~~~~
|
||||||
|
|
@ -246,7 +246,6 @@ main inspiration for qutebrowser)
|
||||||
* https://www.uzbl.org/[uzbl] (C, GTK+ with WebKit1/WebKit2)
|
* https://www.uzbl.org/[uzbl] (C, GTK+ with WebKit1/WebKit2)
|
||||||
* https://github.com/conformal/xombrero[xombrero] (C, GTK+ with WebKit1)
|
* https://github.com/conformal/xombrero[xombrero] (C, GTK+ with WebKit1)
|
||||||
* https://github.com/linkdd/cream-browser[Cream Browser] (C, GTK+ with WebKit1)
|
* https://github.com/linkdd/cream-browser[Cream Browser] (C, GTK+ with WebKit1)
|
||||||
* https://github.com/jun7/wyeb[wyeb] (C, GTK+ with WebKit2)
|
|
||||||
* Firefox addons (not based on WebExtensions or no recent activity):
|
* Firefox addons (not based on WebExtensions or no recent activity):
|
||||||
http://www.vimperator.org/[Vimperator],
|
http://www.vimperator.org/[Vimperator],
|
||||||
http://bug.5digits.org/pentadactyl/index[Pentadactyl],
|
http://bug.5digits.org/pentadactyl/index[Pentadactyl],
|
||||||
|
|
@ -263,6 +262,8 @@ main inspiration for qutebrowser)
|
||||||
https://github.com/1995eaton/chromium-vim[cVim],
|
https://github.com/1995eaton/chromium-vim[cVim],
|
||||||
https://github.com/dcchambers/vb4c[vb4c] (fork of cVim, https://github.com/dcchambers/vb4c/issues/23#issuecomment-810694017[unmaintained]),
|
https://github.com/dcchambers/vb4c[vb4c] (fork of cVim, https://github.com/dcchambers/vb4c/issues/23#issuecomment-810694017[unmaintained]),
|
||||||
https://glee.github.io/[GleeBox]
|
https://glee.github.io/[GleeBox]
|
||||||
|
* Addons for Safari:
|
||||||
|
https://televator.net/vimari/[Vimari]
|
||||||
|
|
||||||
License
|
License
|
||||||
-------
|
-------
|
||||||
|
|
|
||||||
|
|
@ -67,6 +67,8 @@ Added
|
||||||
- New `colors.webpage.darkmode.increase_text_contrast` setting for Qt 6.3+
|
- New `colors.webpage.darkmode.increase_text_contrast` setting for Qt 6.3+
|
||||||
- New `fonts.tooltip`, `colors.tooltip.bg` and `colors.tooltip.fg` settings.
|
- New `fonts.tooltip`, `colors.tooltip.bg` and `colors.tooltip.fg` settings.
|
||||||
- New `log-qt-events` debug flag for `-D`
|
- New `log-qt-events` debug flag for `-D`
|
||||||
|
- New `--all` flags for `:bookmark-del` and `:quickmark-del` to delete all
|
||||||
|
quickmarks/bookmarks.
|
||||||
|
|
||||||
Removed
|
Removed
|
||||||
~~~~~~~
|
~~~~~~~
|
||||||
|
|
@ -202,8 +204,16 @@ Fixed
|
||||||
- Crash when using QtWebKit with PAC and the file has an invalid encoding.
|
- Crash when using QtWebKit with PAC and the file has an invalid encoding.
|
||||||
- Crash with the "tiramisu" notification server.
|
- Crash with the "tiramisu" notification server.
|
||||||
- Crash when the "herbe" notification presenter doesn't start correctly.
|
- Crash when the "herbe" notification presenter doesn't start correctly.
|
||||||
|
- Crash when no notification server is installed/available.
|
||||||
|
- Warning with recent versions of the "deadd" (aka "linux notification center") notification server.
|
||||||
- Crash when using `:print --pdf` with a directory where its parent directory
|
- Crash when using `:print --pdf` with a directory where its parent directory
|
||||||
did not exist.
|
did not exist.
|
||||||
|
- The `PyQt{5,6}.sip` version is now shown correctly in the :version|--version
|
||||||
|
output. Previously that showed the version from the standalone `sip` module
|
||||||
|
which was only set for PyQt5. (#7805)
|
||||||
|
- When a `config.py` calls `.redirect()` via a request interceptor (which is
|
||||||
|
unsupported) and supplies an invalid redirect target URL, an exception is now
|
||||||
|
raised for the `.redirect()` call instead of later inside qutebrowser.
|
||||||
|
|
||||||
[[v2.5.4]]
|
[[v2.5.4]]
|
||||||
v2.5.4 (2023-03-13)
|
v2.5.4 (2023-03-13)
|
||||||
|
|
|
||||||
|
|
@ -204,7 +204,7 @@ If no url and title are provided, then save the current page as a bookmark. If a
|
||||||
|
|
||||||
[[bookmark-del]]
|
[[bookmark-del]]
|
||||||
=== bookmark-del
|
=== bookmark-del
|
||||||
Syntax: +:bookmark-del ['url']+
|
Syntax: +:bookmark-del [*--all*] ['url']+
|
||||||
|
|
||||||
Delete a bookmark.
|
Delete a bookmark.
|
||||||
|
|
||||||
|
|
@ -212,6 +212,9 @@ Delete a bookmark.
|
||||||
* +'url'+: The url of the bookmark to delete. If not given, use the current page's url.
|
* +'url'+: The url of the bookmark to delete. If not given, use the current page's url.
|
||||||
|
|
||||||
|
|
||||||
|
==== optional arguments
|
||||||
|
* +*-a*+, +*--all*+: If given, delete all bookmarks.
|
||||||
|
|
||||||
==== note
|
==== note
|
||||||
* This command does not split arguments after the last argument and handles quotes literally.
|
* This command does not split arguments after the last argument and handles quotes literally.
|
||||||
|
|
||||||
|
|
@ -998,7 +1001,7 @@ You can view all saved quickmarks on the link:qute://bookmarks[bookmarks page].
|
||||||
|
|
||||||
[[quickmark-del]]
|
[[quickmark-del]]
|
||||||
=== quickmark-del
|
=== quickmark-del
|
||||||
Syntax: +:quickmark-del ['name']+
|
Syntax: +:quickmark-del [*--all*] ['name']+
|
||||||
|
|
||||||
Delete a quickmark.
|
Delete a quickmark.
|
||||||
|
|
||||||
|
|
@ -1007,6 +1010,9 @@ Delete a quickmark.
|
||||||
if there are more than one).
|
if there are more than one).
|
||||||
|
|
||||||
|
|
||||||
|
==== optional arguments
|
||||||
|
* +*-a*+, +*--all*+: Delete all quickmarks.
|
||||||
|
|
||||||
==== note
|
==== note
|
||||||
* This command does not split arguments after the last argument and handles quotes literally.
|
* This command does not split arguments after the last argument and handles quotes literally.
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,7 @@ bump2version==1.0.1
|
||||||
certifi==2023.7.22
|
certifi==2023.7.22
|
||||||
cffi==1.15.1
|
cffi==1.15.1
|
||||||
charset-normalizer==3.2.0
|
charset-normalizer==3.2.0
|
||||||
cryptography==41.0.2
|
cryptography==41.0.3
|
||||||
docutils==0.20.1
|
docutils==0.20.1
|
||||||
github3.py==4.0.1
|
github3.py==4.0.1
|
||||||
hunter==3.6.1
|
hunter==3.6.1
|
||||||
|
|
@ -19,12 +19,12 @@ keyring==24.2.0
|
||||||
manhole==1.8.0
|
manhole==1.8.0
|
||||||
markdown-it-py==3.0.0
|
markdown-it-py==3.0.0
|
||||||
mdurl==0.1.2
|
mdurl==0.1.2
|
||||||
more-itertools==9.1.0
|
more-itertools==10.1.0
|
||||||
packaging==23.1
|
packaging==23.1
|
||||||
pkginfo==1.9.6
|
pkginfo==1.9.6
|
||||||
ply==3.11
|
ply==3.11
|
||||||
pycparser==2.21
|
pycparser==2.21
|
||||||
Pygments==2.15.1
|
Pygments==2.16.1
|
||||||
PyJWT==2.8.0
|
PyJWT==2.8.0
|
||||||
Pympler==1.0.1
|
Pympler==1.0.1
|
||||||
pyproject_hooks==1.0.0
|
pyproject_hooks==1.0.0
|
||||||
|
|
@ -34,9 +34,9 @@ readme-renderer==40.0
|
||||||
requests==2.31.0
|
requests==2.31.0
|
||||||
requests-toolbelt==1.0.0
|
requests-toolbelt==1.0.0
|
||||||
rfc3986==2.0.0
|
rfc3986==2.0.0
|
||||||
rich==13.4.2
|
rich==13.5.2
|
||||||
SecretStorage==3.3.3
|
SecretStorage==3.3.3
|
||||||
sip==6.7.10
|
sip==6.7.11
|
||||||
six==1.16.0
|
six==1.16.0
|
||||||
tomli==2.0.1
|
tomli==2.0.1
|
||||||
twine==4.0.2
|
twine==4.0.2
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
# This file is automatically generated by scripts/dev/recompile_requirements.py
|
# This file is automatically generated by scripts/dev/recompile_requirements.py
|
||||||
|
|
||||||
attrs==23.1.0
|
attrs==23.1.0
|
||||||
flake8==6.0.0
|
flake8==6.1.0
|
||||||
flake8-bugbear==23.7.10
|
flake8-bugbear==23.7.10
|
||||||
flake8-builtins==2.1.0
|
flake8-builtins==2.1.0
|
||||||
flake8-comprehensions==3.14.0
|
flake8-comprehensions==3.14.0
|
||||||
|
|
@ -16,8 +16,8 @@ flake8-tidy-imports==4.10.0
|
||||||
flake8-tuple==0.4.1
|
flake8-tuple==0.4.1
|
||||||
mccabe==0.7.0
|
mccabe==0.7.0
|
||||||
pep8-naming==0.13.3
|
pep8-naming==0.13.3
|
||||||
pycodestyle==2.10.0
|
pycodestyle==2.11.0
|
||||||
pydocstyle==6.3.0
|
pydocstyle==6.3.0
|
||||||
pyflakes==3.0.1
|
pyflakes==3.1.0
|
||||||
six==1.16.0
|
six==1.16.0
|
||||||
snowballstemmer==2.2.0
|
snowballstemmer==2.2.0
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
# This file is automatically generated by scripts/dev/recompile_requirements.py
|
# This file is automatically generated by scripts/dev/recompile_requirements.py
|
||||||
|
|
||||||
chardet==5.1.0
|
chardet==5.2.0
|
||||||
diff-cover==7.7.0
|
diff-cover==7.7.0
|
||||||
importlib-resources==6.0.0
|
importlib-resources==6.0.0
|
||||||
Jinja2==3.1.2
|
Jinja2==3.1.2
|
||||||
|
|
@ -9,12 +9,12 @@ MarkupSafe==2.1.3
|
||||||
mypy==1.4.1
|
mypy==1.4.1
|
||||||
mypy-extensions==1.0.0
|
mypy-extensions==1.0.0
|
||||||
pluggy==1.2.0
|
pluggy==1.2.0
|
||||||
Pygments==2.15.1
|
Pygments==2.16.1
|
||||||
PyQt5-stubs==5.15.6.0
|
PyQt5-stubs==5.15.6.0
|
||||||
tomli==2.0.1
|
tomli==2.0.1
|
||||||
types-colorama==0.4.15.12
|
types-colorama==0.4.15.12
|
||||||
types-docutils==0.20.0.1
|
types-docutils==0.20.0.1
|
||||||
types-Pygments==2.15.0.2
|
types-Pygments==2.16.0.0
|
||||||
types-PyYAML==6.0.12.11
|
types-PyYAML==6.0.12.11
|
||||||
types-setuptools==68.0.0.3
|
types-setuptools==68.0.0.3
|
||||||
typing_extensions==4.7.1
|
typing_extensions==4.7.1
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@ astroid==2.15.6
|
||||||
certifi==2023.7.22
|
certifi==2023.7.22
|
||||||
cffi==1.15.1
|
cffi==1.15.1
|
||||||
charset-normalizer==3.2.0
|
charset-normalizer==3.2.0
|
||||||
cryptography==41.0.2
|
cryptography==41.0.3
|
||||||
dill==0.3.7
|
dill==0.3.7
|
||||||
github3.py==4.0.1
|
github3.py==4.0.1
|
||||||
idna==3.4
|
idna==3.4
|
||||||
|
|
@ -12,16 +12,16 @@ isort==5.12.0
|
||||||
lazy-object-proxy==1.9.0
|
lazy-object-proxy==1.9.0
|
||||||
mccabe==0.7.0
|
mccabe==0.7.0
|
||||||
pefile==2023.2.7
|
pefile==2023.2.7
|
||||||
platformdirs==3.9.1
|
platformdirs==3.10.0
|
||||||
pycparser==2.21
|
pycparser==2.21
|
||||||
PyJWT==2.8.0
|
PyJWT==2.8.0
|
||||||
pylint==2.17.4
|
pylint==2.17.5
|
||||||
python-dateutil==2.8.2
|
python-dateutil==2.8.2
|
||||||
./scripts/dev/pylint_checkers
|
./scripts/dev/pylint_checkers
|
||||||
requests==2.31.0
|
requests==2.31.0
|
||||||
six==1.16.0
|
six==1.16.0
|
||||||
tomli==2.0.1
|
tomli==2.0.1
|
||||||
tomlkit==0.11.8
|
tomlkit==0.12.1
|
||||||
typing_extensions==4.7.1
|
typing_extensions==4.7.1
|
||||||
uritemplate==4.1.1
|
uritemplate==4.1.1
|
||||||
# urllib3==2.0.4
|
# urllib3==2.0.4
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,7 @@ charset-normalizer==3.2.0
|
||||||
docutils==0.20.1
|
docutils==0.20.1
|
||||||
idna==3.4
|
idna==3.4
|
||||||
packaging==23.1
|
packaging==23.1
|
||||||
Pygments==2.15.1
|
Pygments==2.16.1
|
||||||
pyproject_hooks==1.0.0
|
pyproject_hooks==1.0.0
|
||||||
pyroma==4.2
|
pyroma==4.2
|
||||||
requests==2.31.0
|
requests==2.31.0
|
||||||
|
|
|
||||||
|
|
@ -11,11 +11,11 @@ importlib-metadata==6.8.0
|
||||||
Jinja2==3.1.2
|
Jinja2==3.1.2
|
||||||
MarkupSafe==2.1.3
|
MarkupSafe==2.1.3
|
||||||
packaging==23.1
|
packaging==23.1
|
||||||
Pygments==2.15.1
|
Pygments==2.16.1
|
||||||
pytz==2023.3
|
pytz==2023.3
|
||||||
requests==2.31.0
|
requests==2.31.0
|
||||||
snowballstemmer==2.2.0
|
snowballstemmer==2.2.0
|
||||||
Sphinx==7.0.1
|
Sphinx==7.1.2
|
||||||
sphinxcontrib-applehelp==1.0.4
|
sphinxcontrib-applehelp==1.0.4
|
||||||
sphinxcontrib-devhelp==1.0.2
|
sphinxcontrib-devhelp==1.0.2
|
||||||
sphinxcontrib-htmlhelp==2.0.1
|
sphinxcontrib-htmlhelp==2.0.1
|
||||||
|
|
|
||||||
|
|
@ -13,23 +13,23 @@ execnet==2.0.2
|
||||||
filelock==3.12.2
|
filelock==3.12.2
|
||||||
Flask==2.3.2
|
Flask==2.3.2
|
||||||
hunter==3.6.1
|
hunter==3.6.1
|
||||||
hypothesis==6.82.0
|
hypothesis==6.82.2
|
||||||
idna==3.4
|
idna==3.4
|
||||||
importlib-metadata==6.8.0
|
importlib-metadata==6.8.0
|
||||||
iniconfig==2.0.0
|
iniconfig==2.0.0
|
||||||
itsdangerous==2.1.2
|
itsdangerous==2.1.2
|
||||||
jaraco.functools==3.8.0
|
jaraco.functools==3.8.1
|
||||||
# Jinja2==3.1.2
|
# Jinja2==3.1.2
|
||||||
Mako==1.2.4
|
Mako==1.2.4
|
||||||
manhole==1.8.0
|
manhole==1.8.0
|
||||||
# MarkupSafe==2.1.3
|
# MarkupSafe==2.1.3
|
||||||
more-itertools==9.1.0
|
more-itertools==10.1.0
|
||||||
packaging==23.1
|
packaging==23.1
|
||||||
parse==1.19.1
|
parse==1.19.1
|
||||||
parse-type==0.6.2
|
parse-type==0.6.2
|
||||||
pluggy==1.2.0
|
pluggy==1.2.0
|
||||||
py-cpuinfo==9.0.0
|
py-cpuinfo==9.0.0
|
||||||
Pygments==2.15.1
|
Pygments==2.16.1
|
||||||
pytest==7.4.0
|
pytest==7.4.0
|
||||||
pytest-bdd==6.1.1
|
pytest-bdd==6.1.1
|
||||||
pytest-benchmark==4.0.0
|
pytest-benchmark==4.0.0
|
||||||
|
|
|
||||||
|
|
@ -1,17 +1,17 @@
|
||||||
# This file is automatically generated by scripts/dev/recompile_requirements.py
|
# This file is automatically generated by scripts/dev/recompile_requirements.py
|
||||||
|
|
||||||
cachetools==5.3.1
|
cachetools==5.3.1
|
||||||
chardet==5.1.0
|
chardet==5.2.0
|
||||||
colorama==0.4.6
|
colorama==0.4.6
|
||||||
distlib==0.3.7
|
distlib==0.3.7
|
||||||
filelock==3.12.2
|
filelock==3.12.2
|
||||||
packaging==23.1
|
packaging==23.1
|
||||||
pip==23.2.1
|
pip==23.2.1
|
||||||
platformdirs==3.9.1
|
platformdirs==3.10.0
|
||||||
pluggy==1.2.0
|
pluggy==1.2.0
|
||||||
pyproject-api==1.5.3
|
pyproject-api==1.5.3
|
||||||
setuptools==68.0.0
|
setuptools==68.0.0
|
||||||
tomli==2.0.1
|
tomli==2.0.1
|
||||||
tox==4.6.4
|
tox==4.6.4
|
||||||
virtualenv==20.24.1
|
virtualenv==20.24.2
|
||||||
wheel==0.41.0
|
wheel==0.41.1
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
# This file is automatically generated by scripts/dev/recompile_requirements.py
|
# This file is automatically generated by scripts/dev/recompile_requirements.py
|
||||||
|
|
||||||
pathspec==0.11.1
|
pathspec==0.11.2
|
||||||
PyYAML==6.0.1
|
PyYAML==6.0.1
|
||||||
yamllint==1.32.0
|
yamllint==1.32.0
|
||||||
|
|
|
||||||
|
|
@ -35,7 +35,7 @@ Possible values:
|
||||||
|
|
||||||
|
|
||||||
import inspect
|
import inspect
|
||||||
from typing import Any, Callable, Iterable
|
from typing import Any, Callable, Iterable, Protocol, Optional, Dict, cast
|
||||||
|
|
||||||
from qutebrowser.utils import qtutils
|
from qutebrowser.utils import qtutils
|
||||||
from qutebrowser.commands import command, cmdexc
|
from qutebrowser.commands import command, cmdexc
|
||||||
|
|
@ -90,7 +90,21 @@ def check_exclusive(flags: Iterable[bool], names: Iterable[str]) -> None:
|
||||||
raise CommandError("Only one of {} can be given!".format(argstr))
|
raise CommandError("Only one of {} can be given!".format(argstr))
|
||||||
|
|
||||||
|
|
||||||
_CmdHandlerType = Callable[..., Any]
|
_CmdHandlerFunc = Callable[..., Any]
|
||||||
|
|
||||||
|
|
||||||
|
class _CmdHandlerType(Protocol):
|
||||||
|
|
||||||
|
"""A qutebrowser command function, which had qute_args patched on it.
|
||||||
|
|
||||||
|
Applying @cmdutils.argument to a function will patch it with a qute_args attribute.
|
||||||
|
Below, we cast the decorated function to _CmdHandlerType to make mypy aware of this.
|
||||||
|
"""
|
||||||
|
|
||||||
|
qute_args: Optional[Dict[str, command.ArgInfo]]
|
||||||
|
|
||||||
|
def __call__(self, *args: Any, **kwargs: Any) -> Any:
|
||||||
|
...
|
||||||
|
|
||||||
|
|
||||||
class register: # noqa: N801,N806 pylint: disable=invalid-name
|
class register: # noqa: N801,N806 pylint: disable=invalid-name
|
||||||
|
|
@ -118,7 +132,7 @@ class register: # noqa: N801,N806 pylint: disable=invalid-name
|
||||||
# The arguments to pass to Command.
|
# The arguments to pass to Command.
|
||||||
self._kwargs = kwargs
|
self._kwargs = kwargs
|
||||||
|
|
||||||
def __call__(self, func: _CmdHandlerType) -> _CmdHandlerType:
|
def __call__(self, func: _CmdHandlerFunc) -> _CmdHandlerType:
|
||||||
"""Register the command before running the function.
|
"""Register the command before running the function.
|
||||||
|
|
||||||
Gets called when a function should be decorated.
|
Gets called when a function should be decorated.
|
||||||
|
|
@ -158,7 +172,8 @@ class register: # noqa: N801,N806 pylint: disable=invalid-name
|
||||||
|
|
||||||
# This is checked by future @cmdutils.argument calls so they fail
|
# This is checked by future @cmdutils.argument calls so they fail
|
||||||
# (as they'd be silently ignored otherwise)
|
# (as they'd be silently ignored otherwise)
|
||||||
func.qute_args = None # type: ignore[attr-defined]
|
func = cast(_CmdHandlerType, func)
|
||||||
|
func.qute_args = None
|
||||||
|
|
||||||
return func
|
return func
|
||||||
|
|
||||||
|
|
@ -210,19 +225,21 @@ class argument: # noqa: N801,N806 pylint: disable=invalid-name
|
||||||
self._argname = argname # The name of the argument to handle.
|
self._argname = argname # The name of the argument to handle.
|
||||||
self._kwargs = kwargs # Valid ArgInfo members.
|
self._kwargs = kwargs # Valid ArgInfo members.
|
||||||
|
|
||||||
def __call__(self, func: _CmdHandlerType) -> _CmdHandlerType:
|
def __call__(self, func: _CmdHandlerFunc) -> _CmdHandlerType:
|
||||||
funcname = func.__name__
|
funcname = func.__name__
|
||||||
|
|
||||||
if self._argname not in inspect.signature(func).parameters:
|
if self._argname not in inspect.signature(func).parameters:
|
||||||
raise ValueError("{} has no argument {}!".format(funcname,
|
raise ValueError("{} has no argument {}!".format(funcname,
|
||||||
self._argname))
|
self._argname))
|
||||||
|
|
||||||
|
func = cast(_CmdHandlerType, func)
|
||||||
if not hasattr(func, 'qute_args'):
|
if not hasattr(func, 'qute_args'):
|
||||||
func.qute_args = {} # type: ignore[attr-defined]
|
func.qute_args = {}
|
||||||
elif func.qute_args is None:
|
elif func.qute_args is None:
|
||||||
raise ValueError("@cmdutils.argument got called above (after) "
|
raise ValueError("@cmdutils.argument got called above (after) "
|
||||||
"@cmdutils.register for {}!".format(funcname))
|
"@cmdutils.register for {}!".format(funcname))
|
||||||
|
|
||||||
arginfo = command.ArgInfo(**self._kwargs)
|
arginfo = command.ArgInfo(**self._kwargs)
|
||||||
func.qute_args[self._argname] = arginfo # type: ignore[attr-defined]
|
func.qute_args[self._argname] = arginfo
|
||||||
|
|
||||||
return func
|
return func
|
||||||
|
|
|
||||||
|
|
@ -30,7 +30,7 @@ if TYPE_CHECKING:
|
||||||
from qutebrowser.keyinput import modeman
|
from qutebrowser.keyinput import modeman
|
||||||
from qutebrowser.config import config, websettings
|
from qutebrowser.config import config, websettings
|
||||||
from qutebrowser.utils import (utils, objreg, usertypes, log, qtutils,
|
from qutebrowser.utils import (utils, objreg, usertypes, log, qtutils,
|
||||||
urlutils, message, jinja)
|
urlutils, message, jinja, version)
|
||||||
from qutebrowser.misc import miscwidgets, objects, sessions
|
from qutebrowser.misc import miscwidgets, objects, sessions
|
||||||
from qutebrowser.browser import eventfilter, inspector
|
from qutebrowser.browser import eventfilter, inspector
|
||||||
from qutebrowser.qt import sip
|
from qutebrowser.qt import sip
|
||||||
|
|
@ -1169,6 +1169,23 @@ class AbstractTab(QWidget):
|
||||||
navigation.url.errorString()))
|
navigation.url.errorString()))
|
||||||
navigation.accepted = False
|
navigation.accepted = False
|
||||||
|
|
||||||
|
# WORKAROUND for QtWebEngine >= 6.3 not allowing form requests from
|
||||||
|
# qute:// to outside domains.
|
||||||
|
if (
|
||||||
|
self.url() == QUrl("qute://start/") and
|
||||||
|
navigation.navigation_type == navigation.Type.form_submitted and
|
||||||
|
navigation.url.matches(
|
||||||
|
QUrl(config.val.url.searchengines['DEFAULT']),
|
||||||
|
urlutils.FormatOption.REMOVE_QUERY) and
|
||||||
|
objects.backend == usertypes.Backend.QtWebEngine and
|
||||||
|
version.qtwebengine_versions().webengine >= utils.VersionNumber(6, 3)
|
||||||
|
):
|
||||||
|
log.webview.debug(
|
||||||
|
"Working around qute://start loading issue for "
|
||||||
|
f"{navigation.url.toDisplayString()}")
|
||||||
|
navigation.accepted = False
|
||||||
|
self.load_url(navigation.url)
|
||||||
|
|
||||||
@pyqtSlot(bool)
|
@pyqtSlot(bool)
|
||||||
def _on_load_finished(self, ok: bool) -> None:
|
def _on_load_finished(self, ok: bool) -> None:
|
||||||
assert self._widget is not None
|
assert self._widget is not None
|
||||||
|
|
|
||||||
|
|
@ -1235,21 +1235,31 @@ class CommandDispatcher:
|
||||||
@cmdutils.register(instance='command-dispatcher', scope='window',
|
@cmdutils.register(instance='command-dispatcher', scope='window',
|
||||||
maxsplit=0)
|
maxsplit=0)
|
||||||
@cmdutils.argument('name', completion=miscmodels.quickmark)
|
@cmdutils.argument('name', completion=miscmodels.quickmark)
|
||||||
def quickmark_del(self, name=None):
|
def quickmark_del(self, name=None, all_=False):
|
||||||
"""Delete a quickmark.
|
"""Delete a quickmark.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
name: The name of the quickmark to delete. If not given, delete the
|
name: The name of the quickmark to delete. If not given, delete the
|
||||||
quickmark for the current page (choosing one arbitrarily
|
quickmark for the current page (choosing one arbitrarily
|
||||||
if there are more than one).
|
if there are more than one).
|
||||||
|
all_: Delete all quickmarks.
|
||||||
"""
|
"""
|
||||||
quickmark_manager = objreg.get('quickmark-manager')
|
quickmark_manager = objreg.get('quickmark-manager')
|
||||||
|
|
||||||
|
if all_:
|
||||||
|
if name is not None:
|
||||||
|
raise cmdutils.CommandError("Cannot specify name and --all")
|
||||||
|
quickmark_manager.clear()
|
||||||
|
message.info("Quickmarks cleared.")
|
||||||
|
return
|
||||||
|
|
||||||
if name is None:
|
if name is None:
|
||||||
url = self._current_url()
|
url = self._current_url()
|
||||||
try:
|
try:
|
||||||
name = quickmark_manager.get_by_qurl(url)
|
name = quickmark_manager.get_by_qurl(url)
|
||||||
except urlmarks.DoesNotExistError as e:
|
except urlmarks.DoesNotExistError as e:
|
||||||
raise cmdutils.CommandError(str(e))
|
raise cmdutils.CommandError(str(e))
|
||||||
|
|
||||||
try:
|
try:
|
||||||
quickmark_manager.delete(name)
|
quickmark_manager.delete(name)
|
||||||
except KeyError:
|
except KeyError:
|
||||||
|
|
@ -1320,18 +1330,28 @@ class CommandDispatcher:
|
||||||
@cmdutils.register(instance='command-dispatcher', scope='window',
|
@cmdutils.register(instance='command-dispatcher', scope='window',
|
||||||
maxsplit=0)
|
maxsplit=0)
|
||||||
@cmdutils.argument('url', completion=miscmodels.bookmark)
|
@cmdutils.argument('url', completion=miscmodels.bookmark)
|
||||||
def bookmark_del(self, url=None):
|
def bookmark_del(self, url=None, all_=False):
|
||||||
"""Delete a bookmark.
|
"""Delete a bookmark.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
url: The url of the bookmark to delete. If not given, use the
|
url: The url of the bookmark to delete. If not given, use the
|
||||||
current page's url.
|
current page's url.
|
||||||
|
all_: If given, delete all bookmarks.
|
||||||
"""
|
"""
|
||||||
|
bookmark_manager = objreg.get('bookmark-manager')
|
||||||
|
if all_:
|
||||||
|
if url is not None:
|
||||||
|
raise cmdutils.CommandError("Cannot specify url and --all")
|
||||||
|
bookmark_manager.clear()
|
||||||
|
message.info("Bookmarks cleared.")
|
||||||
|
return
|
||||||
|
|
||||||
if url is None:
|
if url is None:
|
||||||
url = self._current_url().toString(QUrl.UrlFormattingOption.RemovePassword |
|
url = self._current_url().toString(QUrl.UrlFormattingOption.RemovePassword |
|
||||||
QUrl.ComponentFormattingOption.FullyEncoded)
|
QUrl.ComponentFormattingOption.FullyEncoded)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
objreg.get('bookmark-manager').delete(url)
|
bookmark_manager.delete(url)
|
||||||
except KeyError:
|
except KeyError:
|
||||||
raise cmdutils.CommandError("Bookmark '{}' not found!".format(url))
|
raise cmdutils.CommandError("Bookmark '{}' not found!".format(url))
|
||||||
message.info("Removed bookmark {}".format(url))
|
message.info("Removed bookmark {}".format(url))
|
||||||
|
|
|
||||||
|
|
@ -98,6 +98,11 @@ class UrlMarkManager(QObject):
|
||||||
del self.marks[key]
|
del self.marks[key]
|
||||||
self.changed.emit()
|
self.changed.emit()
|
||||||
|
|
||||||
|
def clear(self):
|
||||||
|
"""Delete all marks."""
|
||||||
|
self.marks.clear()
|
||||||
|
self.changed.emit()
|
||||||
|
|
||||||
|
|
||||||
class QuickmarkManager(UrlMarkManager):
|
class QuickmarkManager(UrlMarkManager):
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,7 @@ from qutebrowser.qt.webenginecore import (QWebEngineUrlRequestInterceptor,
|
||||||
|
|
||||||
from qutebrowser.config import websettings, config
|
from qutebrowser.config import websettings, config
|
||||||
from qutebrowser.browser import shared
|
from qutebrowser.browser import shared
|
||||||
from qutebrowser.utils import debug, log
|
from qutebrowser.utils import debug, log, qtutils
|
||||||
from qutebrowser.extensions import interceptors
|
from qutebrowser.extensions import interceptors
|
||||||
from qutebrowser.misc import objects
|
from qutebrowser.misc import objects
|
||||||
|
|
||||||
|
|
@ -35,6 +35,11 @@ class WebEngineRequest(interceptors.Request):
|
||||||
if self._webengine_info is None:
|
if self._webengine_info is None:
|
||||||
raise interceptors.RedirectException("Request improperly initialized.")
|
raise interceptors.RedirectException("Request improperly initialized.")
|
||||||
|
|
||||||
|
try:
|
||||||
|
qtutils.ensure_valid(url)
|
||||||
|
except qtutils.QtValueError as e:
|
||||||
|
raise interceptors.RedirectException(f"Redirect to invalid URL: {e}")
|
||||||
|
|
||||||
# Redirecting a request that contains payload data is not allowed.
|
# Redirecting a request that contains payload data is not allowed.
|
||||||
# To be safe, abort on any request not in a whitelist.
|
# To be safe, abort on any request not in a whitelist.
|
||||||
verb = self._webengine_info.requestMethod()
|
verb = self._webengine_info.requestMethod()
|
||||||
|
|
|
||||||
|
|
@ -113,6 +113,9 @@ class DBusError(Error):
|
||||||
# https://crashes.qutebrowser.org/view/de62220a
|
# https://crashes.qutebrowser.org/view/de62220a
|
||||||
# after "Notification daemon did quit!"
|
# after "Notification daemon did quit!"
|
||||||
"org.freedesktop.DBus.Error.UnknownObject",
|
"org.freedesktop.DBus.Error.UnknownObject",
|
||||||
|
|
||||||
|
# notmuch-sha1-ef7b6e9e79e5f2f6cba90224122288895c1fe0d8
|
||||||
|
"org.freedesktop.DBus.Error.ServiceUnknown",
|
||||||
}
|
}
|
||||||
|
|
||||||
def __init__(self, msg: QDBusMessage) -> None:
|
def __init__(self, msg: QDBusMessage) -> None:
|
||||||
|
|
@ -856,12 +859,15 @@ class DBusNotificationAdapter(AbstractNotificationAdapter):
|
||||||
log.misc.debug(f"Enabling quirks {quirks}")
|
log.misc.debug(f"Enabling quirks {quirks}")
|
||||||
self._quirks = quirks
|
self._quirks = quirks
|
||||||
|
|
||||||
expected_spec_version = self._quirks.spec_version or self.SPEC_VERSION
|
expected_spec_versions = [self.SPEC_VERSION]
|
||||||
if spec_version != expected_spec_version:
|
if self._quirks.spec_version is not None:
|
||||||
|
expected_spec_versions.append(self._quirks.spec_version)
|
||||||
|
|
||||||
|
if spec_version not in expected_spec_versions:
|
||||||
log.misc.warning(
|
log.misc.warning(
|
||||||
f"Notification server ({name} {ver} by {vendor}) implements "
|
f"Notification server ({name} {ver} by {vendor}) implements "
|
||||||
f"spec {spec_version}, but {expected_spec_version} was expected. "
|
f"spec {spec_version}, but {'/'.join(expected_spec_versions)} was "
|
||||||
f"If {name} is up to date, please report a qutebrowser bug.")
|
f"expected. If {name} is up to date, please report a qutebrowser bug.")
|
||||||
|
|
||||||
# https://specifications.freedesktop.org/notification-spec/latest/ar01s08.html
|
# https://specifications.freedesktop.org/notification-spec/latest/ar01s08.html
|
||||||
icon_key_overrides = {
|
icon_key_overrides = {
|
||||||
|
|
|
||||||
|
|
@ -11,7 +11,7 @@ from qutebrowser.qt.core import QRect, QEventLoop
|
||||||
from qutebrowser.qt.widgets import QApplication
|
from qutebrowser.qt.widgets import QApplication
|
||||||
from qutebrowser.qt.webenginecore import QWebEngineSettings
|
from qutebrowser.qt.webenginecore import QWebEngineSettings
|
||||||
|
|
||||||
from qutebrowser.utils import log, javascript, urlutils, usertypes, utils
|
from qutebrowser.utils import log, javascript, urlutils, usertypes, utils, version
|
||||||
from qutebrowser.browser import webelem
|
from qutebrowser.browser import webelem
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
|
|
@ -213,6 +213,17 @@ class WebEngineElement(webelem.AbstractWebElement):
|
||||||
return True
|
return True
|
||||||
if baseurl.scheme() == url.scheme(): # e.g. a qute:// link
|
if baseurl.scheme() == url.scheme(): # e.g. a qute:// link
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
# Qt 6.3+ needs a user interaction to allow navigations from qute:// to
|
||||||
|
# outside qute:// (like e.g. on qute://bookmarks).
|
||||||
|
versions = version.qtwebengine_versions()
|
||||||
|
if (
|
||||||
|
baseurl.scheme() == "qute" and
|
||||||
|
url.scheme() != "qute" and
|
||||||
|
versions.webengine >= utils.VersionNumber(6, 3)
|
||||||
|
):
|
||||||
|
return True
|
||||||
|
|
||||||
return url.scheme() not in urlutils.WEBENGINE_SCHEMES
|
return url.scheme() not in urlutils.WEBENGINE_SCHEMES
|
||||||
|
|
||||||
def _click_editable(self, click_target: usertypes.ClickTarget) -> None:
|
def _click_editable(self, click_target: usertypes.ClickTarget) -> None:
|
||||||
|
|
|
||||||
|
|
@ -316,6 +316,8 @@ class TabbedBrowser(QWidget):
|
||||||
fields['id'] = self._win_id
|
fields['id'] = self._win_id
|
||||||
|
|
||||||
title = title_format.format(**fields)
|
title = title_format.format(**fields)
|
||||||
|
# prevent hanging WMs and similar issues with giant URLs
|
||||||
|
title = utils.elide(title, 1024)
|
||||||
|
|
||||||
self._window().setWindowTitle(title)
|
self._window().setWindowTitle(title)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -39,6 +39,7 @@ if machinery.IS_QT6:
|
||||||
|
|
||||||
REMOVE_SCHEME = QUrl.UrlFormattingOption.RemoveScheme
|
REMOVE_SCHEME = QUrl.UrlFormattingOption.RemoveScheme
|
||||||
REMOVE_PASSWORD = QUrl.UrlFormattingOption.RemovePassword
|
REMOVE_PASSWORD = QUrl.UrlFormattingOption.RemovePassword
|
||||||
|
REMOVE_QUERY = QUrl.UrlFormattingOption.RemoveQuery
|
||||||
else:
|
else:
|
||||||
UrlFlagsType = Union[
|
UrlFlagsType = Union[
|
||||||
QUrl.FormattingOptions,
|
QUrl.FormattingOptions,
|
||||||
|
|
@ -74,6 +75,8 @@ else:
|
||||||
_QtFormattingOptions, QUrl.UrlFormattingOption.RemoveScheme)
|
_QtFormattingOptions, QUrl.UrlFormattingOption.RemoveScheme)
|
||||||
REMOVE_PASSWORD = cast(
|
REMOVE_PASSWORD = cast(
|
||||||
_QtFormattingOptions, QUrl.UrlFormattingOption.RemovePassword)
|
_QtFormattingOptions, QUrl.UrlFormattingOption.RemovePassword)
|
||||||
|
REMOVE_QUERY = cast(
|
||||||
|
_QtFormattingOptions, QUrl.UrlFormattingOption.RemoveQuery)
|
||||||
|
|
||||||
|
|
||||||
# URL schemes supported by QtWebEngine
|
# URL schemes supported by QtWebEngine
|
||||||
|
|
|
||||||
|
|
@ -381,7 +381,6 @@ class ModuleInfo:
|
||||||
|
|
||||||
def _create_module_info() -> Dict[str, ModuleInfo]:
|
def _create_module_info() -> Dict[str, ModuleInfo]:
|
||||||
packages = [
|
packages = [
|
||||||
('sip', ['SIP_VERSION_STR']),
|
|
||||||
('colorama', ['VERSION', '__version__']),
|
('colorama', ['VERSION', '__version__']),
|
||||||
('jinja2', ['__version__']),
|
('jinja2', ['__version__']),
|
||||||
('pygments', ['__version__']),
|
('pygments', ['__version__']),
|
||||||
|
|
@ -395,9 +394,13 @@ def _create_module_info() -> Dict[str, ModuleInfo]:
|
||||||
('PyQt5.QtWebEngineWidgets', []),
|
('PyQt5.QtWebEngineWidgets', []),
|
||||||
('PyQt5.QtWebEngine', ['PYQT_WEBENGINE_VERSION_STR']),
|
('PyQt5.QtWebEngine', ['PYQT_WEBENGINE_VERSION_STR']),
|
||||||
('PyQt5.QtWebKitWidgets', []),
|
('PyQt5.QtWebKitWidgets', []),
|
||||||
|
('PyQt5.sip', ['SIP_VERSION_STR']),
|
||||||
]
|
]
|
||||||
elif machinery.IS_QT6:
|
elif machinery.IS_QT6:
|
||||||
packages.append(('PyQt6.QtWebEngineCore', ['PYQT_WEBENGINE_VERSION_STR']))
|
packages += [
|
||||||
|
('PyQt6.QtWebEngineCore', ['PYQT_WEBENGINE_VERSION_STR']),
|
||||||
|
('PyQt6.sip', ['SIP_VERSION_STR']),
|
||||||
|
]
|
||||||
else:
|
else:
|
||||||
raise utils.Unreachable()
|
raise utils.Unreachable()
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,7 @@ colorama==0.4.6
|
||||||
importlib-resources==6.0.0 ; python_version=="3.8.*"
|
importlib-resources==6.0.0 ; python_version=="3.8.*"
|
||||||
Jinja2==3.1.2
|
Jinja2==3.1.2
|
||||||
MarkupSafe==2.1.3
|
MarkupSafe==2.1.3
|
||||||
Pygments==2.15.1
|
Pygments==2.16.1
|
||||||
PyYAML==6.0.1
|
PyYAML==6.0.1
|
||||||
zipp==3.16.2
|
zipp==3.16.2
|
||||||
# Unpinned due to recompile_requirements.py limitations
|
# Unpinned due to recompile_requirements.py limitations
|
||||||
|
|
|
||||||
|
|
@ -282,7 +282,7 @@ def build_mac(
|
||||||
gh_token=gh_token)
|
gh_token=gh_token)
|
||||||
|
|
||||||
utils.print_title("Building .app via pyinstaller")
|
utils.print_title("Building .app via pyinstaller")
|
||||||
call_tox(f'pyinstaller-64bit{"-qt5" if qt5 else ""}', '-r', debug=debug)
|
call_tox(f'pyinstaller{"-qt5" if qt5 else ""}', '-r', debug=debug)
|
||||||
utils.print_title("Verifying .app")
|
utils.print_title("Verifying .app")
|
||||||
verify_mac_app()
|
verify_mac_app()
|
||||||
|
|
||||||
|
|
@ -328,18 +328,14 @@ def build_mac(
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
def _get_windows_python_path(x64: bool) -> pathlib.Path:
|
def _get_windows_python_path() -> pathlib.Path:
|
||||||
"""Get the path to Python.exe on Windows."""
|
"""Get the path to Python.exe on Windows."""
|
||||||
parts = str(sys.version_info.major), str(sys.version_info.minor)
|
parts = str(sys.version_info.major), str(sys.version_info.minor)
|
||||||
ver = ''.join(parts)
|
ver = ''.join(parts)
|
||||||
dot_ver = '.'.join(parts)
|
dot_ver = '.'.join(parts)
|
||||||
|
|
||||||
if x64:
|
path = rf'SOFTWARE\Python\PythonCore\{dot_ver}\InstallPath'
|
||||||
path = rf'SOFTWARE\Python\PythonCore\{dot_ver}\InstallPath'
|
fallback = pathlib.Path('C:', f'Python{ver}', 'python.exe')
|
||||||
fallback = pathlib.Path('C:', f'Python{ver}', 'python.exe')
|
|
||||||
else:
|
|
||||||
path = rf'SOFTWARE\WOW6432Node\Python\PythonCore\{dot_ver}-32\InstallPath'
|
|
||||||
fallback = pathlib.Path('C:', f'Python{ver}-32', 'python.exe')
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
key = winreg.OpenKeyEx(winreg.HKEY_LOCAL_MACHINE, path)
|
key = winreg.OpenKeyEx(winreg.HKEY_LOCAL_MACHINE, path)
|
||||||
|
|
@ -349,47 +345,39 @@ def _get_windows_python_path(x64: bool) -> pathlib.Path:
|
||||||
|
|
||||||
|
|
||||||
def _build_windows_single(
|
def _build_windows_single(
|
||||||
*, x64: bool,
|
*,
|
||||||
qt5: bool,
|
qt5: bool,
|
||||||
skip_packaging: bool,
|
skip_packaging: bool,
|
||||||
debug: bool,
|
debug: bool,
|
||||||
) -> List[Artifact]:
|
) -> List[Artifact]:
|
||||||
"""Build on Windows for a single architecture."""
|
"""Build on Windows for a single build type."""
|
||||||
human_arch = '64-bit' if x64 else '32-bit'
|
utils.print_title("Running pyinstaller")
|
||||||
utils.print_title(f"Running pyinstaller {human_arch}")
|
|
||||||
dist_path = pathlib.Path("dist")
|
dist_path = pathlib.Path("dist")
|
||||||
|
|
||||||
arch = "x64" if x64 else "x86"
|
out_path = dist_path / f'qutebrowser-{qutebrowser.__version__}'
|
||||||
out_path = dist_path / f'qutebrowser-{qutebrowser.__version__}-{arch}'
|
|
||||||
_maybe_remove(out_path)
|
_maybe_remove(out_path)
|
||||||
|
|
||||||
python = _get_windows_python_path(x64=x64)
|
python = _get_windows_python_path()
|
||||||
suffix = "64bit" if x64 else "32bit"
|
# FIXME:qt6 does this regress 391623d5ec983ecfc4512c7305c4b7a293ac3872?
|
||||||
if qt5:
|
suffix = "-qt5" if qt5 else ""
|
||||||
# FIXME:qt6 does this regress 391623d5ec983ecfc4512c7305c4b7a293ac3872?
|
call_tox(f'pyinstaller{suffix}', '-r', python=python, debug=debug)
|
||||||
suffix += "-qt5"
|
|
||||||
call_tox(f'pyinstaller-{suffix}', '-r', python=python, debug=debug)
|
|
||||||
|
|
||||||
out_pyinstaller = dist_path / "qutebrowser"
|
out_pyinstaller = dist_path / "qutebrowser"
|
||||||
shutil.move(out_pyinstaller, out_path)
|
shutil.move(out_pyinstaller, out_path)
|
||||||
exe_path = out_path / 'qutebrowser.exe'
|
exe_path = out_path / 'qutebrowser.exe'
|
||||||
|
|
||||||
utils.print_title(f"Verifying {human_arch} exe")
|
utils.print_title("Verifying exe")
|
||||||
verify_windows_exe(exe_path)
|
verify_windows_exe(exe_path)
|
||||||
|
|
||||||
utils.print_title(f"Running {human_arch} smoke test")
|
utils.print_title("Running smoke test")
|
||||||
smoke_test(exe_path, debug=debug, qt5=qt5)
|
smoke_test(exe_path, debug=debug, qt5=qt5)
|
||||||
|
|
||||||
if skip_packaging:
|
if skip_packaging:
|
||||||
return []
|
return []
|
||||||
|
|
||||||
utils.print_title(f"Packaging {human_arch}")
|
utils.print_title("Packaging")
|
||||||
return _package_windows_single(
|
return _package_windows_single(
|
||||||
nsis_flags=[] if x64 else ['/DX86'],
|
|
||||||
out_path=out_path,
|
out_path=out_path,
|
||||||
filename_arch='amd64' if x64 else 'win32',
|
|
||||||
desc_arch=human_arch,
|
|
||||||
desc_suffix='' if x64 else ' (only for 32-bit Windows!)',
|
|
||||||
debug=debug,
|
debug=debug,
|
||||||
qt5=qt5,
|
qt5=qt5,
|
||||||
)
|
)
|
||||||
|
|
@ -398,8 +386,6 @@ def _build_windows_single(
|
||||||
def build_windows(
|
def build_windows(
|
||||||
*, gh_token: str,
|
*, gh_token: str,
|
||||||
skip_packaging: bool,
|
skip_packaging: bool,
|
||||||
only_32bit: bool,
|
|
||||||
only_64bit: bool,
|
|
||||||
qt5: bool,
|
qt5: bool,
|
||||||
debug: bool,
|
debug: bool,
|
||||||
) -> List[Artifact]:
|
) -> List[Artifact]:
|
||||||
|
|
@ -410,37 +396,23 @@ def build_windows(
|
||||||
|
|
||||||
utils.print_title("Building Windows binaries")
|
utils.print_title("Building Windows binaries")
|
||||||
|
|
||||||
artifacts = []
|
|
||||||
|
|
||||||
from scripts.dev import gen_versioninfo
|
from scripts.dev import gen_versioninfo
|
||||||
utils.print_title("Updating VersionInfo file")
|
utils.print_title("Updating VersionInfo file")
|
||||||
gen_versioninfo.main()
|
gen_versioninfo.main()
|
||||||
|
|
||||||
if not only_32bit:
|
artifacts = [
|
||||||
artifacts += _build_windows_single(
|
_build_windows_single(
|
||||||
x64=True,
|
|
||||||
skip_packaging=skip_packaging,
|
skip_packaging=skip_packaging,
|
||||||
debug=debug,
|
debug=debug,
|
||||||
qt5=qt5,
|
qt5=qt5,
|
||||||
)
|
),
|
||||||
if not only_64bit and qt5:
|
]
|
||||||
artifacts += _build_windows_single(
|
|
||||||
x64=False,
|
|
||||||
skip_packaging=skip_packaging,
|
|
||||||
debug=debug,
|
|
||||||
qt5=qt5,
|
|
||||||
)
|
|
||||||
|
|
||||||
return artifacts
|
return artifacts
|
||||||
|
|
||||||
|
|
||||||
def _package_windows_single(
|
def _package_windows_single(
|
||||||
*,
|
*,
|
||||||
nsis_flags: List[str],
|
|
||||||
out_path: pathlib.Path,
|
out_path: pathlib.Path,
|
||||||
desc_arch: str,
|
|
||||||
desc_suffix: str,
|
|
||||||
filename_arch: str,
|
|
||||||
debug: bool,
|
debug: bool,
|
||||||
qt5: bool,
|
qt5: bool,
|
||||||
) -> List[Artifact]:
|
) -> List[Artifact]:
|
||||||
|
|
@ -448,15 +420,14 @@ def _package_windows_single(
|
||||||
artifacts = []
|
artifacts = []
|
||||||
|
|
||||||
dist_path = pathlib.Path("dist")
|
dist_path = pathlib.Path("dist")
|
||||||
utils.print_subtitle(f"Building {desc_arch} installer...")
|
utils.print_subtitle("Building installer...")
|
||||||
subprocess.run(['makensis.exe',
|
subprocess.run(['makensis.exe',
|
||||||
f'/DVERSION={qutebrowser.__version__}', *nsis_flags,
|
f'/DVERSION={qutebrowser.__version__}',
|
||||||
'misc/nsis/qutebrowser.nsi'], check=True)
|
'misc/nsis/qutebrowser.nsi'], check=True)
|
||||||
|
|
||||||
name_parts = [
|
name_parts = [
|
||||||
'qutebrowser',
|
'qutebrowser',
|
||||||
str(qutebrowser.__version__),
|
str(qutebrowser.__version__),
|
||||||
filename_arch,
|
|
||||||
]
|
]
|
||||||
if debug:
|
if debug:
|
||||||
name_parts.append('debug')
|
name_parts.append('debug')
|
||||||
|
|
@ -467,16 +438,15 @@ def _package_windows_single(
|
||||||
artifacts.append(Artifact(
|
artifacts.append(Artifact(
|
||||||
path=dist_path / name,
|
path=dist_path / name,
|
||||||
mimetype='application/vnd.microsoft.portable-executable',
|
mimetype='application/vnd.microsoft.portable-executable',
|
||||||
description=f'Windows {desc_arch} installer{desc_suffix}',
|
description='Windows installer',
|
||||||
))
|
))
|
||||||
|
|
||||||
utils.print_subtitle(f"Zipping {desc_arch} standalone...")
|
utils.print_subtitle("Zipping standalone...")
|
||||||
zip_name_parts = [
|
zip_name_parts = [
|
||||||
'qutebrowser',
|
'qutebrowser',
|
||||||
str(qutebrowser.__version__),
|
str(qutebrowser.__version__),
|
||||||
'windows',
|
'windows',
|
||||||
'standalone',
|
'standalone',
|
||||||
filename_arch,
|
|
||||||
]
|
]
|
||||||
if debug:
|
if debug:
|
||||||
zip_name_parts.append('debug')
|
zip_name_parts.append('debug')
|
||||||
|
|
@ -489,7 +459,7 @@ def _package_windows_single(
|
||||||
artifacts.append(Artifact(
|
artifacts.append(Artifact(
|
||||||
path=zip_path,
|
path=zip_path,
|
||||||
mimetype='application/zip',
|
mimetype='application/zip',
|
||||||
description=f'Windows {desc_arch} standalone{desc_suffix}',
|
description='Windows standalone',
|
||||||
))
|
))
|
||||||
|
|
||||||
return artifacts
|
return artifacts
|
||||||
|
|
@ -660,10 +630,6 @@ def main() -> None:
|
||||||
help="Skip confirmation before uploading.")
|
help="Skip confirmation before uploading.")
|
||||||
parser.add_argument('--skip-packaging', action='store_true', required=False,
|
parser.add_argument('--skip-packaging', action='store_true', required=False,
|
||||||
help="Skip Windows installer/zip generation or macOS DMG.")
|
help="Skip Windows installer/zip generation or macOS DMG.")
|
||||||
parser.add_argument('--32bit', action='store_true', required=False,
|
|
||||||
help="Skip Windows 64 bit build.", dest='only_32bit')
|
|
||||||
parser.add_argument('--64bit', action='store_true', required=False,
|
|
||||||
help="Skip Windows 32 bit build.", dest='only_64bit')
|
|
||||||
parser.add_argument('--debug', action='store_true', required=False,
|
parser.add_argument('--debug', action='store_true', required=False,
|
||||||
help="Build a debug build.")
|
help="Build a debug build.")
|
||||||
parser.add_argument('--qt5', action='store_true', required=False,
|
parser.add_argument('--qt5', action='store_true', required=False,
|
||||||
|
|
@ -694,8 +660,6 @@ def main() -> None:
|
||||||
artifacts = build_windows(
|
artifacts = build_windows(
|
||||||
gh_token=gh_token,
|
gh_token=gh_token,
|
||||||
skip_packaging=args.skip_packaging,
|
skip_packaging=args.skip_packaging,
|
||||||
only_32bit=args.only_32bit,
|
|
||||||
only_64bit=args.only_64bit,
|
|
||||||
qt5=args.qt5,
|
qt5=args.qt5,
|
||||||
debug=args.debug,
|
debug=args.debug,
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -300,3 +300,14 @@ Feature: Special qute:// pages
|
||||||
Scenario: Open qute://gpl
|
Scenario: Open qute://gpl
|
||||||
When I open qute://gpl
|
When I open qute://gpl
|
||||||
Then the page should contain the plaintext "GNU GENERAL PUBLIC LICENSE"
|
Then the page should contain the plaintext "GNU GENERAL PUBLIC LICENSE"
|
||||||
|
|
||||||
|
# qute://start
|
||||||
|
|
||||||
|
Scenario: Seaching on qute://start
|
||||||
|
When I set url.searchengines to {"DEFAULT": "http://localhost:(port)/data/title.html?q={}"}
|
||||||
|
And I open qute://start
|
||||||
|
And I run :click-element id search-field
|
||||||
|
And I wait for "Entering mode KeyMode.insert *" in the log
|
||||||
|
And I press the keys "test"
|
||||||
|
And I press the key "<Enter>"
|
||||||
|
Then data/title.html?q=test should be loaded
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,7 @@
|
||||||
|
|
||||||
import os.path
|
import os.path
|
||||||
|
|
||||||
|
import pytest
|
||||||
import pytest_bdd as bdd
|
import pytest_bdd as bdd
|
||||||
|
|
||||||
from helpers import testutils
|
from helpers import testutils
|
||||||
|
|
@ -11,6 +12,14 @@ from helpers import testutils
|
||||||
bdd.scenarios('urlmarks.feature')
|
bdd.scenarios('urlmarks.feature')
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture(autouse=True)
|
||||||
|
def clear_marks(quteproc):
|
||||||
|
"""Clear all existing marks between tests."""
|
||||||
|
yield
|
||||||
|
quteproc.send_cmd(':quickmark-del --all')
|
||||||
|
quteproc.send_cmd(':bookmark-del --all')
|
||||||
|
|
||||||
|
|
||||||
def _check_marks(quteproc, quickmarks, expected, contains):
|
def _check_marks(quteproc, quickmarks, expected, contains):
|
||||||
"""Make sure the given line does (not) exist in the bookmarks.
|
"""Make sure the given line does (not) exist in the bookmarks.
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -86,6 +86,23 @@ Feature: quickmarks and bookmarks
|
||||||
And I run :bookmark-del http://localhost:(port)/data/numbers/5.txt
|
And I run :bookmark-del http://localhost:(port)/data/numbers/5.txt
|
||||||
Then the bookmark file should not contain "http://localhost:*/data/numbers/5.txt "
|
Then the bookmark file should not contain "http://localhost:*/data/numbers/5.txt "
|
||||||
|
|
||||||
|
Scenario: Deleting all bookmarks
|
||||||
|
When I open data/numbers/1.txt
|
||||||
|
And I run :bookmark-add
|
||||||
|
And I open data/numbers/2.txt
|
||||||
|
And I run :bookmark-add
|
||||||
|
And I run :bookmark-del --all
|
||||||
|
Then the message "Bookmarks cleared." should be shown
|
||||||
|
And the bookmark file should not contain "http://localhost:*/data/numbers/1.txt"
|
||||||
|
And the bookmark file should not contain "http://localhost:*/data/numbers/2.txt"
|
||||||
|
|
||||||
|
Scenario: Deleting all bookmarks with url
|
||||||
|
When I open data/numbers/1.txt
|
||||||
|
And I run :bookmark-add
|
||||||
|
And I run :bookmark-del --all https://example.org
|
||||||
|
Then the error "Cannot specify url and --all" should be shown
|
||||||
|
And the bookmark file should contain "http://localhost:*/data/numbers/1.txt"
|
||||||
|
|
||||||
Scenario: Deleting the current page's bookmark if it doesn't exist
|
Scenario: Deleting the current page's bookmark if it doesn't exist
|
||||||
When I open data/hello.txt
|
When I open data/hello.txt
|
||||||
And I run :bookmark-del
|
And I run :bookmark-del
|
||||||
|
|
@ -210,6 +227,20 @@ Feature: quickmarks and bookmarks
|
||||||
And I run :quickmark-del eighteen
|
And I run :quickmark-del eighteen
|
||||||
Then the quickmark file should not contain "eighteen http://localhost:*/data/numbers/18.txt "
|
Then the quickmark file should not contain "eighteen http://localhost:*/data/numbers/18.txt "
|
||||||
|
|
||||||
|
Scenario: Deleting all quickmarks
|
||||||
|
When I run :quickmark-add http://localhost:(port)/data/numbers/1.txt one
|
||||||
|
When I run :quickmark-add http://localhost:(port)/data/numbers/2.txt two
|
||||||
|
And I run :quickmark-del --all
|
||||||
|
Then the message "Quickmarks cleared." should be shown
|
||||||
|
And the quickmark file should not contain "one http://localhost:*/data/numbers/1.txt"
|
||||||
|
And the quickmark file should not contain "two http://localhost:*/data/numbers/2.txt"
|
||||||
|
|
||||||
|
Scenario: Deleting all quickmarks with name
|
||||||
|
When I run :quickmark-add http://localhost:(port)/data/numbers/1.txt one
|
||||||
|
And I run :quickmark-del --all invalid
|
||||||
|
Then the error "Cannot specify name and --all" should be shown
|
||||||
|
And the quickmark file should contain "one http://localhost:*/data/numbers/1.txt"
|
||||||
|
|
||||||
Scenario: Deleting the current page's quickmark if it has none
|
Scenario: Deleting the current page's quickmark if it has none
|
||||||
When I open data/hello.txt
|
When I open data/hello.txt
|
||||||
And I run :quickmark-del
|
And I run :quickmark-del
|
||||||
|
|
@ -233,3 +264,10 @@ Feature: quickmarks and bookmarks
|
||||||
And I run :bookmark-add
|
And I run :bookmark-add
|
||||||
And I open qute://bookmarks
|
And I open qute://bookmarks
|
||||||
Then the page should contain the plaintext "Test title"
|
Then the page should contain the plaintext "Test title"
|
||||||
|
|
||||||
|
Scenario: Following a bookmark
|
||||||
|
When I open data/numbers/1.txt in a new tab
|
||||||
|
And I run :bookmark-add
|
||||||
|
And I open qute://bookmarks
|
||||||
|
And I hint with args "links current" and follow a
|
||||||
|
Then data/numbers/1.txt should be loaded
|
||||||
|
|
|
||||||
|
|
@ -937,6 +937,7 @@ def test_restart(request, quteproc_new):
|
||||||
# If the new process hangs, this will hang too.
|
# If the new process hangs, this will hang too.
|
||||||
# Still better than just ignoring it, so we can fix it if something is broken.
|
# Still better than just ignoring it, so we can fix it if something is broken.
|
||||||
os.waitpid(pid, 0) # pid, options... positional-only :(
|
os.waitpid(pid, 0) # pid, options... positional-only :(
|
||||||
except ChildProcessError:
|
except (ChildProcessError, PermissionError):
|
||||||
# Already gone
|
# Already gone. Even if not documented, Windows seems to raise PermissionError
|
||||||
|
# here...
|
||||||
pass
|
pass
|
||||||
|
|
|
||||||
|
|
@ -150,7 +150,7 @@ def partial_compare(val1, val2, *, indent=0):
|
||||||
if val2 is Ellipsis:
|
if val2 is Ellipsis:
|
||||||
print_i("Ignoring ellipsis comparison", indent, error=True)
|
print_i("Ignoring ellipsis comparison", indent, error=True)
|
||||||
return PartialCompareOutcome()
|
return PartialCompareOutcome()
|
||||||
elif type(val1) != type(val2): # pylint: disable=unidiomatic-typecheck
|
elif type(val1) is not type(val2):
|
||||||
outcome = PartialCompareOutcome(
|
outcome = PartialCompareOutcome(
|
||||||
"Different types ({}, {}) -> False".format(type(val1).__name__,
|
"Different types ({}, {}) -> False".format(type(val1).__name__,
|
||||||
type(val2).__name__))
|
type(val2).__name__))
|
||||||
|
|
|
||||||
|
|
@ -6,12 +6,15 @@
|
||||||
|
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
import pytest_mock
|
||||||
|
|
||||||
pytest.importorskip('qutebrowser.qt.webenginecore')
|
pytest.importorskip("qutebrowser.qt.webenginecore")
|
||||||
|
|
||||||
|
from qutebrowser.qt.core import QUrl, QByteArray
|
||||||
from qutebrowser.qt.webenginecore import QWebEngineUrlRequestInfo
|
from qutebrowser.qt.webenginecore import QWebEngineUrlRequestInfo
|
||||||
|
|
||||||
from qutebrowser.browser.webengine import interceptor
|
from qutebrowser.browser.webengine import interceptor
|
||||||
|
from qutebrowser.extensions import interceptors
|
||||||
from qutebrowser.utils import qtutils
|
from qutebrowser.utils import qtutils
|
||||||
from helpers import testutils
|
from helpers import testutils
|
||||||
|
|
||||||
|
|
@ -19,10 +22,12 @@ from helpers import testutils
|
||||||
def test_no_missing_resource_types():
|
def test_no_missing_resource_types():
|
||||||
request_interceptor = interceptor.RequestInterceptor()
|
request_interceptor = interceptor.RequestInterceptor()
|
||||||
qb_keys = set(request_interceptor._resource_types.keys())
|
qb_keys = set(request_interceptor._resource_types.keys())
|
||||||
qt_keys = set(testutils.enum_members(
|
qt_keys = set(
|
||||||
QWebEngineUrlRequestInfo,
|
testutils.enum_members(
|
||||||
QWebEngineUrlRequestInfo.ResourceType,
|
QWebEngineUrlRequestInfo,
|
||||||
).values())
|
QWebEngineUrlRequestInfo.ResourceType,
|
||||||
|
).values()
|
||||||
|
)
|
||||||
assert qt_keys == qb_keys
|
assert qt_keys == qb_keys
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -30,3 +35,85 @@ def test_resource_type_values():
|
||||||
request_interceptor = interceptor.RequestInterceptor()
|
request_interceptor = interceptor.RequestInterceptor()
|
||||||
for qt_value, qb_item in request_interceptor._resource_types.items():
|
for qt_value, qb_item in request_interceptor._resource_types.items():
|
||||||
assert qtutils.extract_enum_val(qt_value) == qb_item.value
|
assert qtutils.extract_enum_val(qt_value) == qb_item.value
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
def we_request( # a shrubbery!
|
||||||
|
mocker: pytest_mock.MockerFixture,
|
||||||
|
) -> interceptor.WebEngineRequest:
|
||||||
|
qt_info = mocker.Mock(spec=QWebEngineUrlRequestInfo)
|
||||||
|
qt_info.requestMethod.return_value = QByteArray(b"GET")
|
||||||
|
first_party_url = QUrl("https://firstparty.example.org/")
|
||||||
|
request_url = QUrl("https://request.example.org/")
|
||||||
|
return interceptor.WebEngineRequest(
|
||||||
|
first_party_url=first_party_url,
|
||||||
|
request_url=request_url,
|
||||||
|
webengine_info=qt_info,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def test_block(we_request: interceptor.WebEngineRequest):
|
||||||
|
assert not we_request.is_blocked
|
||||||
|
we_request.block()
|
||||||
|
assert we_request.is_blocked
|
||||||
|
|
||||||
|
|
||||||
|
class TestRedirect:
|
||||||
|
REDIRECT_URL = QUrl("https://redirect.example.com/")
|
||||||
|
|
||||||
|
def test_redirect(self, we_request: interceptor.WebEngineRequest):
|
||||||
|
assert not we_request._redirected
|
||||||
|
we_request.redirect(self.REDIRECT_URL)
|
||||||
|
assert we_request._redirected
|
||||||
|
we_request._webengine_info.redirect.assert_called_once_with(self.REDIRECT_URL)
|
||||||
|
|
||||||
|
def test_twice(self, we_request: interceptor.WebEngineRequest):
|
||||||
|
we_request.redirect(self.REDIRECT_URL)
|
||||||
|
with pytest.raises(
|
||||||
|
interceptors.RedirectException,
|
||||||
|
match=r"Request already redirected.",
|
||||||
|
):
|
||||||
|
we_request.redirect(self.REDIRECT_URL)
|
||||||
|
we_request._webengine_info.redirect.assert_called_once_with(self.REDIRECT_URL)
|
||||||
|
|
||||||
|
def test_invalid_method(self, we_request: interceptor.WebEngineRequest):
|
||||||
|
we_request._webengine_info.requestMethod.return_value = QByteArray(b"POST")
|
||||||
|
with pytest.raises(
|
||||||
|
interceptors.RedirectException,
|
||||||
|
match=(
|
||||||
|
r"Request method b'POST' for https://request.example.org/ does not "
|
||||||
|
r"support redirection."
|
||||||
|
),
|
||||||
|
):
|
||||||
|
we_request.redirect(self.REDIRECT_URL)
|
||||||
|
assert not we_request._webengine_info.redirect.called
|
||||||
|
|
||||||
|
def test_invalid_method_ignore_unsupported(
|
||||||
|
self,
|
||||||
|
we_request: interceptor.WebEngineRequest,
|
||||||
|
caplog: pytest.LogCaptureFixture,
|
||||||
|
):
|
||||||
|
we_request._webengine_info.requestMethod.return_value = QByteArray(b"POST")
|
||||||
|
we_request.redirect(self.REDIRECT_URL, ignore_unsupported=True)
|
||||||
|
assert caplog.messages == [
|
||||||
|
"Request method b'POST' for https://request.example.org/ does not support "
|
||||||
|
"redirection."
|
||||||
|
]
|
||||||
|
assert not we_request._webengine_info.redirect.called
|
||||||
|
|
||||||
|
def test_improperly_initialized(self, we_request: interceptor.WebEngineRequest):
|
||||||
|
we_request._webengine_info = None
|
||||||
|
with pytest.raises(
|
||||||
|
interceptors.RedirectException,
|
||||||
|
match=r"Request improperly initialized.",
|
||||||
|
):
|
||||||
|
we_request.redirect(self.REDIRECT_URL)
|
||||||
|
|
||||||
|
def test_invalid_url(self, we_request: interceptor.WebEngineRequest):
|
||||||
|
url = QUrl()
|
||||||
|
assert not url.isValid()
|
||||||
|
with pytest.raises(
|
||||||
|
interceptors.RedirectException,
|
||||||
|
match=r"Redirect to invalid URL: PyQt\d\.QtCore\.QUrl\(''\) is not valid",
|
||||||
|
):
|
||||||
|
we_request.redirect(url)
|
||||||
|
|
|
||||||
|
|
@ -644,8 +644,8 @@ class TestModuleVersions:
|
||||||
assert version._module_versions() == expected
|
assert version._module_versions() == expected
|
||||||
|
|
||||||
@pytest.mark.parametrize('module, idx, expected', [
|
@pytest.mark.parametrize('module, idx, expected', [
|
||||||
('colorama', 1, 'colorama: no'),
|
('colorama', 0, 'colorama: no'),
|
||||||
('adblock', 5, 'adblock: no'),
|
('adblock', 4, 'adblock: no'),
|
||||||
])
|
])
|
||||||
def test_missing_module(self, module, idx, expected, import_fake):
|
def test_missing_module(self, module, idx, expected, import_fake):
|
||||||
"""Test with a module missing.
|
"""Test with a module missing.
|
||||||
|
|
@ -693,11 +693,11 @@ class TestModuleVersions:
|
||||||
assert not mod_info.is_usable()
|
assert not mod_info.is_usable()
|
||||||
|
|
||||||
expected = f"adblock: {fake_version} (< {mod_info.min_version}, outdated)"
|
expected = f"adblock: {fake_version} (< {mod_info.min_version}, outdated)"
|
||||||
assert version._module_versions()[5] == expected
|
assert version._module_versions()[4] == expected
|
||||||
|
|
||||||
@pytest.mark.parametrize('attribute, expected_modules', [
|
@pytest.mark.parametrize('attribute, expected_modules', [
|
||||||
('VERSION', ['colorama']),
|
('VERSION', ['colorama']),
|
||||||
('SIP_VERSION_STR', ['sip']),
|
('SIP_VERSION_STR', ['PyQt5.sip', 'PyQt6.sip']),
|
||||||
(None, []),
|
(None, []),
|
||||||
])
|
])
|
||||||
def test_version_attribute(self, attribute, expected_modules, import_fake):
|
def test_version_attribute(self, attribute, expected_modules, import_fake):
|
||||||
|
|
|
||||||
11
tox.ini
11
tox.ini
|
|
@ -181,7 +181,7 @@ commands =
|
||||||
{envpython} scripts/dev/check_doc_changes.py {posargs}
|
{envpython} scripts/dev/check_doc_changes.py {posargs}
|
||||||
{envpython} scripts/asciidoc2html.py {posargs}
|
{envpython} scripts/asciidoc2html.py {posargs}
|
||||||
|
|
||||||
[testenv:pyinstaller-{64bit,32bit}{,-qt5}]
|
[testenv:pyinstaller{,-qt5}]
|
||||||
basepython = {env:PYTHON:python3}
|
basepython = {env:PYTHON:python3}
|
||||||
passenv =
|
passenv =
|
||||||
APPDATA
|
APPDATA
|
||||||
|
|
@ -282,3 +282,12 @@ deps =
|
||||||
commands =
|
commands =
|
||||||
!qt5: {envpython} {toxinidir}/scripts/dev/build_release.py {posargs}
|
!qt5: {envpython} {toxinidir}/scripts/dev/build_release.py {posargs}
|
||||||
qt5: {envpython} {toxinidir}/scripts/dev/build_release.py --qt5 {posargs}
|
qt5: {envpython} {toxinidir}/scripts/dev/build_release.py --qt5 {posargs}
|
||||||
|
|
||||||
|
[testenv:package]
|
||||||
|
basepython = {env:PYTHON:python3}
|
||||||
|
setenv =
|
||||||
|
PYTHONWARNINGS=error,default:pkg_resources is deprecated as an API.:DeprecationWarning
|
||||||
|
deps =
|
||||||
|
-r{toxinidir}/requirements.txt
|
||||||
|
-r{toxinidir}/misc/requirements/requirements-dev.txt
|
||||||
|
commands = {envpython} -m build
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue