summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.github/workflows/bleeding.yml2
-rw-r--r--.github/workflows/ci.yml6
-rw-r--r--.mypy.ini1
-rw-r--r--.pylintrc1
-rw-r--r--README.asciidoc2
-rw-r--r--doc/changelog.asciidoc41
-rw-r--r--doc/contributing.asciidoc2
-rw-r--r--doc/help/configuring.asciidoc4
-rw-r--r--doc/help/index.asciidoc6
-rw-r--r--doc/help/settings.asciidoc146
-rw-r--r--doc/install.asciidoc4
-rw-r--r--doc/quickstart.asciidoc6
-rw-r--r--doc/qutebrowser.1.asciidoc8
-rw-r--r--doc/userscripts.asciidoc1
-rw-r--r--misc/requirements/requirements-check-manifest.txt7
-rw-r--r--misc/requirements/requirements-dev.txt32
-rw-r--r--misc/requirements/requirements-flake8.txt6
-rw-r--r--misc/requirements/requirements-mypy.txt24
-rw-r--r--misc/requirements/requirements-pip.txt8
-rw-r--r--misc/requirements/requirements-pyinstaller.txt4
-rw-r--r--misc/requirements/requirements-pylint.txt30
-rw-r--r--misc/requirements/requirements-pylint.txt-raw3
-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.txt2
-rw-r--r--misc/requirements/requirements-pyroma.txt10
-rw-r--r--misc/requirements/requirements-qutebrowser.txt-raw8
-rw-r--r--misc/requirements/requirements-sphinx.txt20
-rw-r--r--misc/requirements/requirements-tests-bleeding.txt3
-rw-r--r--misc/requirements/requirements-tests.txt58
-rw-r--r--misc/requirements/requirements-tests.txt-raw22
-rw-r--r--misc/requirements/requirements-tox.txt13
-rw-r--r--misc/requirements/requirements-tox.txt-raw2
-rw-r--r--misc/userscripts/README.md7
-rwxr-xr-xmisc/userscripts/cast31
-rwxr-xr-xmisc/userscripts/qute-bitwarden41
-rwxr-xr-xmisc/userscripts/qute-lastpass2
-rw-r--r--[-rwxr-xr-x]misc/userscripts/qute-pass7
-rwxr-xr-xmisc/userscripts/ripbang18
-rw-r--r--qutebrowser/browser/commands.py1
-rw-r--r--qutebrowser/browser/shared.py4
-rw-r--r--qutebrowser/browser/webengine/webenginetab.py4
-rw-r--r--qutebrowser/browser/webkit/http.py11
-rw-r--r--qutebrowser/browser/webkit/webkithistory.py2
-rw-r--r--qutebrowser/completion/completer.py2
-rw-r--r--qutebrowser/completion/models/miscmodels.py16
-rw-r--r--qutebrowser/config/configdata.yml70
-rw-r--r--qutebrowser/config/configfiles.py4
-rw-r--r--qutebrowser/config/qtargs.py9
-rw-r--r--qutebrowser/html/settings.html93
-rw-r--r--qutebrowser/html/warning-webkit.html6
-rw-r--r--qutebrowser/mainwindow/prompt.py17
-rw-r--r--qutebrowser/mainwindow/tabbedbrowser.py24
-rw-r--r--qutebrowser/mainwindow/tabwidget.py20
-rw-r--r--qutebrowser/misc/crashsignal.py2
-rw-r--r--qutebrowser/misc/debugcachestats.py26
-rw-r--r--qutebrowser/misc/editor.py2
-rw-r--r--qutebrowser/misc/guiprocess.py34
-rw-r--r--qutebrowser/misc/utilcmds.py5
-rw-r--r--qutebrowser/utils/log.py1
-rw-r--r--qutebrowser/utils/resources.py21
-rw-r--r--qutebrowser/utils/utils.py3
-rw-r--r--qutebrowser/utils/version.py7
-rw-r--r--requirements.txt17
-rwxr-xr-xscripts/dev/build_release.py6
-rw-r--r--scripts/dev/changelog_urls.json161
-rw-r--r--scripts/dev/misc_checks.py7
-rw-r--r--scripts/dev/recompile_requirements.py223
-rw-r--r--scripts/dev/run_pylint_on_tests.py3
-rwxr-xr-xscripts/dev/src2asciidoc.py4
-rw-r--r--tests/conftest.py21
-rw-r--r--tests/end2end/conftest.py39
-rw-r--r--tests/end2end/features/tabs.feature19
-rw-r--r--tests/end2end/features/utilcmds.feature1
-rw-r--r--tests/end2end/fixtures/quteprocess.py4
-rw-r--r--tests/end2end/fixtures/webserver.py10
-rw-r--r--tests/end2end/fixtures/webserver_sub.py2
-rw-r--r--tests/end2end/test_invocations.py82
-rw-r--r--tests/helpers/testutils.py42
-rw-r--r--tests/unit/completion/test_models.py28
-rw-r--r--tests/unit/config/test_qtargs.py26
-rw-r--r--tests/unit/misc/test_editor.py15
-rw-r--r--tests/unit/misc/test_guiprocess.py4
-rw-r--r--tests/unit/utils/test_log.py2
-rw-r--r--tox.ini4
88 files changed, 1119 insertions, 583 deletions
diff --git a/.github/workflows/bleeding.yml b/.github/workflows/bleeding.yml
index 435141e56..b2370357f 100644
--- a/.github/workflows/bleeding.yml
+++ b/.github/workflows/bleeding.yml
@@ -15,7 +15,7 @@ jobs:
container:
image: "qutebrowser/ci:archlinux-webengine-unstable"
env:
- PY_COLORS: "1"
+ FORCE_COLOR: "1"
DOCKER: "archlinux-webengine-unstable"
CI: true
PYTEST_ADDOPTS: "--color=yes"
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index e50ba2c60..afcf720e4 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -6,7 +6,7 @@ on:
- 'dependabot/*'
pull_request:
env:
- PY_COLORS: "1"
+ FORCE_COLOR: "1"
MYPY_FORCE_TERMINAL_WIDTH: "180"
jobs:
@@ -43,7 +43,7 @@ jobs:
key: "${{ matrix.testenv }}-${{ hashFiles('misc/requirements/requirements-*.txt') }}-${{ hashFiles('requirements.txt') }}-${{ hashFiles('scripts/dev/pylint_checkers/qute_pylint/*.py') }}"
- uses: actions/setup-python@v2
with:
- python-version: '3.8'
+ python-version: '3.10'
- uses: actions/setup-node@v2-beta
with:
node-version: '12.x'
@@ -53,7 +53,7 @@ jobs:
- name: Install dependencies
run: |
[[ ${{ matrix.testenv }} == eslint ]] && npm install -g eslint
- [[ ${{ matrix.testenv }} == docs ]] && sudo apt-get install --no-install-recommends asciidoc
+ [[ ${{ matrix.testenv }} == docs ]] && sudo apt-get update && sudo apt-get install --no-install-recommends asciidoc
if [[ ${{ matrix.testenv }} == shellcheck ]]; then
scversion="stable"
bindir="$HOME/.local/bin"
diff --git a/.mypy.ini b/.mypy.ini
index 289f3eb87..501ab747e 100644
--- a/.mypy.ini
+++ b/.mypy.ini
@@ -20,6 +20,7 @@ strict_equality = True
### Other strictness flags
warn_unreachable = True
disallow_any_unimported = True
+enable_error_code = ignore-without-code
### Output
show_error_codes = True
diff --git a/.pylintrc b/.pylintrc
index a3ac3d82a..4ff4f2080 100644
--- a/.pylintrc
+++ b/.pylintrc
@@ -15,6 +15,7 @@ load-plugins=qute_pylint.config,
pylint.extensions.check_elif,
pylint.extensions.typing,
pylint.extensions.docparams,
+ pylint.extensions.private_import,
persistent=n
py-version=3.6
diff --git a/README.asciidoc b/README.asciidoc
index a5d3af9ff..bb1f2562c 100644
--- a/README.asciidoc
+++ b/README.asciidoc
@@ -63,7 +63,7 @@ ways:
* Use the built-in `:report` command or the automatic crash dialog.
* Open an issue in the Github issue tracker.
* Write a mail to the
-https://lists.schokokeks.org/mailman/listinfo.cgi/qutebrowser[mailinglist] at
+https://listi.jpberlin.de/mailman/listinfo/qutebrowser[mailinglist] at
mailto:qutebrowser@lists.qutebrowser.org[].
For security bugs, please contact me directly at mail@qutebrowser.org, GPG ID
diff --git a/doc/changelog.asciidoc b/doc/changelog.asciidoc
index 5e34f5fbc..0f697888d 100644
--- a/doc/changelog.asciidoc
+++ b/doc/changelog.asciidoc
@@ -42,6 +42,21 @@ Changed
- Clicking on a notification now tries to focus the tab where the notification
is coming from. Note this might not work properly if there is more than one
tab from the same host open.
+- Improvements to userscripts:
+ * `qute-bitwarden` understands a new `--password-prompt-invocation`, which can
+ be used to specify a tool other than `rofi` to ask for a password.
+ * `cast` now uses `yt-dlp` if available (falling back to `youtube-dl` if not).
+ It also lets users override the tool to use via a `QUTE_CAST_YTDL_PROGRAM`
+ environment variable.
+ * `qute-pass` now understands a new `--prefix` argument if used in gopass
+ mode, which gets passed as subfolder prefix to `gopass`.
+ * `ripbang` now works again (it got blocked due to a missing user agent and
+ used outdated qutebrowser commands before)
+- The `content.headers.custom` setting now accepts empty strings as values,
+ resulting in an empty header being sent.
+- Renamed settings:
+ * `qt.low_end_device_mode` -> `qt.chromium.low_end_device_mode`
+ * `qt.process_model` -> `qt.chromium.process_model`
Added
~~~~~
@@ -50,6 +65,14 @@ Added
more emacs-like bindings.
- New `{relative_index}` field for `tabs.title.format` (and `.pinned_format`)
which shows relative tab numbers.
+- New `input.mode_override` option which allows overriding the current mode
+ based on the new URL when navigating or switching tabs.
+- New `qt.chromium.sandboxing` setting which allows to disable Chromium's
+ sandboxing (mainly intended for development and testing)
+- New `QUTE_TAB_INDEX` variable for userscripts, containing the index of the
+ current tab.
+- New `editor.remove_file` setting which can be set to `False` to keep all
+ temporary editor files after closing the external editor.
Fixed
~~~~~
@@ -65,6 +88,22 @@ Fixed
shown when closing the last window (rather than closing any window, which
would continue running that window's downloads). Unfortunately, more issues
with `confirm_quit` and multiple windows remain.
+- Crash when a previous crash-log file contains non-ASCII characters (which
+ should never happen unless it was edited manually)
+- Due to changes in Debian, an old workaround (for broken QtWebEngine patching
+ on Debian) caused the inferior qutebrowser error page to be displayed, when
+ Chromium's would have worked fine. The workaround was now dropped.
+- Crash when using `<Ctrl-D>` (`:completion-item-del`) in the `:tab-focus`
+ list, rather than `:tab-select`.
+- Work around a Qt issue causing `:spawn` to run executables from the current
+ directory if no system-wide executable was found. The underlying Qt bug is
+ tracked as [CVE-2022-25255](https://lists.qt-project.org/pipermail/announce/2022-February/000333.html),
+ though the impact with typical qutebrowser usage is low: Normally,
+ qutebrowser is run from a fixed location (usually the users home directory),
+ and `:spawn` is not typically used with executables that don't exist. The main
+ security impact of this bug is in tools like text editors, which are often
+ executed in untrusted directories and might attempt to run auxiliary tools
+ automatically.
[[v2.4.1]]
v2.4.1 (unreleased)
@@ -4241,7 +4280,7 @@ v0.1.4 (2015-03-19)
Changed
~~~~~~~
-* The Windows builds come with Qt 5.4.1 which has some https://lists.schokokeks.org/pipermail/qutebrowser/2015-March/000054.html[related bugfixes].
+* The Windows builds come with Qt 5.4.1 which has some https://listi.jpberlin.de/pipermail/qutebrowser/2015-March/000054.html[related bugfixes].
* Improvements to CPU usage when idle.
* Ensure there's no size for `font-family` settings.
* Handle URLs with double-colon as search strings.
diff --git a/doc/contributing.asciidoc b/doc/contributing.asciidoc
index 1f87e9163..75c19045e 100644
--- a/doc/contributing.asciidoc
+++ b/doc/contributing.asciidoc
@@ -24,7 +24,7 @@ several ways:
* Send a mail to the mailing list at mailto:qutebrowser@lists.qutebrowser.org[]
(optionally
-https://lists.schokokeks.org/mailman/listinfo.cgi/qutebrowser[subscribe]
+https://listi.jpberlin.de/mailman/listinfo/qutebrowser[subscribe]
first).
* Join the IRC channel link:ircs://irc.libera.chat:6697/#qutebrowser[`#qutebrowser`] on
https://libera.chat/[Libera Chat] (https://web.libera.chat/#qutebrowser[webchat],
diff --git a/doc/help/configuring.asciidoc b/doc/help/configuring.asciidoc
index 552145023..b469118ed 100644
--- a/doc/help/configuring.asciidoc
+++ b/doc/help/configuring.asciidoc
@@ -407,11 +407,11 @@ Pre-built colorschemes
- A collection of https://github.com/chriskempson/base16[base16] color-schemes can be found in https://github.com/theova/base16-qutebrowser[base16-qutebrowser] and used with https://github.com/AuditeMarlow/base16-manager[base16-manager].
- Another collection: https://github.com/leosolid/qutebrowser-themes[qutebrowser-themes]
-- https://gitlab.com/jjzmajic/qutewal[Pywal integration]
+- Pywal integration: https://gitlab.com/jjzmajic/qutewal[qutewal], https://github.com/makman12/pywalQute[pywalQute]
- https://github.com/arcticicestudio/nord[Nord]: https://github.com/Linuus/nord-qutebrowser[Linuus], https://github.com/KnownAsDon/QuteBrowser-Nord-Theme[KnownAsDon]
- https://github.com/dracula/qutebrowser-dracula-theme[Dracula]
- https://gitlab.com/lovetocode999/selenized-qutebrowser[Selenized]
-- https://github.com/morhetz/gruvbox[gruvbox]: https://github.com/The-Compiler/dotfiles/blob/master/qutebrowser/gruvbox.py[The-Compiler], https://gitlab.com/shaneyost/dots-popos-september-2020/-/blob/master/qutebrowser/config.py[Shane Yost]
+- https://github.com/The-Compiler/dotfiles/blob/master/qutebrowser/gruvbox.py[gruvbox]
- https://www.opencode.net/wakellor957/qb-breath/-/blob/main/qb-breath.py[Manjaro Breath-like]
Avoiding flake8 errors
diff --git a/doc/help/index.asciidoc b/doc/help/index.asciidoc
index c7fb88c8d..127cc5d86 100644
--- a/doc/help/index.asciidoc
+++ b/doc/help/index.asciidoc
@@ -26,10 +26,10 @@ link:ircs://irc.libera.chat:6697/#qutebrowser[`#qutebrowser`] on
https://libera.chat/[Libera Chat]
(https://web.libera.chat/#qutebrowser[webchat], https://matrix.to/#qutebrowser:libera.chat[via Matrix]),
or by writing a message to the
-https://lists.schokokeks.org/mailman/listinfo.cgi/qutebrowser[mailinglist] at
+https://listi.jpberlin.de/mailman/listinfo/qutebrowser[mailinglist] at
mailto:qutebrowser@lists.qutebrowser.org[].
-There's also an https://lists.schokokeks.org/mailman/listinfo.cgi/qutebrowser-announce[announce-only mailinglist]
+There's also an https://listi.jpberlin.de/mailman/listinfo/qutebrowser-announce[announce-only mailinglist]
at mailto:qutebrowser-announce@lists.qutebrowser.org[] (the announcements also
get sent to the general qutebrowser@ list).
@@ -49,7 +49,7 @@ ways:
* Use the built-in `:report` command or the automatic crash dialog.
* Open an issue in the Github issue tracker.
* Write a mail to the
-https://lists.schokokeks.org/mailman/listinfo.cgi/qutebrowser[mailinglist] at
+https://listi.jpberlin.de/mailman/listinfo/qutebrowser[mailinglist] at
mailto:qutebrowser@lists.qutebrowser.org[].
Other resources
diff --git a/doc/help/settings.asciidoc b/doc/help/settings.asciidoc
index b8705749a..bdc5bef99 100644
--- a/doc/help/settings.asciidoc
+++ b/doc/help/settings.asciidoc
@@ -213,6 +213,7 @@
|<<downloads.remove_finished,downloads.remove_finished>>|Duration (in milliseconds) to wait before removing finished downloads.
|<<editor.command,editor.command>>|Editor (and arguments) to use for the `edit-*` commands.
|<<editor.encoding,editor.encoding>>|Encoding to use for the editor.
+|<<editor.remove_file,editor.remove_file>>|Delete the temporary file upon closing the editor.
|<<fileselect.folder.command,fileselect.folder.command>>|Command (and arguments) to use for selecting a single folder in forms. The command should write the selected folder path to the specified file or stdout.
|<<fileselect.handler,fileselect.handler>>|Handler for selecting file(s) in forms. If `external`, then the commands specified by `fileselect.single_file.command` and `fileselect.multiple_files.command` are used to select one or multiple files respectively.
|<<fileselect.multiple_files.command,fileselect.multiple_files.command>>|Command (and arguments) to use for selecting multiple files in forms. The command should write the selected file paths to the specified file or to stdout, separated by newlines.
@@ -271,6 +272,7 @@
|<<input.links_included_in_focus_chain,input.links_included_in_focus_chain>>|Include hyperlinks in the keyboard focus chain when tabbing.
|<<input.match_counts,input.match_counts>>|Interpret number prefixes as counts for bindings.
|<<input.media_keys,input.media_keys>>|Whether the underlying Chromium should handle media keys.
+|<<input.mode_override,input.mode_override>>|Mode to change to when focusing on a tab/URL changes.
|<<input.mouse.back_forward_buttons,input.mouse.back_forward_buttons>>|Enable back and forward buttons on the mouse.
|<<input.mouse.rocker_gestures,input.mouse.rocker_gestures>>|Enable Opera-like mouse rocker gestures.
|<<input.partial_timeout,input.partial_timeout>>|Timeout (in milliseconds) for partially typed key bindings.
@@ -286,13 +288,14 @@
|<<prompt.filebrowser,prompt.filebrowser>>|Show a filebrowser in download prompts.
|<<prompt.radius,prompt.radius>>|Rounding radius (in pixels) for the edges of prompts.
|<<qt.args,qt.args>>|Additional arguments to pass to Qt, without leading `--`.
+|<<qt.chromium.low_end_device_mode,qt.chromium.low_end_device_mode>>|When to use Chromium's low-end device mode.
+|<<qt.chromium.process_model,qt.chromium.process_model>>|Which Chromium process model to use.
+|<<qt.chromium.sandboxing,qt.chromium.sandboxing>>|What sandboxing mechanisms in Chromium to use.
|<<qt.environ,qt.environ>>|Additional environment variables to set.
|<<qt.force_platform,qt.force_platform>>|Force a Qt platform to use.
|<<qt.force_platformtheme,qt.force_platformtheme>>|Force a Qt platformtheme to use.
|<<qt.force_software_rendering,qt.force_software_rendering>>|Force software rendering for QtWebEngine.
|<<qt.highdpi,qt.highdpi>>|Turn on Qt HighDPI scaling.
-|<<qt.low_end_device_mode,qt.low_end_device_mode>>|When to use Chromium's low-end device mode.
-|<<qt.process_model,qt.process_model>>|Which Chromium process model to use.
|<<qt.workarounds.locale,qt.workarounds.locale>>|Work around locale parsing issues in QtWebEngine 5.15.3.
|<<qt.workarounds.remove_service_workers,qt.workarounds.remove_service_workers>>|Delete the QtWebEngine Service Worker directory on every start.
|<<scrolling.bar,scrolling.bar>>|When/how to show the scrollbar.
@@ -438,9 +441,7 @@ The following modes are available:
that single keys can't be bound in this mode.
* passthrough: Similar to insert mode, but passes through all keypresses
- except `<Escape>` to leave the mode. It might be useful to bind
- `<Escape>` to some other key in this mode if you want to be able to send
- an Escape key to the website as well. Note that single keys can't be
+ except `<Shift+Escape>` to leave the mode. Note that single keys can't be
bound in this mode.
* command: Entered when pressing the `:` key in order to enter a command.
@@ -2941,6 +2942,14 @@ Type: <<types,Encoding>>
Default: +pass:[utf-8]+
+[[editor.remove_file]]
+=== editor.remove_file
+Delete the temporary file upon closing the editor.
+
+Type: <<types,Bool>>
+
+Default: +pass:[true]+
+
[[fileselect.folder.command]]
=== fileselect.folder.command
Command (and arguments) to use for selecting a single folder in forms. The command should write the selected folder path to the specified file or stdout.
@@ -3587,6 +3596,22 @@ Type: <<types,Bool>>
Default: +pass:[true]+
+[[input.mode_override]]
+=== input.mode_override
+Mode to change to when focusing on a tab/URL changes.
+
+This setting supports link:configuring{outfilesuffix}#patterns[URL patterns].
+
+Type: <<types,String>>
+
+Valid values:
+
+ * +normal+
+ * +insert+
+ * +passthrough+
+
+Default: empty
+
[[input.mouse.back_forward_buttons]]
=== input.mouse.back_forward_buttons
Enable back and forward buttons on the mouse.
@@ -3617,7 +3642,7 @@ Default: +pass:[0]+
[[input.spatial_navigation]]
=== input.spatial_navigation
Enable spatial navigation.
-Spatial navigation consists in the ability to navigate between focusable elements in a Web page, such as hyperlinks and form controls, by using Left, Right, Up and Down arrow keys. For example, if the user presses the Right key, heuristics determine whether there is an element he might be trying to reach towards the right and which element he probably wants.
+Spatial navigation consists in the ability to navigate between focusable elements, such as hyperlinks and form controls, on a web page by using the Left, Right, Up and Down arrow keys. For example, if a user presses the Right key, heuristics determine whether there is an element they might be trying to reach towards the right and which element they probably want.
This setting supports link:configuring{outfilesuffix}#patterns[URL patterns].
@@ -3755,6 +3780,73 @@ Type: <<types,List of String>>
Default: empty
+[[qt.chromium.low_end_device_mode]]
+=== qt.chromium.low_end_device_mode
+When to use Chromium's low-end device mode.
+This improves the RAM usage of renderer processes, at the expense of performance.
+
+This setting requires a restart.
+
+This setting is only available with the QtWebEngine backend.
+
+Type: <<types,String>>
+
+Valid values:
+
+ * +always+: Always use low-end device mode.
+ * +auto+: Decide automatically (uses low-end mode with < 1 GB available RAM).
+ * +never+: Never use low-end device mode.
+
+Default: +pass:[auto]+
+
+[[qt.chromium.process_model]]
+=== qt.chromium.process_model
+Which Chromium process model to use.
+Alternative process models use less resources, but decrease security and robustness.
+See the following pages for more details:
+
+ - https://www.chromium.org/developers/design-documents/process-models
+ - https://doc.qt.io/qt-5/qtwebengine-features.html#process-models
+
+This setting requires a restart.
+
+This setting is only available with the QtWebEngine backend.
+
+Type: <<types,String>>
+
+Valid values:
+
+ * +process-per-site-instance+: Pages from separate sites are put into separate processes and separate visits to the same site are also isolated.
+ * +process-per-site+: Pages from separate sites are put into separate processes. Unlike Process per Site Instance, all visits to the same site will share an OS process. The benefit of this model is reduced memory consumption, because more web pages will share processes. The drawbacks include reduced security, robustness, and responsiveness.
+ * +single-process+: Run all tabs in a single process. This should be used for debugging purposes only, and it disables `:open --private`.
+
+Default: +pass:[process-per-site-instance]+
+
+[[qt.chromium.sandboxing]]
+=== qt.chromium.sandboxing
+What sandboxing mechanisms in Chromium to use.
+Chromium has various sandboxing layers, which should be enabled for normal browser usage. Mainly for testing and development, it's possible to disable individual sandboxing layers via this setting.
+Open `chrome://sandbox` to see the current sandbox status.
+Changing this setting is only recommended if you know what you're doing, as it **disables one of Chromium's security layers**. To avoid sandboxing being accidentally disabled persistently, this setting can only be set via `config.py`, not via `:set`.
+See the Chromium documentation for more details:
+- https://chromium.googlesource.com/chromium/src/\+/HEAD/docs/linux/sandboxing.md[Linux] - https://chromium.googlesource.com/chromium/src/\+/HEAD/docs/design/sandbox.md[Windows] - https://chromium.googlesource.com/chromium/src/\+/HEAD/docs/design/sandbox_faq.md[FAQ (Windows-centric)]
+
+This setting requires a restart.
+
+This setting can only be set in config.py.
+
+This setting is only available with the QtWebEngine backend.
+
+Type: <<types,String>>
+
+Valid values:
+
+ * +enable-all+: Enable all available sandboxing mechanisms.
+ * +disable-seccomp-bpf+: Disable the Seccomp BPF filter sandbox (Linux only).
+ * +disable-all+: Disable all sandboxing (**not recommended!**).
+
+Default: +pass:[enable-all]+
+
[[qt.environ]]
=== qt.environ
Additional environment variables to set.
@@ -3820,48 +3912,6 @@ Type: <<types,Bool>>
Default: +pass:[false]+
-[[qt.low_end_device_mode]]
-=== qt.low_end_device_mode
-When to use Chromium's low-end device mode.
-This improves the RAM usage of renderer processes, at the expense of performance.
-
-This setting requires a restart.
-
-This setting is only available with the QtWebEngine backend.
-
-Type: <<types,String>>
-
-Valid values:
-
- * +always+: Always use low-end device mode.
- * +auto+: Decide automatically (uses low-end mode with < 1 GB available RAM).
- * +never+: Never use low-end device mode.
-
-Default: +pass:[auto]+
-
-[[qt.process_model]]
-=== qt.process_model
-Which Chromium process model to use.
-Alternative process models use less resources, but decrease security and robustness.
-See the following pages for more details:
-
- - https://www.chromium.org/developers/design-documents/process-models
- - https://doc.qt.io/qt-5/qtwebengine-features.html#process-models
-
-This setting requires a restart.
-
-This setting is only available with the QtWebEngine backend.
-
-Type: <<types,String>>
-
-Valid values:
-
- * +process-per-site-instance+: Pages from separate sites are put into separate processes and separate visits to the same site are also isolated.
- * +process-per-site+: Pages from separate sites are put into separate processes. Unlike Process per Site Instance, all visits to the same site will share an OS process. The benefit of this model is reduced memory consumption, because more web pages will share processes. The drawbacks include reduced security, robustness, and responsiveness.
- * +single-process+: Run all tabs in a single process. This should be used for debugging purposes only, and it disables `:open --private`.
-
-Default: +pass:[process-per-site-instance]+
-
[[qt.workarounds.locale]]
=== qt.workarounds.locale
Work around locale parsing issues in QtWebEngine 5.15.3.
diff --git a/doc/install.asciidoc b/doc/install.asciidoc
index 83c332b4d..dd284fb9a 100644
--- a/doc/install.asciidoc
+++ b/doc/install.asciidoc
@@ -278,7 +278,7 @@ https://github.com/qutebrowser/qutebrowser/releases[are built] for every
release.
Note that you'll need to upgrade to new versions manually (subscribe to the
-https://lists.schokokeks.org/mailman/listinfo.cgi/qutebrowser-announce[qutebrowser-announce
+https://listi.jpberlin.de/mailman/listinfo/qutebrowser-announce[qutebrowser-announce
mailinglist] to get notified on new releases). You can install a newer version
without uninstalling the older one.
@@ -335,7 +335,7 @@ files from the
https://github.com/qutebrowser/qutebrowser/releases[release page].
Note that you'll need to upgrade to new versions manually (subscribe to the
-https://lists.schokokeks.org/mailman/listinfo.cgi/qutebrowser-announce[qutebrowser-announce
+https://listi.jpberlin.de/mailman/listinfo/qutebrowser-announce[qutebrowser-announce
mailinglist] to get notified on new releases).
The binary release ships with a QtWebEngine built without proprietary codec
diff --git a/doc/quickstart.asciidoc b/doc/quickstart.asciidoc
index 2e61e442d..0c42880ab 100644
--- a/doc/quickstart.asciidoc
+++ b/doc/quickstart.asciidoc
@@ -33,8 +33,8 @@ image:https://raw.githubusercontent.com/qutebrowser/qutebrowser/master/doc/img/c
`scripts/asciidoc2html.py` to generate the documentation.
* Go to the link:qute://settings[settings page] to set up qutebrowser the way you want it.
* Subscribe to
-https://lists.schokokeks.org/mailman/listinfo.cgi/qutebrowser[the mailinglist] or
-https://lists.schokokeks.org/mailman/listinfo.cgi/qutebrowser-announce[the announce-only mailinglist].
+https://listi.jpberlin.de/mailman/listinfo/qutebrowser[the mailinglist] or
+https://listi.jpberlin.de/mailman/listinfo/qutebrowser-announce[the announce-only mailinglist].
* Let me know what features you are missing or things that need (even small!)
improvements.
@@ -52,7 +52,7 @@ or https://matrix.to/#qutebrowser:libera.chat[via Matrix])
* On Reddit: https://www.reddit.com/r/qutebrowser/[/r/qutebrowser]
* Via https://github.com/qutebrowser/qutebrowser/discussions[GitHub Discussions]
* Using the mailinglist: mailto:qutebrowser@lists.qutebrowser.org[]
-(https://lists.schokokeks.org/mailman/listinfo.cgi/qutebrowser[subscribe])
+(https://listi.jpberlin.de/mailman/listinfo/qutebrowser[subscribe])
Donating
--------
diff --git a/doc/qutebrowser.1.asciidoc b/doc/qutebrowser.1.asciidoc
index bc312f108..a2b2bcec9 100644
--- a/doc/qutebrowser.1.asciidoc
+++ b/doc/qutebrowser.1.asciidoc
@@ -34,7 +34,7 @@ show it.
*'URL'*::
URLs to open on startup (empty as a window separator).
-=== optional arguments
+=== options
*-h*, *--help*::
show this help message and exit
@@ -130,7 +130,7 @@ If you found a bug, use the built-in ':report' command to create a bug report
with all information needed.
If you prefer, you can also write to the
-https://lists.schokokeks.org/mailman/listinfo.cgi/qutebrowser[mailinglist] at
+https://listi.jpberlin.de/mailman/listinfo/qutebrowser[mailinglist] at
mailto:qutebrowser@lists.qutebrowser.org[] instead.
For security bugs, please contact me directly at me@the-compiler.org, GPG ID
@@ -152,9 +152,9 @@ this program. If not, see <https://www.gnu.org/licenses/>.
== RESOURCES
* Website: https://www.qutebrowser.org/
* Mailinglist: mailto:qutebrowser@lists.qutebrowser.org[] /
-https://lists.schokokeks.org/mailman/listinfo.cgi/qutebrowser
+https://listi.jpberlin.de/mailman/listinfo/qutebrowser
* Announce-only mailinglist: mailto:qutebrowser-announce@lists.qutebrowser.org[] /
-https://lists.schokokeks.org/mailman/listinfo.cgi/qutebrowser-announce
+https://listi.jpberlin.de/mailman/listinfo/qutebrowser-announce
* IRC: link:ircs://irc.libera.chat:6697/#qutebrowser[`#qutebrowser`] on
https://libera.chat/[Libera Chat] (https://web.libera.chat/#qutebrowser[webchat],
https://matrix.to/#qutebrowser:libera.chat[via Matrix])
diff --git a/doc/userscripts.asciidoc b/doc/userscripts.asciidoc
index 5ff358ee0..4539a0ca8 100644
--- a/doc/userscripts.asciidoc
+++ b/doc/userscripts.asciidoc
@@ -56,6 +56,7 @@ In `command` mode:
- `QUTE_TITLE`: The title of the current page.
- `QUTE_SELECTED_TEXT`: The text currently selected on the page.
- `QUTE_COUNT`: The `count` from the spawn command running the userscript.
+- `QUTE_TAB_INDEX`: The current tab's index.
In `hints` mode:
diff --git a/misc/requirements/requirements-check-manifest.txt b/misc/requirements/requirements-check-manifest.txt
index bd32971c2..63f9eddc5 100644
--- a/misc/requirements/requirements-check-manifest.txt
+++ b/misc/requirements/requirements-check-manifest.txt
@@ -1,9 +1,8 @@
# This file is automatically generated by scripts/dev/recompile_requirements.py
build==0.7.0
-check-manifest==0.47
+check-manifest==0.48
packaging==21.3
pep517==0.12.0
-pyparsing==3.0.6
-toml==0.10.2
-tomli==2.0.0
+pyparsing==3.0.7
+tomli==2.0.1
diff --git a/misc/requirements/requirements-dev.txt b/misc/requirements/requirements-dev.txt
index dc2041598..9f7f5074b 100644
--- a/misc/requirements/requirements-dev.txt
+++ b/misc/requirements/requirements-dev.txt
@@ -5,41 +5,39 @@ build==0.7.0
bump2version==1.0.1
certifi==2021.10.8
cffi==1.15.0
-charset-normalizer==2.0.9
+charset-normalizer==2.0.12
colorama==0.4.4
-cryptography==36.0.1
-Deprecated==1.2.13
+cryptography==36.0.2
docutils==0.18.1
-github3.py==3.0.0
+github3.py==3.2.0
hunter==3.4.3
idna==3.3
-importlib-metadata==4.10.0
+importlib-metadata==4.11.3
jeepney==0.7.1
-jwcrypto==1.0
keyring==23.5.0
manhole==1.8.0
packaging==21.3
pep517==0.12.0
pkginfo==1.8.2
pycparser==2.21
-Pygments==2.11.1
+Pygments==2.11.2
+PyJWT==2.3.0
Pympler==1.0.1
-pyparsing==3.0.6
+pyparsing==3.0.7
PyQt-builder==1.12.2
python-dateutil==2.8.2
-readme-renderer==32.0
-requests==2.26.0
+readme-renderer==34.0
+requests==2.27.1
requests-toolbelt==0.9.1
-rfc3986==1.5.0
+rfc3986==2.0.0
SecretStorage==3.3.1
-sip==6.5.0
+sip==6.5.1
six==1.16.0
toml==0.10.2
-tomli==2.0.0
-tqdm==4.62.3
-twine==3.7.1
+tomli==2.0.1
+tqdm==4.63.1
+twine==3.8.0
uritemplate==4.1.1
-# urllib3==1.26.7
+# urllib3==1.26.9
webencodings==0.5.1
-wrapt==1.13.3
zipp==3.7.0
diff --git a/misc/requirements/requirements-flake8.txt b/misc/requirements/requirements-flake8.txt
index 2f13b4733..8a7428933 100644
--- a/misc/requirements/requirements-flake8.txt
+++ b/misc/requirements/requirements-flake8.txt
@@ -2,9 +2,9 @@
attrs==21.4.0
flake8==4.0.1
-flake8-bugbear==21.11.29
+flake8-bugbear==22.3.23
flake8-builtins==1.5.3
-flake8-comprehensions==3.7.0
+flake8-comprehensions==3.8.0
flake8-copyright==0.2.2
flake8-debugger==4.0.0
flake8-deprecated==1.3
@@ -15,7 +15,7 @@ flake8-plugin-utils==1.3.2
flake8-polyfill==1.0.2
flake8-pytest-style==1.6.0
flake8-string-format==0.3.0
-flake8-tidy-imports==4.5.0
+flake8-tidy-imports==4.6.0
flake8-tuple==0.4.1
mccabe==0.6.1
pep8-naming==0.12.1
diff --git a/misc/requirements/requirements-mypy.txt b/misc/requirements/requirements-mypy.txt
index 0b40fa31f..d8fbba5ee 100644
--- a/misc/requirements/requirements-mypy.txt
+++ b/misc/requirements/requirements-mypy.txt
@@ -1,19 +1,19 @@
# This file is automatically generated by scripts/dev/recompile_requirements.py
chardet==4.0.0
-diff-cover==6.4.4
-importlib-metadata==4.10.0
-importlib-resources==5.4.0
-Jinja2==3.0.3
-lxml==4.7.1
-MarkupSafe==2.0.1
-mypy==0.930
+diff-cover==6.4.5
+importlib-metadata==4.11.3
+importlib-resources==5.6.0
+Jinja2==3.1.1
+lxml==4.8.0
+MarkupSafe==2.1.1
+mypy==0.942
mypy-extensions==0.4.3
pluggy==1.0.0
-Pygments==2.11.1
+Pygments==2.11.2
PyQt5-stubs==5.15.2.0
-tomli==2.0.0
-types-dataclasses==0.6.2
-types-PyYAML==6.0.1
-typing_extensions==4.0.1
+tomli==2.0.1
+types-dataclasses==0.6.4
+types-PyYAML==6.0.5
+typing_extensions==4.1.1
zipp==3.7.0
diff --git a/misc/requirements/requirements-pip.txt b/misc/requirements/requirements-pip.txt
deleted file mode 100644
index aba943496..000000000
--- a/misc/requirements/requirements-pip.txt
+++ /dev/null
@@ -1,8 +0,0 @@
-# This file is automatically generated by scripts/dev/recompile_requirements.py
-
-appdirs==1.4.4
-packaging==20.4
-pyparsing==2.4.7
-setuptools==47.3.1
-six==1.15.0
-wheel==0.34.2
diff --git a/misc/requirements/requirements-pyinstaller.txt b/misc/requirements/requirements-pyinstaller.txt
index b7c84e3be..d7e46505c 100644
--- a/misc/requirements/requirements-pyinstaller.txt
+++ b/misc/requirements/requirements-pyinstaller.txt
@@ -1,5 +1,5 @@
# This file is automatically generated by scripts/dev/recompile_requirements.py
altgraph==0.17.2
-pyinstaller==4.7
-pyinstaller-hooks-contrib==2021.4
+pyinstaller==4.10
+pyinstaller-hooks-contrib==2022.3
diff --git a/misc/requirements/requirements-pylint.txt b/misc/requirements/requirements-pylint.txt
index 2ea50cb8f..3e5ebea9e 100644
--- a/misc/requirements/requirements-pylint.txt
+++ b/misc/requirements/requirements-pylint.txt
@@ -1,30 +1,30 @@
# This file is automatically generated by scripts/dev/recompile_requirements.py
-astroid==2.9.1
+astroid==2.11.2
certifi==2021.10.8
cffi==1.15.0
-charset-normalizer==2.0.9
-cryptography==36.0.1
-Deprecated==1.2.13
+charset-normalizer==2.0.12
+cryptography==36.0.2
+dill==0.3.4
future==0.18.2
-github3.py==3.0.0
+github3.py==3.2.0
idna==3.3
isort==5.10.1
-jwcrypto==1.0
lazy-object-proxy==1.7.1
-mccabe==0.6.1
+mccabe==0.7.0
pefile==2021.9.3
-platformdirs==2.4.1 ; python_version>="3.7"
+platformdirs==2.5.1 ; python_version>="3.7"
pycparser==2.21
-pylint==2.12.2
+PyJWT==2.3.0
+pylint==2.13.3
python-dateutil==2.8.2
./scripts/dev/pylint_checkers
-requests==2.26.0
+requests==2.27.1
six==1.16.0
-toml==0.10.2
-typed-ast==1.5.1 ; python_version<"3.8"
-typing_extensions==4.0.1
+tomli==2.0.1
+typed-ast==1.5.2 ; python_version<"3.8"
+typing_extensions==4.1.1
uritemplate==4.1.1
-# urllib3==1.26.7
-wrapt==1.13.3
+# urllib3==1.26.9
+wrapt==1.14.0
platformdirs==2.4.0 ; python_version=="3.6.*"
diff --git a/misc/requirements/requirements-pylint.txt-raw b/misc/requirements/requirements-pylint.txt-raw
index 273d07cf4..52633ec1a 100644
--- a/misc/requirements/requirements-pylint.txt-raw
+++ b/misc/requirements/requirements-pylint.txt-raw
@@ -11,9 +11,6 @@ pefile
# Already included via test requirements
#@ ignore: urllib3
-# For pylint_checkers
-#@ pip_args: --use-feature=in-tree-build
-
# Python 3.6
#@ markers: platformdirs python_version>="3.7"
#@ add: platformdirs==2.4.0 ; python_version=="3.6.*"
diff --git a/misc/requirements/requirements-pyqt-5.12.txt b/misc/requirements/requirements-pyqt-5.12.txt
index 890306127..c49484a2d 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.9.0
+PyQt5-sip==12.9.1
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 5f4da4758..59d78862f 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.9.0
+PyQt5-sip==12.9.1
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 9ce643666..ac7c0a028 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.9.0
+PyQt5-sip==12.9.1
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 b111a93f3..6164b89ca 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.9.0
+PyQt5-sip==12.9.1
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 3a3110c8b..03f016177 100644
--- a/misc/requirements/requirements-pyqt-5.15.txt
+++ b/misc/requirements/requirements-pyqt-5.15.txt
@@ -2,6 +2,6 @@
PyQt5==5.15.6 # rq.filter: < 5.16
PyQt5-Qt5==5.15.2
-PyQt5-sip==12.9.0
+PyQt5-sip==12.9.1
PyQtWebEngine==5.15.5 # rq.filter: < 5.16
PyQtWebEngine-Qt5==5.15.2
diff --git a/misc/requirements/requirements-pyqt.txt b/misc/requirements/requirements-pyqt.txt
index 3953d27b3..bf667ac97 100644
--- a/misc/requirements/requirements-pyqt.txt
+++ b/misc/requirements/requirements-pyqt.txt
@@ -2,6 +2,6 @@
PyQt5==5.15.6
PyQt5-Qt5==5.15.2
-PyQt5-sip==12.9.0
+PyQt5-sip==12.9.1
PyQtWebEngine==5.15.5
PyQtWebEngine-Qt5==5.15.2
diff --git a/misc/requirements/requirements-pyroma.txt b/misc/requirements/requirements-pyroma.txt
index cdb3b388f..a78ba8560 100644
--- a/misc/requirements/requirements-pyroma.txt
+++ b/misc/requirements/requirements-pyroma.txt
@@ -1,10 +1,10 @@
# This file is automatically generated by scripts/dev/recompile_requirements.py
certifi==2021.10.8
-charset-normalizer==2.0.9
+charset-normalizer==2.0.12
docutils==0.18.1
idna==3.3
-Pygments==2.11.1
-pyroma==3.2
-requests==2.26.0
-urllib3==1.26.7
+Pygments==2.11.2
+pyroma==3.3
+requests==2.27.1
+urllib3==1.26.9
diff --git a/misc/requirements/requirements-qutebrowser.txt-raw b/misc/requirements/requirements-qutebrowser.txt-raw
index 139d599f8..b260fa16c 100644
--- a/misc/requirements/requirements-qutebrowser.txt-raw
+++ b/misc/requirements/requirements-qutebrowser.txt-raw
@@ -14,12 +14,18 @@ adblock # Improved adblocking
importlib-metadata # Determining PyQt version
typing_extensions # from importlib-metadata
-#@ markers: importlib-resources python_version<"3.9"
+#@ markers: importlib-resources python_version=="3.7.*" or python_version=="3.8.*"
#@ markers: importlib-metadata python_version=="3.7.*"
#@ markers: typing_extensions python_version<"3.8"
#@ markers: dataclasses python_version<"3.7"
# Python 3.6
+#@ add: importlib-resources<5.6.0 ; python_version=="3.6.*"
#@ add: importlib-metadata<4.9 ; python_version=="3.6.*"
+#
#@ markers: zipp python_version>="3.7"
#@ add: zipp<3.7 ; python_version=="3.6.*"
+#@ markers: MarkupSafe python_version>="3.7"
+#@ add: MarkupSafe<2.1.0 ; python_version=="3.6.*"
+#@ markers: Jinja2 python_version>="3.7"
+#@ add: Jinja2<3.1.0 ; python_version=="3.6.*"
diff --git a/misc/requirements/requirements-sphinx.txt b/misc/requirements/requirements-sphinx.txt
index 687b1cd00..bec429e04 100644
--- a/misc/requirements/requirements-sphinx.txt
+++ b/misc/requirements/requirements-sphinx.txt
@@ -3,23 +3,25 @@
alabaster==0.7.12
Babel==2.9.1
certifi==2021.10.8
-charset-normalizer==2.0.9
+charset-normalizer==2.0.12
docutils==0.17.1
idna==3.3
imagesize==1.3.0
-Jinja2==3.0.3
-MarkupSafe==2.0.1
+importlib-metadata==4.11.3
+Jinja2==3.1.1
+MarkupSafe==2.1.1
packaging==21.3
-Pygments==2.11.1
-pyparsing==3.0.6
-pytz==2021.3
-requests==2.26.0
+Pygments==2.11.2
+pyparsing==3.0.7
+pytz==2022.1
+requests==2.27.1
snowballstemmer==2.2.0
-Sphinx==4.3.2
+Sphinx==4.5.0
sphinxcontrib-applehelp==1.0.2
sphinxcontrib-devhelp==1.0.2
sphinxcontrib-htmlhelp==2.0.0
sphinxcontrib-jsmath==1.0.1
sphinxcontrib-qthelp==1.0.3
sphinxcontrib-serializinghtml==1.1.5
-urllib3==1.26.7
+urllib3==1.26.9
+zipp==3.7.0
diff --git a/misc/requirements/requirements-tests-bleeding.txt b/misc/requirements/requirements-tests-bleeding.txt
index d2a7fcfb6..72d6ad083 100644
--- a/misc/requirements/requirements-tests-bleeding.txt
+++ b/misc/requirements/requirements-tests-bleeding.txt
@@ -34,6 +34,7 @@ adblock
## qutebrowser dependencies
git+https://github.com/pallets/jinja.git
-git+https://github.com/yaml/pyyaml.git
+# Problematic: https://github.com/yaml/pyyaml/issues/601
+PyYAML
git+https://github.com/tartley/colorama.git
git+https://github.com/pyparsing/pyparsing.git
diff --git a/misc/requirements/requirements-tests.txt b/misc/requirements/requirements-tests.txt
index 7d17cc661..787d2791b 100644
--- a/misc/requirements/requirements-tests.txt
+++ b/misc/requirements/requirements-tests.txt
@@ -3,62 +3,74 @@
attrs==21.4.0
beautifulsoup4==4.10.0
certifi==2021.10.8
-charset-normalizer==2.0.9
-cheroot==8.5.2
-click==8.0.3
-coverage==6.2
-EasyProcess==0.3
+charset-normalizer==2.0.12
+cheroot==8.6.0
+click==8.1.0 ; python_version>="3.7"
+coverage==6.3.2 ; python_version>="3.7"
execnet==1.9.0
-filelock==3.4.2 ; python_version>="3.7"
-Flask==2.0.2
+filelock==3.6.0 ; python_version>="3.7"
+Flask==2.1.0 ; python_version>="3.7"
glob2==0.7
hunter==3.4.3
-hypothesis==6.34.1 ; python_version>="3.7"
+hypothesis==6.40.0 ; python_version>="3.7"
icdiff==2.0.4
idna==3.3
+importlib-metadata==4.11.3 ; python_version=="3.7.*"
iniconfig==1.1.1
-itsdangerous==2.0.1
+itsdangerous==2.1.2 ; python_version>="3.7"
jaraco.functools==3.5.0 ; python_version>="3.7"
-# Jinja2==3.0.3
-Mako==1.1.6
+# Jinja2==3.1.1
+Mako==1.2.0 ; python_version>="3.7"
manhole==1.8.0
-# MarkupSafe==2.0.1
+# MarkupSafe==2.1.1
more-itertools==8.12.0
packaging==21.3
parse==1.19.0
-parse-type==0.5.2
+parse-type==0.6.0
pluggy==1.0.0
pprintpp==0.4.0
py==1.11.0
py-cpuinfo==8.0.0
-Pygments==2.11.1
-pyparsing==3.0.6
-pytest==6.2.5
+Pygments==2.11.2
+pyparsing==3.0.7
+pytest==7.1.1 ; python_version>="3.7"
pytest-bdd==4.1.0
pytest-benchmark==3.4.1
pytest-cov==3.0.0
pytest-forked==1.4.0
pytest-icdiff==0.5
pytest-instafail==0.4.2
-pytest-mock==3.6.1
+pytest-mock==3.7.0 ; python_version>="3.7"
pytest-qt==4.0.2
pytest-repeat==0.9.1
pytest-rerunfailures==10.2
pytest-xdist==2.5.0
pytest-xvfb==2.0.0
-PyVirtualDisplay==2.2
-requests==2.26.0
+PyVirtualDisplay==3.0
+requests==2.27.1
requests-file==1.5.1
six==1.16.0
sortedcontainers==2.4.0
soupsieve==2.3.1
-tldextract==3.1.2
+tldextract==3.2.0 ; python_version>="3.7"
toml==0.10.2
-tomli==2.0.0 ; python_version>="3.7"
-urllib3==1.26.7
+tomli==2.0.1 ; python_version>="3.7"
+urllib3==1.26.9
vulture==2.3
-Werkzeug==2.0.2
+Werkzeug==2.1.0 ; python_version>="3.7"
+zipp==3.7.0 ; python_version>="3.7"
jaraco.functools<3.5 ; python_version=="3.6.*"
tomli<2 ; python_version=="3.6.*"
filelock==3.4.1 ; python_version=="3.6.*"
hypothesis<6.32 ; python_version=="3.6.*"
+coverage<6.3 ; python_version=="3.6.*"
+pytest-mock<3.7 ; python_version=="3.6.*"
+itsdangerous<2.1.0 ; python_version=="3.6.*"
+tldextract<3.2.0 ; python_version=="3.6.*"
+Mako<1.2.0 ; python_version=="3.6.*"
+pytest<7.1.0 ; python_version=="3.6.*"
+click<8.1.0 ; python_version=="3.6.*"
+Flask<2.1.0 ; python_version=="3.6.*"
+Werkzeug<2.1.0 ; python_version=="3.6.*"
+zipp<3.7 ; python_version=="3.6.*"
+importlib-metadata<4.9 ; python_version=="3.6.*"
diff --git a/misc/requirements/requirements-tests.txt-raw b/misc/requirements/requirements-tests.txt-raw
index ff66280ff..6338a1a97 100644
--- a/misc/requirements/requirements-tests.txt-raw
+++ b/misc/requirements/requirements-tests.txt-raw
@@ -45,3 +45,25 @@ tldextract
#@ add: filelock==3.4.1 ; python_version=="3.6.*"
#@ markers: hypothesis python_version>="3.7"
#@ add: hypothesis<6.32 ; python_version=="3.6.*"
+#@ markers: coverage python_version>="3.7"
+#@ add: coverage<6.3 ; python_version=="3.6.*"
+#@ markers: pytest-mock python_version>="3.7"
+#@ add: pytest-mock<3.7 ; python_version=="3.6.*"
+#@ markers: itsdangerous python_version>="3.7"
+#@ add: itsdangerous<2.1.0 ; python_version=="3.6.*"
+#@ markers: tldextract python_version>="3.7"
+#@ add: tldextract<3.2.0 ; python_version=="3.6.*"
+#@ markers: Mako python_version>="3.7"
+#@ add: Mako<1.2.0 ; python_version=="3.6.*"
+#@ markers: pytest python_version>="3.7"
+#@ add: pytest<7.1.0 ; python_version=="3.6.*"
+#@ markers: click python_version>="3.7"
+#@ add: click<8.1.0 ; python_version=="3.6.*"
+#@ markers: Flask python_version>="3.7"
+#@ add: Flask<2.1.0 ; python_version=="3.6.*"
+#@ markers: Werkzeug python_version>="3.7"
+#@ add: Werkzeug<2.1.0 ; python_version=="3.6.*"
+#@ markers: zipp python_version>="3.7"
+#@ add: zipp<3.7 ; python_version=="3.6.*"
+#@ markers: importlib-metadata python_version=="3.7.*"
+#@ add: importlib-metadata<4.9 ; python_version=="3.6.*"
diff --git a/misc/requirements/requirements-tox.txt b/misc/requirements/requirements-tox.txt
index d1a85cebe..a87519740 100644
--- a/misc/requirements/requirements-tox.txt
+++ b/misc/requirements/requirements-tox.txt
@@ -1,19 +1,20 @@
# This file is automatically generated by scripts/dev/recompile_requirements.py
distlib==0.3.4
-filelock==3.4.2 ; python_version>="3.7"
+filelock==3.6.0 ; python_version>="3.7"
packaging==21.3
-pip==21.3.1
-platformdirs==2.4.1 ; python_version>="3.7"
+pip==22.0.4 ; python_version>="3.7"
+platformdirs==2.5.1 ; python_version>="3.7"
pluggy==1.0.0
py==1.11.0
-pyparsing==3.0.6
-setuptools==60.2.0 ; python_version>="3.7"
+pyparsing==3.0.7
+setuptools==61.2.0 ; python_version>="3.7"
six==1.16.0
toml==0.10.2
tox==3.24.5
-virtualenv==20.13.0
+virtualenv==20.14.0
wheel==0.37.1
setuptools<60 ; python_version=="3.6.*"
filelock==3.4.1 ; python_version=="3.6.*"
platformdirs==2.4.0 ; python_version=="3.6.*"
+pip==21.3.1 ; python_version=="3.6.*"
diff --git a/misc/requirements/requirements-tox.txt-raw b/misc/requirements/requirements-tox.txt-raw
index 9b1968480..2a9f30c5a 100644
--- a/misc/requirements/requirements-tox.txt-raw
+++ b/misc/requirements/requirements-tox.txt-raw
@@ -8,3 +8,5 @@ wheel
#@ add: filelock==3.4.1 ; python_version=="3.6.*"
#@ markers: platformdirs python_version>="3.7"
#@ add: platformdirs==2.4.0 ; python_version=="3.6.*"
+#@ markers: pip python_version>="3.7"
+#@ add: pip==21.3.1 ; python_version=="3.6.*"
diff --git a/misc/userscripts/README.md b/misc/userscripts/README.md
index c67bf8c6f..8d65d83fd 100644
--- a/misc/userscripts/README.md
+++ b/misc/userscripts/README.md
@@ -85,6 +85,13 @@ The following userscripts can be found on their own repositories.
various small userscripts written in Raku.
- [bitwarden-rofi](https://github.com/haztecaso/bitwarden-rofi), rofi wrapper for bitwarden cli
interface using gpg instead of keyctl.
+- [yomichad](https://github.com/potamides/yomichad): Japanese pop-up dictionary
+ for looking up readings and definitions of currently selected words, kanji
+ and names
+- [~palb91/qutescripts](https://git.sr.ht/~palb91/qutescripts): A list of
+ personal userscripts for qutebrowser (`domcycle`: settings per domain,
+ `gitclone`, `jsdownload`: smart download, and `substiqute`: bash-like url
+ substitution)
[Zotero]: https://www.zotero.org/
[Pocket]: https://getpocket.com/
diff --git a/misc/userscripts/cast b/misc/userscripts/cast
index df74fe97e..ec703d5fb 100755
--- a/misc/userscripts/cast
+++ b/misc/userscripts/cast
@@ -20,6 +20,14 @@
#
# Dependencies
# - castnow, https://github.com/xat/castnow
+# - youtube-dl (https://youtube-dl.org/) or a drop-in replacement such as
+# yt-dlp (https://github.com/yt-dlp/yt-dlp).
+#
+# Configuration:
+# This script looks at the optional QUTE_CAST_YTDL_PROGRAM environment
+# variable (if it exists) to decide which program to use for downloading
+# videos. If specified, this should be youtube-dl or a drop-in replacement
+# for it.
#
# Author
# Simon Désaulniers <sim.desaulniers@gmail.com>
@@ -133,23 +141,34 @@ echo "jseval -q $(printjs)" >> "$QUTE_FIFO"
tmpdir=$(mktemp -d)
file_to_cast=${tmpdir}/qutecast
-program_=$(command -v castnow)
+cast_program=$(command -v castnow)
+
+# pick a ytdl program
+for p in "$QUTE_CAST_YTDL_PROGRAM" yt-dlp youtube-dl; do
+ ytdl_program=$(command -v -- "$p")
+ [ "$ytdl_program" == "" ] || break
+done
-if [[ "${program_}" == "" ]]; then
- msg error "castnow can't be found..."
+if [[ "${cast_program}" == "" ]]; then
+ msg error "castnow can't be found"
+ exit 1
+fi
+if [[ "${ytdl_program}" == "" ]]; then
+ msg error "youtube-dl or a drop-in replacement can't be found in PATH, and no installed program " \
+ "specified in QUTE_CAST_YTDL_PROGRAM (currently \"$QUTE_CAST_YTDL_PROGRAM\")"
exit 1
fi
# kill any running instance of castnow
-pkill -f "${program_}"
+pkill -f -- "${cast_program}"
# start youtube download in stream mode (-o -) into temporary file
-youtube-dl -qo - "$1" > "${file_to_cast}" &
+"${ytdl_program}" -qo - "$1" > "${file_to_cast}" &
ytdl_pid=$!
msg info "Casting $1" >> "$QUTE_FIFO"
# start castnow in stream mode to cast on ChromeCast
-tail -F "${file_to_cast}" | ${program_} -
+tail -F "${file_to_cast}" | ${cast_program} -
# cleanup remaining background process and file on disk
kill ${ytdl_pid}
diff --git a/misc/userscripts/qute-bitwarden b/misc/userscripts/qute-bitwarden
index c6407127a..f8629d1dc 100755
--- a/misc/userscripts/qute-bitwarden
+++ b/misc/userscripts/qute-bitwarden
@@ -6,7 +6,7 @@
# This file is part of qutebrowser.
#
# qutebrowser is free software: you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published bjy
+# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
@@ -62,6 +62,8 @@ argument_parser = argparse.ArgumentParser(
argument_parser.add_argument('url', nargs='?', default=os.getenv('QUTE_URL'))
argument_parser.add_argument('--dmenu-invocation', '-d', default='rofi -dmenu -i -p Bitwarden',
help='Invocation used to execute a dmenu-provider')
+argument_parser.add_argument('--password-prompt-invocation', '-p', default='rofi -dmenu -p "Master Password" -password -lines 0',
+ help='Invocation used to prompt the user for their Bitwarden password')
argument_parser.add_argument('--no-insert-mode', '-n', dest='insert_mode', action='store_false',
help="Don't automatically enter insert mode")
argument_parser.add_argument('--totp', '-t', action='store_true',
@@ -98,16 +100,12 @@ def qute_command(command):
fifo.flush()
-def ask_password():
- process = subprocess.run([
- 'rofi',
- '-dmenu',
- '-p',
- 'Master Password',
- '-password',
- '-lines',
- '0',
- ], universal_newlines=True, stdout=subprocess.PIPE)
+def ask_password(password_prompt_invocation):
+ process = subprocess.run(
+ shlex.split(password_prompt_invocation),
+ universal_newlines=True,
+ stdout=subprocess.PIPE,
+ )
if process.returncode > 0:
raise Exception('Could not unlock vault')
master_pass = process.stdout.strip()
@@ -117,10 +115,10 @@ def ask_password():
).strip()
-def get_session_key(auto_lock):
+def get_session_key(auto_lock, password_prompt_invocation):
if auto_lock == 0:
subprocess.call(['keyctl', 'purge', 'user', 'bw_session'])
- return ask_password()
+ return ask_password(password_prompt_invocation)
else:
process = subprocess.run(
['keyctl', 'request', 'user', 'bw_session'],
@@ -129,7 +127,7 @@ def get_session_key(auto_lock):
)
key_id = process.stdout.strip()
if process.returncode > 0:
- session = ask_password()
+ session = ask_password(password_prompt_invocation)
if not session:
raise Exception('Could not unlock vault')
key_id = subprocess.check_output(
@@ -145,8 +143,8 @@ def get_session_key(auto_lock):
).strip()
-def pass_(domain, encoding, auto_lock):
- session_key = get_session_key(auto_lock)
+def pass_(domain, encoding, auto_lock, password_prompt_invocation):
+ session_key = get_session_key(auto_lock, password_prompt_invocation)
process = subprocess.run(
['bw', 'list', 'items', '--session', session_key, '--url', domain],
stdout=subprocess.PIPE,
@@ -166,8 +164,8 @@ def pass_(domain, encoding, auto_lock):
return out
-def get_totp_code(selection_id, domain_name, encoding, auto_lock):
- session_key = get_session_key(auto_lock)
+def get_totp_code(selection_id, domain_name, encoding, auto_lock, password_prompt_invocation):
+ session_key = get_session_key(auto_lock, password_prompt_invocation)
process = subprocess.run(
['bw', 'get', 'totp', '--session', session_key, selection_id],
stdout=subprocess.PIPE,
@@ -224,6 +222,7 @@ def main(arguments):
target,
arguments.io_encoding,
arguments.auto_lock,
+ arguments.password_prompt_invocation,
)
)
if not target_candidates:
@@ -270,7 +269,8 @@ def main(arguments):
selection['id'],
selection['name'],
arguments.io_encoding,
- arguments.auto_lock
+ arguments.auto_lock,
+ arguments.password_prompt_invocation,
)
)
else:
@@ -294,7 +294,8 @@ def main(arguments):
selection['id'],
selection['name'],
arguments.io_encoding,
- arguments.auto_lock
+ arguments.auto_lock,
+ arguments.password_prompt_invocation,
)
)
diff --git a/misc/userscripts/qute-lastpass b/misc/userscripts/qute-lastpass
index d2a72f077..edb48dfdc 100755
--- a/misc/userscripts/qute-lastpass
+++ b/misc/userscripts/qute-lastpass
@@ -6,7 +6,7 @@
# This file is part of qutebrowser.
#
# qutebrowser is free software: you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published bjy
+# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
diff --git a/misc/userscripts/qute-pass b/misc/userscripts/qute-pass
index 76a82e9c3..f410802c8 100755..100644
--- a/misc/userscripts/qute-pass
+++ b/misc/userscripts/qute-pass
@@ -77,6 +77,8 @@ argument_parser.add_argument('--password-store', '-p',
help='Path to your pass password-store (only used in pass-mode)', type=expanded_path)
argument_parser.add_argument('--mode', '-M', choices=['pass', 'gopass'], default="pass",
help='Select mode [gopass] to use gopass instead of the standard pass.')
+argument_parser.add_argument('--prefix', type=str,
+ help='Search only the given subfolder of the store (only used in gopass-mode)')
argument_parser.add_argument('--username-pattern', '-u', default=r'.*/(.+)',
help='Regular expression that matches the username')
argument_parser.add_argument('--username-target', '-U', choices=['path', 'secret'], default='path',
@@ -132,7 +134,10 @@ def find_pass_candidates(domain, unfiltered=False):
candidates = []
if arguments.mode == "gopass":
- all_passwords = subprocess.run(["gopass", "list", "--flat" ], stdout=subprocess.PIPE).stdout.decode("UTF-8").splitlines()
+ gopass_args = ["gopass", "list", "--flat"]
+ if arguments.prefix:
+ gopass_args.append(arguments.prefix)
+ all_passwords = subprocess.run(gopass_args, stdout=subprocess.PIPE).stdout.decode("UTF-8").splitlines()
for password in all_passwords:
if unfiltered or domain in password:
diff --git a/misc/userscripts/ripbang b/misc/userscripts/ripbang
index df9cb2fbc..2f867c838 100755
--- a/misc/userscripts/ripbang
+++ b/misc/userscripts/ripbang
@@ -9,26 +9,22 @@
# :spawn --userscript ripbang amazon maps
#
-from __future__ import print_function
import os, re, requests, sys
-
-try:
- from urllib.parse import unquote
-except ImportError:
- from urllib import unquote
+from urllib.parse import urlparse, parse_qs
for argument in sys.argv[1:]:
bang = '!' + argument
r = requests.get('https://duckduckgo.com/',
- params={'q': bang + ' SEARCHTEXT'})
+ params={'q': bang + ' SEARCHTEXT'},
+ headers={'user-agent': 'qutebrowser ripbang'})
- searchengine = unquote(re.search("url=[^']+", r.text).group(0))
- searchengine = searchengine.replace('url=', '')
- searchengine = searchengine.replace('/l/?kh=-1&uddg=', '')
+ searchengine = re.search("url=([^']+)", r.text).group(1)
+ searchengine = urlparse(searchengine).query
+ searchengine = parse_qs(searchengine)['uddg'][0]
searchengine = searchengine.replace('SEARCHTEXT', '{}')
if os.getenv('QUTE_FIFO'):
with open(os.environ['QUTE_FIFO'], 'w') as fifo:
- fifo.write('set searchengines %s %s' % (bang, searchengine))
+ fifo.write('config-dict-add url.searchengines %s %s' % (bang, searchengine))
else:
print('%s %s' % (bang, searchengine))
diff --git a/qutebrowser/browser/commands.py b/qutebrowser/browser/commands.py
index 395d8e8a4..00d5e521f 100644
--- a/qutebrowser/browser/commands.py
+++ b/qutebrowser/browser/commands.py
@@ -1152,6 +1152,7 @@ class CommandDispatcher:
idx = self._current_index()
if idx != -1:
+ env['QUTE_TAB_INDEX'] = str(idx + 1)
env['QUTE_TITLE'] = self._tabbed_browser.widget.page_title(idx)
# FIXME:qtwebengine: If tab is None, run_async will fail!
diff --git a/qutebrowser/browser/shared.py b/qutebrowser/browser/shared.py
index 41c971642..f195bbf28 100644
--- a/qutebrowser/browser/shared.py
+++ b/qutebrowser/browser/shared.py
@@ -51,7 +51,9 @@ def custom_headers(url):
conf_headers = config.instance.get('content.headers.custom', url=url)
for header, value in conf_headers.items():
- headers[header.encode('ascii')] = value.encode('ascii')
+ encoded_header = header.encode('ascii')
+ encoded_value = b"" if value is None else value.encode('ascii')
+ headers[encoded_header] = encoded_value
accept_language = config.instance.get('content.headers.accept_language',
url=url)
diff --git a/qutebrowser/browser/webengine/webenginetab.py b/qutebrowser/browser/webengine/webenginetab.py
index fa877f560..7d355d10e 100644
--- a/qutebrowser/browser/webengine/webenginetab.py
+++ b/qutebrowser/browser/webengine/webenginetab.py
@@ -1530,7 +1530,6 @@ class WebEngineTab(browsertab.AbstractTab):
displayed.
WORKAROUND for https://bugreports.qt.io/browse/QTBUG-66643
- WORKAROUND for https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=882805
"""
match = re.search(r'"errorCode":"([^"]*)"', html)
if match is None:
@@ -1539,8 +1538,7 @@ class WebEngineTab(browsertab.AbstractTab):
error = match.group(1)
log.webview.error("Load error: {}".format(error))
- missing_jst = 'jstProcess(' in html and 'jstProcess=' not in html
- if js_enabled and not missing_jst:
+ if js_enabled:
return
self._show_error_page(self.url(), error=error)
diff --git a/qutebrowser/browser/webkit/http.py b/qutebrowser/browser/webkit/http.py
index eacb95679..e43d8fdef 100644
--- a/qutebrowser/browser/webkit/http.py
+++ b/qutebrowser/browser/webkit/http.py
@@ -101,8 +101,15 @@ class ContentDisposition:
if defects != [cls._IGNORED_DEFECT]: # type: ignore[comparison-overlap]
raise ContentDispositionError(defects)
- assert isinstance(parsed, email.headerregistry.ContentDispositionHeader), parsed
- return cls(disposition=parsed.content_disposition, params=parsed.params)
+ # https://github.com/python/mypy/issues/12314
+ assert isinstance(
+ parsed, # type: ignore[unreachable]
+ email.headerregistry.ContentDispositionHeader,
+ ), parsed
+ return cls( # type: ignore[unreachable]
+ disposition=parsed.content_disposition,
+ params=parsed.params,
+ )
def filename(self):
"""The filename from the Content-Disposition header or None.
diff --git a/qutebrowser/browser/webkit/webkithistory.py b/qutebrowser/browser/webkit/webkithistory.py
index 4149bda88..6ffe65193 100644
--- a/qutebrowser/browser/webkit/webkithistory.py
+++ b/qutebrowser/browser/webkit/webkithistory.py
@@ -44,7 +44,7 @@ class WebHistoryInterface(QWebHistoryInterface):
"""Required for a QWebHistoryInterface impl, obsoleted by add_url."""
@debugcachestats.register(name='history')
- @functools.lru_cache(maxsize=32768)
+ @functools.lru_cache(maxsize=32768) # noqa: B019
def historyContains(self, url_string):
"""Called by WebKit to determine if a URL is contained in the history.
diff --git a/qutebrowser/completion/completer.py b/qutebrowser/completion/completer.py
index fb61e48fb..8b0de9d8a 100644
--- a/qutebrowser/completion/completer.py
+++ b/qutebrowser/completion/completer.py
@@ -39,7 +39,7 @@ class CompletionInfo:
"""Context passed into all completion functions."""
config: config.Config
- keyconf: config.KeyConfig # pylint: disable=undefined-variable
+ keyconf: config.KeyConfig # pylint: disable=used-before-assignment
win_id: int
cur_tab: 'browsertab.AbstractTab'
diff --git a/qutebrowser/completion/models/miscmodels.py b/qutebrowser/completion/models/miscmodels.py
index d8ebafb29..77072c720 100644
--- a/qutebrowser/completion/models/miscmodels.py
+++ b/qutebrowser/completion/models/miscmodels.py
@@ -103,17 +103,23 @@ def session(*, info=None):
return model
-def _tabs(*, win_id_filter=lambda _win_id: True, add_win_id=True):
+def _tabs(*, win_id_filter=lambda _win_id: True, add_win_id=True, cur_win_id=None):
"""Helper to get the completion model for tabs/other_tabs.
Args:
win_id_filter: A filter function for window IDs to include.
Should return True for all included windows.
add_win_id: Whether to add the window ID to the completion items.
+ cur_win_id: Window ID to be passed from info.win_id
"""
def delete_tab(data):
"""Close the selected tab."""
- win_id, tab_index = data[0].split('/')
+ if cur_win_id is None:
+ win_id, tab_index = data[0].split('/')
+ else:
+ win_id = cur_win_id
+ tab_index = data[0]
+
tabbed_browser = objreg.get('tabbed-browser', scope='window',
window=int(win_id))
tabbed_browser.on_tab_close_requested(int(tab_index) - 1)
@@ -177,13 +183,15 @@ def other_tabs(*, info):
Used for the tab-take command.
"""
- return _tabs(win_id_filter=lambda win_id: win_id != info.win_id)
+ return _tabs(
+ win_id_filter=lambda win_id: win_id != info.win_id,
+ cur_win_id=info.win_id)
def tab_focus(*, info):
"""A model to complete on open tabs in the current window."""
model = _tabs(win_id_filter=lambda win_id: win_id == info.win_id,
- add_win_id=False)
+ add_win_id=False, cur_win_id=info.win_id)
special = [
("last", "Focus the last-focused tab"),
diff --git a/qutebrowser/config/configdata.yml b/qutebrowser/config/configdata.yml
index b0d9c6364..95338b36f 100644
--- a/qutebrowser/config/configdata.yml
+++ b/qutebrowser/config/configdata.yml
@@ -239,6 +239,9 @@ qt.force_platformtheme:
based on the desktop environment.
qt.process_model:
+ renamed: qt.chromium.process_model
+
+qt.chromium.process_model:
type:
name: String
valid_values:
@@ -268,6 +271,9 @@ qt.process_model:
- https://doc.qt.io/qt-5/qtwebengine-features.html#process-models
qt.low_end_device_mode:
+ renamed: qt.chromium.low_end_device_mode
+
+qt.chromium.low_end_device_mode:
type:
name: String
valid_values:
@@ -284,6 +290,39 @@ qt.low_end_device_mode:
This improves the RAM usage of renderer processes, at the expense of
performance.
+qt.chromium.sandboxing:
+ type:
+ name: String
+ valid_values:
+ - enable-all: Enable all available sandboxing mechanisms.
+ - disable-seccomp-bpf: Disable the Seccomp BPF filter sandbox (Linux only).
+ - disable-all: Disable all sandboxing (**not recommended!**).
+ default: enable-all
+ backend: QtWebEngine
+ restart: true
+ no_autoconfig: true # due to it being dangerous
+ # yamllint disable rule:line-length
+ desc: >-
+ What sandboxing mechanisms in Chromium to use.
+
+ Chromium has various sandboxing layers, which should be enabled for normal
+ browser usage. Mainly for testing and development, it's possible to disable
+ individual sandboxing layers via this setting.
+
+ Open `chrome://sandbox` to see the current sandbox status.
+
+ Changing this setting is only recommended if you know what you're doing, as
+ it **disables one of Chromium's security layers**. To avoid sandboxing being
+ accidentally disabled persistently, this setting can only be set via
+ `config.py`, not via `:set`.
+
+ See the Chromium documentation for more details:
+
+ - https://chromium.googlesource.com/chromium/src/\+/HEAD/docs/linux/sandboxing.md[Linux]
+ - https://chromium.googlesource.com/chromium/src/\+/HEAD/docs/design/sandbox.md[Windows]
+ - https://chromium.googlesource.com/chromium/src/\+/HEAD/docs/design/sandbox_faq.md[FAQ (Windows-centric)]
+ # yamllint enable rule:line-length
+
qt.highdpi:
type: Bool
default: false
@@ -616,6 +655,7 @@ content.headers.custom:
valtype:
name: String
encoding: ascii
+ none_ok: true
none_ok: true
supports_pattern: true
desc: Custom headers for qutebrowser HTTP requests.
@@ -1395,12 +1435,16 @@ editor.command:
* `{line0}`: Same as `{line}`, but starting from index 0.
* `{column0}`: Same as `{column}`, but starting from index 0.
-
editor.encoding:
type: Encoding
default: utf-8
desc: Encoding to use for the editor.
+editor.remove_file:
+ default: true
+ type: Bool
+ desc: Delete the temporary file upon closing the editor.
+
## fileselect
fileselect.handler:
@@ -1782,10 +1826,10 @@ input.spatial_navigation:
Enable spatial navigation.
Spatial navigation consists in the ability to navigate between focusable
- elements in a Web page, such as hyperlinks and form controls, by using
- Left, Right, Up and Down arrow keys. For example, if the user presses the
- Right key, heuristics determine whether there is an element he might be
- trying to reach towards the right and which element he probably wants.
+ elements, such as hyperlinks and form controls, on a web page by using the
+ Left, Right, Up and Down arrow keys. For example, if a user presses the
+ Right key, heuristics determine whether there is an element they might be
+ trying to reach towards the right and which element they probably want.
input.media_keys:
default: true
@@ -1810,6 +1854,18 @@ input.match_counts:
Disabling it allows for emacs-like bindings where number keys are passed
through (according to `input.forward_unbound_keys`) instead.
+input.mode_override:
+ default: null
+ type:
+ name: String
+ none_ok: true
+ valid_values:
+ - normal
+ - insert
+ - passthrough
+ supports_pattern: true
+ desc: Mode to change to when focusing on a tab/URL changes.
+
## keyhint
keyhint.blacklist:
@@ -3870,9 +3926,7 @@ bindings.commands:
that single keys can't be bound in this mode.
* passthrough: Similar to insert mode, but passes through all keypresses
- except `<Escape>` to leave the mode. It might be useful to bind
- `<Escape>` to some other key in this mode if you want to be able to send
- an Escape key to the website as well. Note that single keys can't be
+ except `<Shift+Escape>` to leave the mode. Note that single keys can't be
bound in this mode.
* command: Entered when pressing the `:` key in order to enter a command.
diff --git a/qutebrowser/config/configfiles.py b/qutebrowser/config/configfiles.py
index 6f0d0b13c..d97771fad 100644
--- a/qutebrowser/config/configfiles.py
+++ b/qutebrowser/config/configfiles.py
@@ -384,8 +384,8 @@ class YamlMigrations(QObject):
changed = pyqtSignal()
- def __init__(self, settings: _SettingsType,
- parent: QObject = None) -> None:
+ # Note: settings is Any because it's not validated yet.
+ def __init__(self, settings: Any, parent: QObject = None) -> None:
super().__init__(parent)
self._settings = settings
diff --git a/qutebrowser/config/qtargs.py b/qutebrowser/config/qtargs.py
index 2f93b7de5..9e7f2620d 100644
--- a/qutebrowser/config/qtargs.py
+++ b/qutebrowser/config/qtargs.py
@@ -321,12 +321,12 @@ def _qtwebengine_settings_args(versions: version.WebEngineVersions) -> Iterator[
'--force-webrtc-ip-handling-policy='
'disable_non_proxied_udp',
},
- 'qt.process_model': {
+ 'qt.chromium.process_model': {
'process-per-site-instance': None,
'process-per-site': '--process-per-site',
'single-process': '--single-process',
},
- 'qt.low_end_device_mode': {
+ 'qt.chromium.low_end_device_mode': {
'auto': None,
'always': '--enable-low-end-device-mode',
'never': '--disable-low-end-device-mode',
@@ -338,6 +338,11 @@ def _qtwebengine_settings_args(versions: version.WebEngineVersions) -> Iterator[
True: '--force-prefers-reduced-motion',
False: None,
},
+ 'qt.chromium.sandboxing': {
+ 'enable-all': None,
+ 'disable-seccomp-bpf': '--disable-seccomp-filter-sandbox',
+ 'disable-all': '--no-sandbox',
+ }
}
qt_514_ver = utils.VersionNumber(5, 14)
diff --git a/qutebrowser/html/settings.html b/qutebrowser/html/settings.html
index dfbc5c168..b06917fd5 100644
--- a/qutebrowser/html/settings.html
+++ b/qutebrowser/html/settings.html
@@ -65,6 +65,7 @@ input[type="radio"] {
width: min-content;
margin: 0;
border: none;
+ cursor: pointer;
}
label {
@@ -81,6 +82,12 @@ input[type="radio"]:checked + label {
color: #084c88;
}
+.radio-button {
+ position: relative; /* The absolutely positioned element inside this tag (the radio button) gets positioned relative to it. */
+ display: inline-flex;
+ margin: 3px 1px;
+}
+
.setting {
width: 60%;
}
@@ -107,13 +114,39 @@ input[type="radio"]:checked + label {
color: #635d5dcf;
font-size: 80%;
font-style: italic;
+}
+
+.option-description p {
+ margin: 0;
+}
+
+.long-description {
white-space: pre-line;
}
-.radio-button {
- position: relative; /* The absolutely positioned element inside this tag (the radio button) gets positioned relative to it. */
- display: inline-flex;
- margin: 3px 1px;
+details summary > * {
+ display: inline;
+}
+
+details[open] .details {
+ display: none;
+}
+
+summary {
+ margin: .5ex 0;
+ width: fit-content;
+ color: #1887c5;
+ outline: none;
+ font-size: 105%;
+ cursor: pointer;
+}
+
+summary .short-description {
+ color: #635d5dcf;
+}
+
+summary::selection {
+ background-color: inherit;
}
{% endblock %}
@@ -126,35 +159,49 @@ input[type="radio"]:checked + label {
</tr>
{% for option in configdata.DATA.values()|sort(attribute='name') if not option.no_autoconfig %}
<tr>
+ {% set loopIndex = loop.index0 %}
<!-- FIXME: convert to string properly -->
<td class="setting">{{ option.name }}
{% if option.description %}
- <p class="option-description">{{ option.description|e }}</p>
+ {% set description = option.description.split('\n', 1) %}
+ <div class="option-description">
+ {% if description|length > 1 %}
+ <details>
+ <summary>
+ <p class="short-description">{{ description[0]|e }}</p>
+ <span class="details">Details</span>
+ </summary>
+ <p class="long-description">{{ description[1]|e }}</p>
+ </details>
+ {% else %}
+ <p>{{ description[0]|e }}</p>
+ {% endif %}
+ </div>
{% endif %}
</td>
{% if option.typ.valid_values is not none %}
<td class="valid-value">
{% for value in option.typ.valid_values.values %}
- <div class="radio-button">
- <input type="radio" id="input-{{ option.name }}-{{ loop.index0 }}"
- name="{{ option.name }}" value="{{ value }}"
- onclick="cset('{{ option.name }}', this.value)"
- {% if confget(option.name) == value %}
- checked
- {% endif %}>
- <label for="input-{{ option.name }}-{{ loop.index0 }}">
- {{ value }}
- </label>
- </div>
- {% endfor %}
- </td>
+ <div class="radio-button">
+ <input type="radio" id="input-{{ option.name }}-{{ loop.index0 }}"
+ name="{{ option.name }}" value="{{ value }}"
+ onclick="cset('{{ option.name }}', this.value)"
+ {% if confget(option.name) == value %}
+ checked
+ {% endif %}>
+ <label for="input-{{ option.name }}-{{ loop.index0 }}">
+ {{ value }}
+ </label>
+ </div>
+ {% endfor %}
+ </td>
{% else %}
<td class="value">
- <input type="text"
- id="input-{{ option.name }}"
- onblur="cset('{{ option.name }}', this.value)"
- value="{{ confget(option.name) }}">
- </input>
+ <input type="text"
+ id="input-{{ option.name }}"
+ onblur="cset('{{ option.name }}', this.value)"
+ value="{{ confget(option.name) }}">
+ </input>
</td>
{% endif %}
</tr>
diff --git a/qutebrowser/html/warning-webkit.html b/qutebrowser/html/warning-webkit.html
index 975f98c1b..f5cf9bf01 100644
--- a/qutebrowser/html/warning-webkit.html
+++ b/qutebrowser/html/warning-webkit.html
@@ -41,8 +41,8 @@ hopefully help.</p>
notification support was added for Qt 5.13.0.</p>
<p><b>Resource usage</b>: qutebrowser v1.5.0 added the <span
-class="mono">qt.process_model</span> and <span
-class="mono">qt.low_end_device_mode</span> settings which can be used to
+class="mono">qt.chromium.process_model</span> and <span
+class="mono">qt.chromium.low_end_device_mode</span> settings which can be used to
decrease the resource usage of QtWebEngine (but come with other drawbacks).</p>
<p><b>Not trusting Google</b>: Various people have checked the connections made
@@ -78,7 +78,7 @@ security fixes took months to arrive there). You might be better off choosing an
method</a>.</p>
<p><b>White flashing between loads with a custom stylesheet</b>: This doesn't
-seem to happen with <span class="mono">qt.process_model = single-process</span>
+seem to happen with <span class="mono">qt.chromium.process_model = single-process</span>
set. However, note that that setting comes with decreased security and
stability, but QtWebKit doesn't have any process isolation at all.</p>
{% endblock %}
diff --git a/qutebrowser/mainwindow/prompt.py b/qutebrowser/mainwindow/prompt.py
index c8cbe572b..29ea9a45f 100644
--- a/qutebrowser/mainwindow/prompt.py
+++ b/qutebrowser/mainwindow/prompt.py
@@ -536,9 +536,11 @@ class _BasePrompt(QWidget):
self.KEY_MODE.name)
labels = []
+ has_bindings = False
for cmd, text in self._allowed_commands():
bindings = all_bindings.get(cmd, [])
if bindings:
+ has_bindings = True
binding = None
preferred = ['<enter>', '<escape>']
for pref in preferred:
@@ -547,8 +549,11 @@ class _BasePrompt(QWidget):
if binding is None:
binding = bindings[0]
key_label = QLabel('<b>{}</b>'.format(html.escape(binding)))
- text_label = QLabel(text)
- labels.append((key_label, text_label))
+ else:
+ key_label = QLabel(f'<b>unbound</b> (<tt>{html.escape(cmd)}</tt>)')
+
+ text_label = QLabel(text)
+ labels.append((key_label, text_label))
for i, (key_label, text_label) in enumerate(labels):
self._key_grid.addWidget(key_label, i, 0)
@@ -559,6 +564,14 @@ class _BasePrompt(QWidget):
self._vbox.addLayout(self._key_grid)
+ if not has_bindings:
+ label = QLabel(
+ "<b>Note:</b> You seem to have unbound all keys for this prompt "
+ f"(<tt>{self.KEY_MODE.name}</tt> key mode)."
+ "<br/>Run <tt>qutebrowser :CMD</tt> with a command from above to "
+ "close this prompt, then fix this in your config.")
+ self._vbox.addWidget(label)
+
def _check_save_support(self, save):
if save:
raise UnsupportedOperationError("Saving answers is only possible "
diff --git a/qutebrowser/mainwindow/tabbedbrowser.py b/qutebrowser/mainwindow/tabbedbrowser.py
index c3f06e185..a96f6d583 100644
--- a/qutebrowser/mainwindow/tabbedbrowser.py
+++ b/qutebrowser/mainwindow/tabbedbrowser.py
@@ -223,6 +223,10 @@ class TabbedBrowser(QWidget):
# https://bugreports.qt.io/browse/QTBUG-65223
self.cur_load_finished.connect(self._leave_modes_on_load)
+ # handle mode_override
+ self.current_tab_changed.connect(lambda tab: self._mode_override(tab.url()))
+ self.cur_url_changed.connect(self._mode_override)
+
# This init is never used, it is immediately thrown away in the next
# line.
self.undo_stack: UndoStackType = collections.deque()
@@ -509,8 +513,7 @@ class TabbedBrowser(QWidget):
last_close_urlstr = urls[last_close].toString().rstrip('/')
first_tab_urlstr = first_tab_url.toString().rstrip('/')
last_close_url_used = first_tab_urlstr == last_close_urlstr
- use_current_tab = (only_one_tab_open and no_history and
- last_close_url_used)
+ use_current_tab = no_history and last_close_url_used
entries = self.undo_stack[-depth]
del self.undo_stack[-depth]
@@ -777,6 +780,23 @@ class TabbedBrowser(QWidget):
if not self.widget.page_title(idx):
self.widget.set_page_title(idx, url.toDisplayString())
+ def _mode_override(self, url: QUrl) -> None:
+ """Override mode if url matches pattern.
+
+ Args:
+ url: The QUrl to match for
+ """
+ if not url.isValid():
+ return
+ mode = config.instance.get('input.mode_override', url=url)
+ if mode:
+ log.modes.debug(f"Mode change to {mode} triggered for url {url}")
+ modeman.enter(
+ self._win_id,
+ usertypes.KeyMode[mode],
+ reason='mode_override',
+ )
+
@pyqtSlot(browsertab.AbstractTab)
def _on_icon_changed(self, tab):
"""Set the icon of a tab.
diff --git a/qutebrowser/mainwindow/tabwidget.py b/qutebrowser/mainwindow/tabwidget.py
index 0e95b7745..511c2c309 100644
--- a/qutebrowser/mainwindow/tabwidget.py
+++ b/qutebrowser/mainwindow/tabwidget.py
@@ -410,6 +410,15 @@ class TabBar(QTabBar):
config.instance.changed.connect(self._on_config_changed)
self._set_icon_size()
QTimer.singleShot(0, self.maybe_hide)
+ self._minimum_tab_size_hint_helper = functools.lru_cache(maxsize=2**9)(
+ self._minimum_tab_size_hint_helper_uncached
+ )
+ debugcachestats.register(name=f'tab width cache (win_id={win_id})')(
+ self._minimum_tab_size_hint_helper
+ )
+ self._minimum_tab_height = functools.lru_cache(maxsize=1)(
+ self._minimum_tab_height_uncached
+ )
def __repr__(self):
return utils.get_repr(self, count=self.count())
@@ -575,11 +584,9 @@ class TabBar(QTabBar):
icon_width, ellipsis,
pinned)
- @debugcachestats.register(name='tab width cache')
- @functools.lru_cache(maxsize=2**9)
- def _minimum_tab_size_hint_helper(self, tab_text: str,
- icon_width: int,
- ellipsis: bool, pinned: bool) -> QSize:
+ def _minimum_tab_size_hint_helper_uncached(self, tab_text: str,
+ icon_width: int,
+ ellipsis: bool, pinned: bool) -> QSize:
"""Helper function to cache tab results.
Config values accessed in here should be added to _on_config_changed to
@@ -610,8 +617,7 @@ class TabBar(QTabBar):
width = max(min_width, width)
return QSize(width, height)
- @functools.lru_cache(maxsize=1)
- def _minimum_tab_height(self):
+ def _minimum_tab_height_uncached(self):
padding = config.cache['tabs.padding']
return self.fontMetrics().height() + padding.top + padding.bottom
diff --git a/qutebrowser/misc/crashsignal.py b/qutebrowser/misc/crashsignal.py
index f7578a07f..d94d3ec54 100644
--- a/qutebrowser/misc/crashsignal.py
+++ b/qutebrowser/misc/crashsignal.py
@@ -100,7 +100,7 @@ class CrashHandler(QObject):
# There's no log file, so we can use this to display crashes to
# the user on the next start.
self._init_crashlogfile()
- except OSError:
+ except (OSError, UnicodeDecodeError):
log.init.exception("Error while handling crash log file!")
self._init_crashlogfile()
diff --git a/qutebrowser/misc/debugcachestats.py b/qutebrowser/misc/debugcachestats.py
index 2004ad7ab..9090bd0ea 100644
--- a/qutebrowser/misc/debugcachestats.py
+++ b/qutebrowser/misc/debugcachestats.py
@@ -23,11 +23,15 @@ Because many modules depend on this command, this needs to have as few
dependencies as possible to avoid cyclic dependencies.
"""
-from typing import Any, Callable, List, Optional, Tuple, TypeVar
+import weakref
+import sys
+from typing import Any, Callable, Optional, TypeVar, Mapping
+from qutebrowser.utils import log
-# The second element of each tuple should be a lru_cache wrapped function
-_CACHE_FUNCTIONS: List[Tuple[str, Any]] = []
+
+# The callable should be a lru_cache wrapped function
+_CACHE_FUNCTIONS: Mapping[str, Any] = weakref.WeakValueDictionary()
_T = TypeVar('_T', bound=Callable[..., Any])
@@ -36,13 +40,21 @@ _T = TypeVar('_T', bound=Callable[..., Any])
def register(name: Optional[str] = None) -> Callable[[_T], _T]:
"""Register a lru_cache wrapped function for debug_cache_stats."""
def wrapper(fn: _T) -> _T:
- _CACHE_FUNCTIONS.append((fn.__name__ if name is None else name, fn))
- return fn
+ fn_name = fn.__name__ if name is None else name
+ if sys.version_info < (3, 9):
+ log.misc.vdebug( # type: ignore[attr-defined]
+ "debugcachestats not supported on python < 3.9, not adding '%s'",
+ fn_name,
+ )
+ return fn
+
+ else:
+ _CACHE_FUNCTIONS[fn_name] = fn
+ return fn
return wrapper
def debug_cache_stats() -> None:
"""Print LRU cache stats."""
- from qutebrowser.utils import log
- for name, fn in _CACHE_FUNCTIONS:
+ for name, fn in _CACHE_FUNCTIONS.items():
log.misc.info('{}: {}'.format(name, fn.cache_info()))
diff --git a/qutebrowser/misc/editor.py b/qutebrowser/misc/editor.py
index d561a7b96..3ef84284d 100644
--- a/qutebrowser/misc/editor.py
+++ b/qutebrowser/misc/editor.py
@@ -135,7 +135,7 @@ class ExternalEditor(QObject):
message.error("Failed to create initial file: {}".format(e))
return
- self._remove_file = True
+ self._remove_file = config.val.editor.remove_file
line, column = self._calc_line_and_column(text, caret_position)
self._start_editor(line=line, column=column)
diff --git a/qutebrowser/misc/guiprocess.py b/qutebrowser/misc/guiprocess.py
index 8e9747ad8..e14169f93 100644
--- a/qutebrowser/misc/guiprocess.py
+++ b/qutebrowser/misc/guiprocess.py
@@ -22,6 +22,7 @@
import dataclasses
import locale
import shlex
+import shutil
from typing import Mapping, Sequence, Dict, Optional
from PyQt5.QtCore import (pyqtSlot, pyqtSignal, QObject, QProcess,
@@ -168,6 +169,7 @@ class GUIProcess(QObject):
self._output_messages = output_messages
self.outcome = ProcessOutcome(what=what)
self.cmd: Optional[str] = None
+ self.resolved_cmd: Optional[str] = None
self.args: Optional[Sequence[str]] = None
self.pid: Optional[int] = None
@@ -269,10 +271,9 @@ class GUIProcess(QObject):
# We can't get some kind of error code from Qt...
# https://bugreports.qt.io/browse/QTBUG-44769
- # However, it looks like those strings aren't actually translated?
- known_errors = ['No such file or directory', 'Permission denied']
- if (': ' in error_string and # pragma: no branch
- error_string.split(': ', maxsplit=1)[1] in known_errors):
+ # but we pre-resolve the executable in Python, which also checks if it's
+ # runnable.
+ if self.resolved_cmd is None: # pragma: no branch
msg += f'\nHint: Make sure {self.cmd!r} exists and is executable'
if version.is_flatpak():
msg += ' inside the Flatpak container'
@@ -334,10 +335,23 @@ class GUIProcess(QObject):
self.outcome.running = True
def _pre_start(self, cmd: str, args: Sequence[str]) -> None:
- """Prepare starting of a QProcess."""
+ """Resolve the given command and prepare starting of a QProcess.
+
+ Doing the resolving in Python here instead of letting Qt do it serves
+ two purposes:
+
+ - Being able to show a nicer error message without having to parse the
+ string we get from Qt: https://bugreports.qt.io/browse/QTBUG-44769
+ - Not running the file from the current directory on Unix with
+ Qt < 5.15.? and 6.2.4, as a WORKAROUND for CVE-2022-25255:
+ https://invent.kde.org/qt/qt/qtbase/-/merge_requests/139
+ https://www.qt.io/blog/security-advisory-qprocess
+ https://lists.qt-project.org/pipermail/announce/2022-February/000333.html
+ """
if self.outcome.running:
raise ValueError("Trying to start a running QProcess!")
self.cmd = cmd
+ self.resolved_cmd = shutil.which(cmd)
self.args = args
log.procs.debug(f"Executing: {self}")
if self.verbose:
@@ -347,7 +361,10 @@ class GUIProcess(QObject):
"""Convenience wrapper around QProcess::start."""
log.procs.debug("Starting process.")
self._pre_start(cmd, args)
- self._proc.start(cmd, args)
+ self._proc.start(
+ self.resolved_cmd, # type: ignore[arg-type]
+ args,
+ )
self._post_start()
self._proc.closeWriteChannel()
@@ -356,7 +373,10 @@ class GUIProcess(QObject):
log.procs.debug("Starting detached.")
self._pre_start(cmd, args)
ok, self.pid = self._proc.startDetached(
- cmd, args, None) # type: ignore[call-arg]
+ self.resolved_cmd,
+ args,
+ None, # workingDirectory
+ ) # type: ignore[call-arg]
if not ok:
message.error("Error while spawning {}".format(self.what))
diff --git a/qutebrowser/misc/utilcmds.py b/qutebrowser/misc/utilcmds.py
index 7ba45bdc3..14c02864e 100644
--- a/qutebrowser/misc/utilcmds.py
+++ b/qutebrowser/misc/utilcmds.py
@@ -23,6 +23,7 @@
import functools
import os
+import sys
import traceback
from typing import Optional
@@ -125,7 +126,9 @@ def debug_all_objects() -> None:
@cmdutils.register(debug=True)
def debug_cache_stats() -> None:
"""Print LRU cache stats."""
- debugcachestats.debug_cache_stats()
+ if sys.version_info < (3, 9):
+ raise cmdutils.CommandError('debugcachestats not supported on python < 3.9')
+ debugcachestats.debug_cache_stats() # type: ignore[unreachable]
@cmdutils.register(debug=True)
diff --git a/qutebrowser/utils/log.py b/qutebrowser/utils/log.py
index 4853d62ae..22035e074 100644
--- a/qutebrowser/utils/log.py
+++ b/qutebrowser/utils/log.py
@@ -43,7 +43,6 @@ except ImportError:
colorama = None
if TYPE_CHECKING:
- # pylint: disable=unused-import
from qutebrowser.config import config as configmodule
from typing import TextIO
diff --git a/qutebrowser/utils/resources.py b/qutebrowser/utils/resources.py
index f561d6747..b14b0faf4 100644
--- a/qutebrowser/utils/resources.py
+++ b/qutebrowser/utils/resources.py
@@ -24,19 +24,25 @@ import sys
import contextlib
import posixpath
import pathlib
-from typing import Iterator, Iterable
+from typing import Iterator, Iterable, Union
# We cannot use the stdlib version on 3.7-3.8 because we need the files() API.
if sys.version_info >= (3, 9):
import importlib.resources as importlib_resources
+ from importlib.abc import Traversable
else: # pragma: no cover
import importlib_resources
+ from importlib_resources.abc import Traversable
import qutebrowser
_cache = {}
-def _path(filename: str) -> pathlib.Path:
+
+_ResourceType = Union[Traversable, pathlib.Path]
+
+
+def _path(filename: str) -> _ResourceType:
"""Get a pathlib.Path object for a resource."""
assert not posixpath.isabs(filename), filename
assert os.path.pardir not in filename.split(posixpath.sep), filename
@@ -64,7 +70,7 @@ def _keyerror_workaround() -> Iterator[None]:
def _glob(
- resource_path: pathlib.Path,
+ resource_path: _ResourceType,
subdir: str,
ext: str,
) -> Iterable[str]:
@@ -77,14 +83,11 @@ def _glob(
glob_path = resource_path / subdir
if isinstance(resource_path, pathlib.Path):
+ assert isinstance(glob_path, pathlib.Path)
for full_path in glob_path.glob(f'*{ext}'): # . is contained in ext
yield full_path.relative_to(resource_path).as_posix()
- else: # zipfile.Path or importlib_resources compat object
- # Unfortunately, we can't tell mypy about resource_path being of type
- # Union[pathlib.Path, zipfile.Path] because we set "python_version = 3.6" in
- # .mypy.ini, but the zipfile stubs (correctly) only declare zipfile.Path with
- # Python 3.8...
- assert glob_path.is_dir(), glob_path # type: ignore[unreachable]
+ else: # zipfile.Path or other importlib_resources.abc.Traversable
+ assert glob_path.is_dir(), glob_path
for subpath in glob_path.iterdir():
if subpath.name.endswith(ext):
yield posixpath.join(subdir, subpath.name)
diff --git a/qutebrowser/utils/utils.py b/qutebrowser/utils/utils.py
index a899fa599..9c68932f3 100644
--- a/qutebrowser/utils/utils.py
+++ b/qutebrowser/utils/utils.py
@@ -300,8 +300,7 @@ def fake_io(write_func: Callable[[str], int]) -> Iterator[None]:
def disabled_excepthook() -> Iterator[None]:
"""Run code with the exception hook temporarily disabled."""
old_excepthook = sys.excepthook
- # https://github.com/python/typeshed/pull/6678
- sys.excepthook = sys.__excepthook__ # type: ignore[assignment]
+ sys.excepthook = sys.__excepthook__
try:
yield
finally:
diff --git a/qutebrowser/utils/version.py b/qutebrowser/utils/version.py
index 8da86dd00..bf6b49fa6 100644
--- a/qutebrowser/utils/version.py
+++ b/qutebrowser/utils/version.py
@@ -511,11 +511,10 @@ def _get_pyqt_webengine_qt_version() -> Optional[str]:
give us an accurate answer.
"""
try:
- import importlib_metadata
+ import importlib.metadata as importlib_metadata # type: ignore[import]
except ImportError:
try:
- # pylint: disable=line-too-long
- import importlib.metadata as importlib_metadata # type: ignore[import, no-redef]
+ import importlib_metadata # type: ignore[no-redef]
except ImportError:
log.misc.debug("Neither importlib.metadata nor backport available")
return None
@@ -724,7 +723,7 @@ class WebEngineVersions:
)
-def qtwebengine_versions(avoid_init: bool = False) -> WebEngineVersions:
+def qtwebengine_versions(*, avoid_init: bool = False) -> WebEngineVersions:
"""Get the QtWebEngine and Chromium version numbers.
If we have a parsed user agent, we use it here. If not, we avoid initializing
diff --git a/requirements.txt b/requirements.txt
index 8d1151dd7..25c1bd98f 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -1,15 +1,18 @@
# This file is automatically generated by scripts/dev/recompile_requirements.py
-adblock==0.5.1
+adblock==0.5.2
colorama==0.4.4
dataclasses==0.6 ; python_version<"3.7"
-importlib-metadata==4.10.0 ; python_version=="3.7.*"
-importlib-resources==5.4.0 ; python_version<"3.9"
-Jinja2==3.0.3
-MarkupSafe==2.0.1
-Pygments==2.11.1
+importlib-metadata==4.11.3 ; python_version=="3.7.*"
+importlib-resources==5.6.0 ; python_version=="3.7.*" or python_version=="3.8.*"
+Jinja2==3.1.1 ; python_version>="3.7"
+MarkupSafe==2.1.1 ; python_version>="3.7"
+Pygments==2.11.2
PyYAML==6.0
-typing_extensions==4.0.1 ; python_version<"3.8"
+typing_extensions==4.1.1 ; python_version<"3.8"
zipp==3.7.0 ; python_version>="3.7"
+importlib-resources<5.6.0 ; python_version=="3.6.*"
importlib-metadata<4.9 ; python_version=="3.6.*"
zipp<3.7 ; python_version=="3.6.*"
+MarkupSafe<2.1.0 ; python_version=="3.6.*"
+Jinja2<3.1.0 ; python_version=="3.6.*"
diff --git a/scripts/dev/build_release.py b/scripts/dev/build_release.py
index 5463441be..797b15e52 100755
--- a/scripts/dev/build_release.py
+++ b/scripts/dev/build_release.py
@@ -596,9 +596,9 @@ def github_upload(artifacts, tag, gh_token):
assets = [asset for asset in release.assets()
if asset.name == basename]
if assets:
- asset = assets[0]
- print("Deleting stray asset {}".format(asset.name))
- asset.delete()
+ stray_asset = assets[0]
+ print("Deleting stray asset {}".format(stray_asset.name))
+ stray_asset.delete()
else:
break
diff --git a/scripts/dev/changelog_urls.json b/scripts/dev/changelog_urls.json
new file mode 100644
index 000000000..0de1d68d9
--- /dev/null
+++ b/scripts/dev/changelog_urls.json
@@ -0,0 +1,161 @@
+{
+ "pyparsing": "https://github.com/pyparsing/pyparsing/blob/master/CHANGES",
+ "pylint": "https://pylint.pycqa.org/en/latest/whatsnew/changelog.html",
+ "dill": "https://github.com/uqfoundation/dill/commits/master",
+ "isort": "https://pycqa.github.io/isort/CHANGELOG/",
+ "lazy-object-proxy": "https://github.com/ionelmc/python-lazy-object-proxy/blob/master/CHANGELOG.rst",
+ "mccabe": "https://github.com/PyCQA/mccabe#changes",
+ "pytest-cov": "https://github.com/pytest-dev/pytest-cov/blob/master/CHANGELOG.rst",
+ "pytest-xdist": "https://github.com/pytest-dev/pytest-xdist/blob/master/CHANGELOG.rst",
+ "pytest-forked": "https://github.com/pytest-dev/pytest-forked/blob/master/CHANGELOG.rst",
+ "pytest-xvfb": "https://github.com/The-Compiler/pytest-xvfb/blob/master/CHANGELOG.rst",
+ "PyVirtualDisplay": "https://github.com/ponty/PyVirtualDisplay/commits/master",
+ "execnet": "https://execnet.readthedocs.io/en/latest/changelog.html",
+ "pytest-rerunfailures": "https://github.com/pytest-dev/pytest-rerunfailures/blob/master/CHANGES.rst",
+ "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://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/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",
+ "mypy": "https://mypy-lang.blogspot.com/",
+ "types-PyYAML": "https://github.com/python/typeshed/commits/master/stubs/PyYAML",
+ "types-dataclasses": "https://github.com/python/typeshed/commits/master/stubs/dataclasses",
+ "pytest": "https://docs.pytest.org/en/latest/changelog.html",
+ "iniconfig": "https://github.com/RonnyPfannschmidt/iniconfig/blob/master/CHANGELOG",
+ "tox": "https://tox.readthedocs.io/en/latest/changelog.html",
+ "PyYAML": "https://github.com/yaml/pyyaml/blob/master/CHANGES",
+ "pytest-bdd": "https://github.com/pytest-dev/pytest-bdd/blob/master/CHANGES.rst",
+ "snowballstemmer": "https://github.com/snowballstem/snowball/blob/master/NEWS",
+ "virtualenv": "https://github.com/pypa/virtualenv/blob/main/docs/changelog.rst",
+ "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://jinja.palletsprojects.com/en/latest/changes/",
+ "MarkupSafe": "https://markupsafe.palletsprojects.com/en/latest/changes/",
+ "flake8": "https://github.com/PyCQA/flake8/tree/main/docs/source/release-notes",
+ "flake8-docstrings": "https://pypi.org/project/flake8-docstrings/",
+ "flake8-debugger": "https://github.com/JBKahn/flake8-debugger/",
+ "flake8-builtins": "https://github.com/gforcada/flake8-builtins/blob/master/CHANGES.rst",
+ "flake8-bugbear": "https://github.com/PyCQA/flake8-bugbear#change-log",
+ "flake8-tidy-imports": "https://github.com/adamchainz/flake8-tidy-imports/blob/master/HISTORY.rst",
+ "flake8-tuple": "https://github.com/ar4s/flake8_tuple/blob/master/HISTORY.rst",
+ "flake8-comprehensions": "https://github.com/adamchainz/flake8-comprehensions/blob/master/HISTORY.rst",
+ "flake8-copyright": "https://github.com/savoirfairelinux/flake8-copyright/blob/master/CHANGELOG.rst",
+ "flake8-deprecated": "https://github.com/gforcada/flake8-deprecated/blob/master/CHANGES.rst",
+ "flake8-future-import": "https://github.com/xZise/flake8-future-import#changes",
+ "flake8-mock": "https://github.com/aleGpereira/flake8-mock#changes",
+ "flake8-polyfill": "https://gitlab.com/pycqa/flake8-polyfill/-/blob/master/CHANGELOG.rst",
+ "flake8-string-format": "https://github.com/xZise/flake8-string-format#changes",
+ "flake8-plugin-utils": "https://github.com/afonasev/flake8-plugin-utils#change-log",
+ "flake8-pytest-style": "https://github.com/m-burst/flake8-pytest-style#change-log",
+ "pep8-naming": "https://github.com/PyCQA/pep8-naming/blob/master/CHANGELOG.rst",
+ "pycodestyle": "https://github.com/PyCQA/pycodestyle/blob/master/CHANGES.txt",
+ "pyflakes": "https://github.com/PyCQA/pyflakes/blob/master/NEWS.rst",
+ "cffi": "https://github.com/python-cffi/release-doc/blob/master/doc/source/whatsnew.rst",
+ "astroid": "https://github.com/PyCQA/astroid/blob/main/ChangeLog",
+ "pytest-instafail": "https://github.com/pytest-dev/pytest-instafail/blob/master/CHANGES.rst",
+ "coverage": "https://github.com/nedbat/coveragepy/blob/master/CHANGES.rst",
+ "colorama": "https://github.com/tartley/colorama/blob/master/CHANGELOG.rst",
+ "hunter": "https://github.com/ionelmc/python-hunter/blob/master/CHANGELOG.rst",
+ "uritemplate": "https://github.com/python-hyper/uritemplate/blob/master/HISTORY.rst",
+ "more-itertools": "https://github.com/erikrose/more-itertools/blob/master/docs/versions.rst",
+ "pydocstyle": "https://www.pydocstyle.org/en/latest/release_notes.html",
+ "Sphinx": "https://www.sphinx-doc.org/en/master/changes.html",
+ "Babel": "https://github.com/python-babel/babel/blob/master/CHANGES",
+ "alabaster": "https://alabaster.readthedocs.io/en/latest/changelog.html",
+ "imagesize": "https://github.com/shibukawa/imagesize_py/commits/master",
+ "pytz": "https://mm.icann.org/pipermail/tz-announce/",
+ "sphinxcontrib-applehelp": "https://www.sphinx-doc.org/en/master/changes.html",
+ "sphinxcontrib-devhelp": "https://www.sphinx-doc.org/en/master/changes.html",
+ "sphinxcontrib-htmlhelp": "https://www.sphinx-doc.org/en/master/changes.html",
+ "sphinxcontrib-jsmath": "https://www.sphinx-doc.org/en/master/changes.html",
+ "sphinxcontrib-qthelp": "https://www.sphinx-doc.org/en/master/changes.html",
+ "sphinxcontrib-serializinghtml": "https://www.sphinx-doc.org/en/master/changes.html",
+ "jaraco.functools": "https://github.com/jaraco/jaraco.functools/blob/master/CHANGES.rst",
+ "parse": "https://github.com/r1chardj0n3s/parse#potential-gotchas",
+ "py": "https://py.readthedocs.io/en/latest/changelog.html#changelog",
+ "Pympler": "https://github.com/pympler/pympler/blob/master/CHANGELOG.md",
+ "pytest-mock": "https://github.com/pytest-dev/pytest-mock/blob/master/CHANGELOG.rst",
+ "pytest-qt": "https://github.com/pytest-dev/pytest-qt/blob/master/CHANGELOG.rst",
+ "pyinstaller": "https://pyinstaller.readthedocs.io/en/stable/CHANGES.html",
+ "pyinstaller-hooks-contrib": "https://github.com/pyinstaller/pyinstaller-hooks-contrib/blob/master/CHANGELOG.rst",
+ "pytest-benchmark": "https://pytest-benchmark.readthedocs.io/en/stable/changelog.html",
+ "typed-ast": "https://github.com/python/typed_ast/commits/master",
+ "docutils": "https://docutils.sourceforge.io/RELEASE-NOTES.html",
+ "bump2version": "https://github.com/c4urself/bump2version/blob/master/CHANGELOG.md",
+ "six": "https://github.com/benjaminp/six/blob/master/CHANGES",
+ "altgraph": "https://github.com/ronaldoussoren/altgraph/blob/master/doc/changelog.rst",
+ "urllib3": "https://github.com/urllib3/urllib3/blob/master/CHANGES.rst",
+ "lxml": "https://github.com/lxml/lxml/blob/master/CHANGES.txt",
+ "wrapt": "https://github.com/GrahamDumpleton/wrapt/blob/develop/docs/changes.rst",
+ "pep517": "https://github.com/pypa/pep517/blob/master/doc/changelog.rst",
+ "cryptography": "https://cryptography.io/en/latest/changelog.html",
+ "toml": "https://github.com/uiri/toml/releases",
+ "tomli": "https://github.com/hukkin/tomli/blob/master/CHANGELOG.md",
+ "PyQt5": "https://www.riverbankcomputing.com/news",
+ "PyQt5-Qt5": "https://www.riverbankcomputing.com/news",
+ "PyQtWebEngine": "https://www.riverbankcomputing.com/news",
+ "PyQtWebEngine-Qt5": "https://www.riverbankcomputing.com/news",
+ "PyQt-builder": "https://www.riverbankcomputing.com/news",
+ "PyQt5-sip": "https://www.riverbankcomputing.com/news",
+ "PyQt5-stubs": "https://github.com/stlehmann/PyQt5-stubs/blob/master/CHANGELOG.md",
+ "sip": "https://www.riverbankcomputing.com/news",
+ "Pygments": "https://pygments.org/docs/changelog/",
+ "vulture": "https://github.com/jendrikseipp/vulture/blob/master/CHANGELOG.md",
+ "distlib": "https://github.com/pypa/distlib/blob/master/CHANGES.rst",
+ "py-cpuinfo": "https://github.com/workhorsy/py-cpuinfo/blob/master/ChangeLog",
+ "cheroot": "https://cheroot.cherrypy.dev/en/latest/history.html",
+ "certifi": "https://ccadb-public.secure.force.com/mozilla/IncludedCACertificateReport",
+ "chardet": "https://github.com/chardet/chardet/releases",
+ "charset-normalizer": "https://github.com/Ousret/charset_normalizer/blob/master/CHANGELOG.md",
+ "idna": "https://github.com/kjd/idna/blob/master/HISTORY.rst",
+ "tldextract": "https://github.com/john-kurkowski/tldextract/blob/master/CHANGELOG.md",
+ "typing_extensions": "https://github.com/python/typing/blob/master/typing_extensions/CHANGELOG",
+ "diff-cover": "https://github.com/Bachmann1234/diff_cover/blob/master/CHANGELOG",
+ "pytest-icdiff": "https://github.com/hjwp/pytest-icdiff/blob/master/HISTORY.rst",
+ "icdiff": "https://github.com/jeffkaufman/icdiff/blob/master/ChangeLog",
+ "pprintpp": "https://github.com/wolever/pprintpp/blob/master/CHANGELOG.txt",
+ "beautifulsoup4": "https://bazaar.launchpad.net/~leonardr/beautifulsoup/bs4/view/head:/CHANGELOG",
+ "check-manifest": "https://github.com/mgedmin/check-manifest/blob/master/CHANGES.rst",
+ "yamllint": "https://github.com/adrienverge/yamllint/blob/master/CHANGELOG.rst",
+ "pathspec": "https://github.com/cpburnz/python-path-specification/blob/master/CHANGES.rst",
+ "filelock": "https://github.com/tox-dev/py-filelock/blob/main/docs/changelog.rst",
+ "github3.py": "https://github3.readthedocs.io/en/latest/release-notes/index.html",
+ "manhole": "https://github.com/ionelmc/python-manhole/blob/master/CHANGELOG.rst",
+ "pycparser": "https://github.com/eliben/pycparser/blob/master/CHANGES",
+ "python-dateutil": "https://dateutil.readthedocs.io/en/stable/changelog.html",
+ "platformdirs": "https://github.com/platformdirs/platformdirs/blob/main/CHANGES.rst",
+ "pluggy": "https://github.com/pytest-dev/pluggy/blob/master/CHANGELOG.rst",
+ "mypy-extensions": "https://github.com/python/mypy_extensions/commits/master",
+ "pyroma": "https://github.com/regebro/pyroma/blob/master/CHANGES.txt",
+ "adblock": "https://github.com/ArniDagur/python-adblock/blob/master/CHANGELOG.md",
+ "importlib-resources": "https://importlib-resources.readthedocs.io/en/latest/history.html",
+ "importlib-metadata": "https://github.com/python/importlib_metadata/blob/main/CHANGES.rst",
+ "zipp": "https://github.com/jaraco/zipp/blob/main/CHANGES.rst",
+ "dataclasses": "https://github.com/ericvsmith/dataclasses#release-history",
+ "pip": "https://pip.pypa.io/en/stable/news/",
+ "wheel": "https://wheel.readthedocs.io/en/stable/news.html",
+ "setuptools": "https://setuptools.readthedocs.io/en/latest/history.html",
+ "future": "https://python-future.org/whatsnew.html",
+ "pefile": "https://github.com/erocarrera/pefile/commits/master",
+ "SecretStorage": "https://github.com/mitya57/secretstorage/blob/master/changelog",
+ "bleach": "https://github.com/mozilla/bleach/blob/main/CHANGES",
+ "jeepney": "https://gitlab.com/takluyver/jeepney/-/blob/master/docs/release-notes.rst",
+ "keyring": "https://github.com/jaraco/keyring/blob/main/CHANGES.rst",
+ "pkginfo": "https://bazaar.launchpad.net/~tseaver/pkginfo/trunk/view/head:/CHANGES.txt",
+ "readme-renderer": "https://github.com/pypa/readme_renderer/blob/main/CHANGES.rst",
+ "requests-toolbelt": "https://github.com/requests/toolbelt/blob/master/HISTORY.rst",
+ "rfc3986": "https://rfc3986.readthedocs.io/en/latest/release-notes/index.html",
+ "tqdm": "https://tqdm.github.io/releases/",
+ "twine": "https://twine.readthedocs.io/en/stable/changelog.html",
+ "webencodings": "https://github.com/gsnedders/python-webencodings/commits/master",
+ "PyJWT": "https://github.com/jpadilla/pyjwt/blob/master/CHANGELOG.rst"
+}
diff --git a/scripts/dev/misc_checks.py b/scripts/dev/misc_checks.py
index bae51e372..37c59d98f 100644
--- a/scripts/dev/misc_checks.py
+++ b/scripts/dev/misc_checks.py
@@ -111,7 +111,7 @@ def check_changelog_urls(_args: argparse.Namespace = None) -> bool:
utils.print_col(f"Extra changelog URLs: {req_str}", 'red')
if not ok:
- print("Hint: Changelog URLs are in scripts/dev/recompile_requirements.py")
+ print("Hint: Changelog URLs are in scripts/dev/changelog_urls.json")
return ok
@@ -199,11 +199,6 @@ def check_spelling(args: argparse.Namespace) -> Optional[bool]:
"Don't use a blanket 'noqa', use something like 'noqa: X123' instead.",
),
(
- re.compile(r'# type: ignore[^\[]'),
- ("Don't use a blanket 'type: ignore', use something like "
- "'type: ignore[error-code]' instead."),
- ),
- (
re.compile(r'# type: (?!ignore(\[|$))'),
"Don't use type comments, use type annotations instead.",
),
diff --git a/scripts/dev/recompile_requirements.py b/scripts/dev/recompile_requirements.py
index dc56380bf..f8337b21f 100644
--- a/scripts/dev/recompile_requirements.py
+++ b/scripts/dev/recompile_requirements.py
@@ -24,6 +24,7 @@ import re
import sys
import os.path
import glob
+import json
import subprocess
import tempfile
import argparse
@@ -39,168 +40,8 @@ REPO_DIR = os.path.join(os.path.dirname(os.path.abspath(__file__)),
'..', '..') # /scripts/dev -> /scripts -> /
REQ_DIR = os.path.join(REPO_DIR, 'misc', 'requirements')
-CHANGELOG_URLS = {
- 'pyparsing': 'https://github.com/pyparsing/pyparsing/blob/master/CHANGES',
- 'pylint': 'https://pylint.pycqa.org/en/latest/whatsnew/changelog.html',
- 'isort': 'https://pycqa.github.io/isort/CHANGELOG/',
- 'lazy-object-proxy': 'https://github.com/ionelmc/python-lazy-object-proxy/blob/master/CHANGELOG.rst',
- 'mccabe': 'https://github.com/PyCQA/mccabe#changes',
- 'pytest-cov': 'https://github.com/pytest-dev/pytest-cov/blob/master/CHANGELOG.rst',
- 'pytest-xdist': 'https://github.com/pytest-dev/pytest-xdist/blob/master/CHANGELOG.rst',
- 'pytest-forked': 'https://github.com/pytest-dev/pytest-forked/blob/master/CHANGELOG.rst',
- 'pytest-xvfb': 'https://github.com/The-Compiler/pytest-xvfb/blob/master/CHANGELOG.rst',
- 'EasyProcess': 'https://github.com/ponty/EasyProcess/commits/master',
- 'PyVirtualDisplay': 'https://github.com/ponty/PyVirtualDisplay/commits/master',
- 'execnet': 'https://execnet.readthedocs.io/en/latest/changelog.html',
- 'pytest-rerunfailures': 'https://github.com/pytest-dev/pytest-rerunfailures/blob/master/CHANGES.rst',
- '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://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/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',
- 'mypy': 'https://mypy-lang.blogspot.com/',
- 'types-PyYAML': 'https://github.com/python/typeshed/commits/master/stubs/PyYAML',
- 'types-dataclasses': 'https://github.com/python/typeshed/commits/master/stubs/dataclasses',
- 'pytest': 'https://docs.pytest.org/en/latest/changelog.html',
- 'iniconfig': 'https://github.com/RonnyPfannschmidt/iniconfig/blob/master/CHANGELOG',
- 'tox': 'https://tox.readthedocs.io/en/latest/changelog.html',
- 'PyYAML': 'https://github.com/yaml/pyyaml/blob/master/CHANGES',
- 'pytest-bdd': 'https://github.com/pytest-dev/pytest-bdd/blob/master/CHANGES.rst',
- 'snowballstemmer': 'https://github.com/snowballstem/snowball/blob/master/NEWS',
- 'virtualenv': 'https://github.com/pypa/virtualenv/blob/main/docs/changelog.rst',
- '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://jinja.palletsprojects.com/en/latest/changes/',
- 'MarkupSafe': 'https://markupsafe.palletsprojects.com/en/latest/changes/',
- 'flake8': 'https://github.com/PyCQA/flake8/tree/main/docs/source/release-notes',
- 'flake8-docstrings': 'https://pypi.org/project/flake8-docstrings/',
- 'flake8-debugger': 'https://github.com/JBKahn/flake8-debugger/',
- 'flake8-builtins': 'https://github.com/gforcada/flake8-builtins/blob/master/CHANGES.rst',
- 'flake8-bugbear': 'https://github.com/PyCQA/flake8-bugbear#change-log',
- 'flake8-tidy-imports': 'https://github.com/adamchainz/flake8-tidy-imports/blob/master/HISTORY.rst',
- 'flake8-tuple': 'https://github.com/ar4s/flake8_tuple/blob/master/HISTORY.rst',
- 'flake8-comprehensions': 'https://github.com/adamchainz/flake8-comprehensions/blob/master/HISTORY.rst',
- 'flake8-copyright': 'https://github.com/savoirfairelinux/flake8-copyright/blob/master/CHANGELOG.rst',
- 'flake8-deprecated': 'https://github.com/gforcada/flake8-deprecated/blob/master/CHANGES.rst',
- 'flake8-future-import': 'https://github.com/xZise/flake8-future-import#changes',
- 'flake8-mock': 'https://github.com/aleGpereira/flake8-mock#changes',
- 'flake8-polyfill': 'https://gitlab.com/pycqa/flake8-polyfill/-/blob/master/CHANGELOG.rst',
- 'flake8-string-format': 'https://github.com/xZise/flake8-string-format#changes',
- 'flake8-plugin-utils': 'https://github.com/afonasev/flake8-plugin-utils#change-log',
- 'flake8-pytest-style': 'https://github.com/m-burst/flake8-pytest-style#change-log',
- 'pep8-naming': 'https://github.com/PyCQA/pep8-naming/blob/master/CHANGELOG.rst',
- 'pycodestyle': 'https://github.com/PyCQA/pycodestyle/blob/master/CHANGES.txt',
- 'pyflakes': 'https://github.com/PyCQA/pyflakes/blob/master/NEWS.rst',
- 'cffi': 'https://github.com/python-cffi/release-doc/blob/master/doc/source/whatsnew.rst',
- 'astroid': 'https://github.com/PyCQA/astroid/blob/main/ChangeLog',
- 'pytest-instafail': 'https://github.com/pytest-dev/pytest-instafail/blob/master/CHANGES.rst',
- 'coverage': 'https://github.com/nedbat/coveragepy/blob/master/CHANGES.rst',
- 'colorama': 'https://github.com/tartley/colorama/blob/master/CHANGELOG.rst',
- 'hunter': 'https://github.com/ionelmc/python-hunter/blob/master/CHANGELOG.rst',
- 'uritemplate': 'https://github.com/python-hyper/uritemplate/blob/master/HISTORY.rst',
- 'more-itertools': 'https://github.com/erikrose/more-itertools/blob/master/docs/versions.rst',
- 'pydocstyle': 'https://www.pydocstyle.org/en/latest/release_notes.html',
- 'Sphinx': 'https://www.sphinx-doc.org/en/master/changes.html',
- 'Babel': 'https://github.com/python-babel/babel/blob/master/CHANGES',
- 'alabaster': 'https://alabaster.readthedocs.io/en/latest/changelog.html',
- 'imagesize': 'https://github.com/shibukawa/imagesize_py/commits/master',
- 'pytz': 'https://mm.icann.org/pipermail/tz-announce/',
- 'sphinxcontrib-applehelp': 'https://www.sphinx-doc.org/en/master/changes.html',
- 'sphinxcontrib-devhelp': 'https://www.sphinx-doc.org/en/master/changes.html',
- 'sphinxcontrib-htmlhelp': 'https://www.sphinx-doc.org/en/master/changes.html',
- 'sphinxcontrib-jsmath': 'https://www.sphinx-doc.org/en/master/changes.html',
- 'sphinxcontrib-qthelp': 'https://www.sphinx-doc.org/en/master/changes.html',
- 'sphinxcontrib-serializinghtml': 'https://www.sphinx-doc.org/en/master/changes.html',
- 'jaraco.functools': 'https://github.com/jaraco/jaraco.functools/blob/master/CHANGES.rst',
- 'parse': 'https://github.com/r1chardj0n3s/parse#potential-gotchas',
- 'py': 'https://py.readthedocs.io/en/latest/changelog.html#changelog',
- 'Pympler': 'https://github.com/pympler/pympler/blob/master/CHANGELOG.md',
- 'pytest-mock': 'https://github.com/pytest-dev/pytest-mock/blob/master/CHANGELOG.rst',
- 'pytest-qt': 'https://github.com/pytest-dev/pytest-qt/blob/master/CHANGELOG.rst',
- 'pyinstaller': 'https://pyinstaller.readthedocs.io/en/stable/CHANGES.html',
- 'pyinstaller-hooks-contrib': 'https://github.com/pyinstaller/pyinstaller-hooks-contrib/blob/master/CHANGELOG.rst',
- 'pytest-benchmark': 'https://pytest-benchmark.readthedocs.io/en/stable/changelog.html',
- 'typed-ast': 'https://github.com/python/typed_ast/commits/master',
- 'docutils': 'https://docutils.sourceforge.io/RELEASE-NOTES.html',
- 'bump2version': 'https://github.com/c4urself/bump2version/blob/master/CHANGELOG.md',
- 'six': 'https://github.com/benjaminp/six/blob/master/CHANGES',
- 'altgraph': 'https://github.com/ronaldoussoren/altgraph/blob/master/doc/changelog.rst',
- 'urllib3': 'https://github.com/urllib3/urllib3/blob/master/CHANGES.rst',
- 'lxml': 'https://github.com/lxml/lxml/blob/master/CHANGES.txt',
- 'jwcrypto': 'https://github.com/latchset/jwcrypto/commits/master',
- 'wrapt': 'https://github.com/GrahamDumpleton/wrapt/blob/develop/docs/changes.rst',
- 'pep517': 'https://github.com/pypa/pep517/blob/master/doc/changelog.rst',
- 'cryptography': 'https://cryptography.io/en/latest/changelog.html',
- 'toml': 'https://github.com/uiri/toml/releases',
- 'tomli': 'https://github.com/hukkin/tomli/blob/master/CHANGELOG.md',
- 'PyQt5': 'https://www.riverbankcomputing.com/news',
- 'PyQt5-Qt5': 'https://www.riverbankcomputing.com/news',
- 'PyQtWebEngine': 'https://www.riverbankcomputing.com/news',
- 'PyQtWebEngine-Qt5': 'https://www.riverbankcomputing.com/news',
- 'PyQt-builder': 'https://www.riverbankcomputing.com/news',
- 'PyQt5-sip': 'https://www.riverbankcomputing.com/news',
- 'PyQt5-stubs': 'https://github.com/stlehmann/PyQt5-stubs/blob/master/CHANGELOG.md',
- 'sip': 'https://www.riverbankcomputing.com/news',
- 'Pygments': 'https://pygments.org/docs/changelog/',
- 'vulture': 'https://github.com/jendrikseipp/vulture/blob/master/CHANGELOG.md',
- 'distlib': 'https://github.com/pypa/distlib/blob/master/CHANGES.rst',
- 'py-cpuinfo': 'https://github.com/workhorsy/py-cpuinfo/blob/master/ChangeLog',
- 'cheroot': 'https://cheroot.cherrypy.org/en/latest/history.html',
- 'certifi': 'https://ccadb-public.secure.force.com/mozilla/IncludedCACertificateReport',
- 'chardet': 'https://github.com/chardet/chardet/releases',
- 'charset-normalizer': 'https://github.com/Ousret/charset_normalizer/blob/master/CHANGELOG.md',
- 'idna': 'https://github.com/kjd/idna/blob/master/HISTORY.rst',
- 'tldextract': 'https://github.com/john-kurkowski/tldextract/blob/master/CHANGELOG.md',
- 'typing_extensions': 'https://github.com/python/typing/blob/master/typing_extensions/CHANGELOG',
- 'diff-cover': 'https://github.com/Bachmann1234/diff_cover/blob/master/CHANGELOG',
- 'pytest-icdiff': 'https://github.com/hjwp/pytest-icdiff/blob/master/HISTORY.rst',
- 'icdiff': 'https://github.com/jeffkaufman/icdiff/blob/master/ChangeLog',
- 'pprintpp': 'https://github.com/wolever/pprintpp/blob/master/CHANGELOG.txt',
- 'beautifulsoup4': 'https://bazaar.launchpad.net/~leonardr/beautifulsoup/bs4/view/head:/CHANGELOG',
- 'check-manifest': 'https://github.com/mgedmin/check-manifest/blob/master/CHANGES.rst',
- 'yamllint': 'https://github.com/adrienverge/yamllint/blob/master/CHANGELOG.rst',
- 'pathspec': 'https://github.com/cpburnz/python-path-specification/blob/master/CHANGES.rst',
- 'filelock': 'https://github.com/tox-dev/py-filelock/blob/main/docs/changelog.rst',
- 'github3.py': 'https://github3py.readthedocs.io/en/master/release-notes/index.html',
- 'manhole': 'https://github.com/ionelmc/python-manhole/blob/master/CHANGELOG.rst',
- 'pycparser': 'https://github.com/eliben/pycparser/blob/master/CHANGES',
- 'python-dateutil': 'https://dateutil.readthedocs.io/en/stable/changelog.html',
- 'platformdirs': 'https://github.com/platformdirs/platformdirs/blob/main/CHANGES.rst',
- 'pluggy': 'https://github.com/pytest-dev/pluggy/blob/master/CHANGELOG.rst',
- 'mypy-extensions': 'https://github.com/python/mypy_extensions/commits/master',
- 'pyroma': 'https://github.com/regebro/pyroma/blob/master/HISTORY.txt',
- 'adblock': 'https://github.com/ArniDagur/python-adblock/blob/master/CHANGELOG.md',
- 'importlib-resources': 'https://importlib-resources.readthedocs.io/en/latest/history.html',
- 'importlib-metadata': 'https://github.com/python/importlib_metadata/blob/main/CHANGES.rst',
- 'zipp': 'https://github.com/jaraco/zipp/blob/main/CHANGES.rst',
- 'dataclasses': 'https://github.com/ericvsmith/dataclasses#release-history',
- 'pip': 'https://pip.pypa.io/en/stable/news/',
- 'wheel': 'https://wheel.readthedocs.io/en/stable/news.html',
- 'setuptools': 'https://setuptools.readthedocs.io/en/latest/history.html',
- 'future': 'https://python-future.org/whatsnew.html',
- 'pefile': 'https://github.com/erocarrera/pefile/commits/master',
- 'Deprecated': 'https://github.com/tantale/deprecated/blob/master/CHANGELOG.rst',
- 'SecretStorage': 'https://github.com/mitya57/secretstorage/blob/master/changelog',
- 'bleach': 'https://github.com/mozilla/bleach/blob/main/CHANGES',
- 'jeepney': 'https://gitlab.com/takluyver/jeepney/-/blob/master/docs/release-notes.rst',
- 'keyring': 'https://github.com/jaraco/keyring/blob/main/CHANGES.rst',
- 'pkginfo': 'https://bazaar.launchpad.net/~tseaver/pkginfo/trunk/view/head:/CHANGES.txt',
- 'readme-renderer': 'https://github.com/pypa/readme_renderer/blob/main/CHANGES.rst',
- 'requests-toolbelt': 'https://github.com/requests/toolbelt/blob/master/HISTORY.rst',
- 'rfc3986': 'https://rfc3986.readthedocs.io/en/latest/release-notes/index.html',
- 'tqdm': 'https://tqdm.github.io/releases/',
- 'twine': 'https://twine.readthedocs.io/en/stable/changelog.html',
- 'webencodings': 'https://github.com/gsnedders/python-webencodings/commits/master',
-}
+CHANGELOG_URLS_PATH = pathlib.Path(__file__).parent / "changelog_urls.json"
+CHANGELOG_URLS = json.loads(CHANGELOG_URLS_PATH.read_text())
def convert_line(line, comments):
@@ -347,10 +188,11 @@ class Change:
"""A single requirements change from a git diff output."""
- def __init__(self, name):
+ def __init__(self, name: str, base_path: pathlib.Path) -> None:
self.name = name
self.old = None
self.new = None
+ self.base = extract_requirement_name(base_path)
if CHANGELOG_URLS.get(name):
self.url = CHANGELOG_URLS[name]
self.link = '[{}]({})'.format(self.name, self.url)
@@ -358,24 +200,27 @@ class Change:
self.url = '(no changelog)'
self.link = self.name
+ def __lt__(self, other):
+ return (self.base, self.name.lower()) < (other.base, other.name.lower())
+
def __str__(self):
+ prefix = f"- [{self.base}] {self.name}"
+ suffix = f" {self.url}"
if self.old is None:
- return '- {} new: {} {}'.format(self.name, self.new, self.url)
+ return f"{prefix} new: {self.new} {suffix}"
elif self.new is None:
- return '- {} removed: {} {}'.format(self.name, self.old,
- self.url)
+ return f"{prefix} removed: {self.old} {suffix}"
else:
- return '- {} {} -> {} {}'.format(self.name, self.old, self.new,
- self.url)
+ return f"{prefix} {self.old} -> {self.new} {suffix}"
def table_str(self):
"""Generate a markdown table."""
if self.old is None:
- return '| {} | -- | {} |'.format(self.link, self.new)
+ return f'| {self.base} | {self.link} | -- | {self.new} |'
elif self.new is None:
- return '| {} | {} | -- |'.format(self.link, self.old)
+ return f'| {self.base} | {self.link} | {self.old} | -- |'
else:
- return '| {} | {} | {} |'.format(self.link, self.old, self.new)
+ return f'| {self.base} | {self.link} | {self.old} | {self.new} |'
def _get_changed_files():
@@ -383,14 +228,23 @@ def _get_changed_files():
changed_files = set()
filenames = git_diff('--name-only')
for filename in filenames:
- filename = filename.strip()
- filename = filename.replace('misc/requirements/requirements-', '')
- filename = filename.replace('.txt', '')
- changed_files.add(filename)
+ requirement_name = extract_requirement_name(pathlib.Path(filename))
+ changed_files.add(requirement_name)
return sorted(changed_files)
+def extract_requirement_name(path: pathlib.Path) -> str:
+ """Get a requirement name from a file path.
+
+ e.g. "pylint" from "misc/requirements/requirements-pylint.txt"
+ """
+ prefix = "requirements-"
+ assert path.suffix == ".txt", path
+ assert path.stem.startswith(prefix), path
+ return path.stem[len(prefix):]
+
+
def parse_versioned_line(line):
"""Parse a requirements.txt line into name/version."""
if line[0] == '#': # ignored dependency
@@ -424,10 +278,19 @@ def parse_versioned_line(line):
def _get_changes(diff):
"""Get a list of changed versions from git."""
changes_dict = {}
+ current_path = None
+
for line in diff:
if not line.startswith('-') and not line.startswith('+'):
continue
- elif line.startswith('+++ ') or line.startswith('--- '):
+ elif line.startswith('--- '):
+ prefix = '--- a/'
+ current_path = pathlib.Path(line[len(prefix):])
+ continue
+ elif line.startswith('+++ '):
+ prefix = '+++ b/'
+ new_path = pathlib.Path(line[len(prefix):])
+ assert current_path == new_path, (current_path, new_path)
continue
elif not line.strip():
# Could be newline changes on Windows
@@ -439,14 +302,14 @@ def _get_changes(diff):
name, version = parse_versioned_line(line[1:])
if name not in changes_dict:
- changes_dict[name] = Change(name)
+ changes_dict[name] = Change(name, base_path=current_path)
if line.startswith('-'):
changes_dict[name].old = version
elif line.startswith('+'):
changes_dict[name].new = version
- return [change for _name, change in sorted(changes_dict.items())]
+ return sorted(changes_dict.values())
def print_changed_files():
@@ -473,8 +336,8 @@ def print_changed_files():
print('::set-output name=changed::' +
files_text.replace('\n', '%0A'))
table_header = [
- '| Requirement | old | new |',
- '|-------------|-----|-----|',
+ '| File | Requirement | old | new |',
+ '|------|-------------|-----|-----|',
]
diff_table = '%0A'.join(table_header +
[change.table_str() for change in changes])
diff --git a/scripts/dev/run_pylint_on_tests.py b/scripts/dev/run_pylint_on_tests.py
index 28c6e32c9..a44828a85 100644
--- a/scripts/dev/run_pylint_on_tests.py
+++ b/scripts/dev/run_pylint_on_tests.py
@@ -64,6 +64,9 @@ def main():
'import-error',
# tests/helpers imports
'wrong-import-order',
+ # https://github.com/PyCQA/pylint/issues/6036
+ # https://github.com/PyCQA/pylint/issues/6037
+ 'unnecessary-ellipsis',
]
toxinidir = sys.argv[1]
diff --git a/scripts/dev/src2asciidoc.py b/scripts/dev/src2asciidoc.py
index 375bb1eb7..febd2bf8a 100755
--- a/scripts/dev/src2asciidoc.py
+++ b/scripts/dev/src2asciidoc.py
@@ -536,7 +536,9 @@ def regenerate_manpage(filename):
# pylint: disable=protected-access
for group in parser._action_groups:
groupdata = []
- groupdata.append('=== {}'.format(group.title))
+ # https://bugs.python.org/issue9694 backport
+ title = "options" if group.title == "optional arguments" else group.title
+ groupdata.append('=== {}'.format(title))
if group.description is not None:
groupdata.append(group.description)
for action in group._group_actions:
diff --git a/tests/conftest.py b/tests/conftest.py
index 26cc04345..84cae784b 100644
--- a/tests/conftest.py
+++ b/tests/conftest.py
@@ -17,31 +17,29 @@
# You should have received a copy of the GNU General Public License
# along with qutebrowser. If not, see <https://www.gnu.org/licenses/>.
-# pylint: disable=unused-import,wildcard-import,unused-wildcard-import
-
"""The qutebrowser test suite conftest file."""
import os
import pathlib
import sys
-import warnings
import pytest
import hypothesis
-from PyQt5.QtCore import PYQT_VERSION
pytest.register_assert_rewrite('helpers')
+# pylint: disable=wildcard-import,unused-import,unused-wildcard-import
from helpers import logfail
from helpers.logfail import fail_on_logging
from helpers.messagemock import message_mock
from helpers.fixtures import * # noqa: F403
+# pylint: enable=wildcard-import,unused-import,unused-wildcard-import
from helpers import testutils
-from qutebrowser.utils import qtutils, standarddir, usertypes, utils, version
+from qutebrowser.utils import usertypes, utils, version
from qutebrowser.misc import objects, earlyinit
-from qutebrowser.qt import sip
-import qutebrowser.app # To register commands
+# To register commands
+import qutebrowser.app # pylint: disable=unused-import
_qute_scheme_handler = None
@@ -196,9 +194,8 @@ def pytest_ignore_collect(path):
@pytest.fixture(scope='session')
def qapp_args():
"""Make QtWebEngine unit tests run on older Qt versions + newer kernels."""
- seccomp_args = testutils.seccomp_args(qt_flag=False)
- if seccomp_args:
- return [sys.argv[0]] + seccomp_args
+ if testutils.disable_seccomp_bpf_sandbox():
+ return [sys.argv[0], testutils.DISABLE_SECCOMP_BPF_FLAG]
return []
@@ -251,6 +248,7 @@ def _select_backend(config):
backend = backend_arg or backend_env or _auto_select_backend()
# Fail early if selected backend is not available
+ # pylint: disable=unused-import
if backend == 'webkit':
import PyQt5.QtWebKitWidgets
elif backend == 'webengine':
@@ -262,6 +260,7 @@ def _select_backend(config):
def _auto_select_backend():
+ # pylint: disable=unused-import
try:
# Try to use QtWebKit as the default backend
import PyQt5.QtWebKitWidgets
@@ -339,7 +338,7 @@ def check_yaml_c_exts():
https://github.com/yaml/pyyaml/issues/416
"""
if testutils.ON_CI and sys.version_info[:2] != (3, 10):
- from yaml import CLoader
+ from yaml import CLoader # pylint: disable=unused-import
@pytest.hookimpl(tryfirst=True, hookwrapper=True)
diff --git a/tests/end2end/conftest.py b/tests/end2end/conftest.py
index 16170d460..14ac6f395 100644
--- a/tests/end2end/conftest.py
+++ b/tests/end2end/conftest.py
@@ -17,12 +17,9 @@
# You should have received a copy of the GNU General Public License
# along with qutebrowser. If not, see <https://www.gnu.org/licenses/>.
-# pylint: disable=unused-import
-
"""Things needed for end2end testing."""
import re
-import os
import pathlib
import sys
import shutil
@@ -34,13 +31,14 @@ from PyQt5.QtCore import PYQT_VERSION, QCoreApplication
pytest.register_assert_rewrite('end2end.fixtures')
+# pylint: disable=unused-import
from end2end.fixtures.notificationserver import notification_server
from end2end.fixtures.webserver import server, server_per_test, server2, ssl_server
from end2end.fixtures.quteprocess import (quteproc_process, quteproc,
quteproc_new)
from end2end.fixtures.testprocess import pytest_runtest_makereport
+# pylint: enable=unused-import
from qutebrowser.utils import qtutils, utils
-from qutebrowser.browser.webengine import spell
def pytest_configure(config):
@@ -61,7 +59,7 @@ def pytest_unconfigure(config):
stats.dump_stats((pathlib.Path('prof') / 'combined.pstats'))
-def _check_hex_version(op_str, running_version, version):
+def _check_version(op_str, running_version, version_str, as_hex=False):
operators = {
'==': operator.eq,
'!=': operator.ne,
@@ -71,9 +69,12 @@ def _check_hex_version(op_str, running_version, version):
'<': operator.lt,
}
op = operators[op_str]
- major, minor, patch = [int(e) for e in version.split('.')]
- hex_version = (major << 16) | (minor << 8) | patch
- return op(running_version, hex_version)
+ major, minor, patch = [int(e) for e in version_str.split('.')]
+ if as_hex:
+ version = (major << 16) | (minor << 8) | patch
+ else:
+ version = (major, minor, patch)
+ return op(running_version, version)
def _get_version_tag(tag):
@@ -84,7 +85,7 @@ def _get_version_tag(tag):
casesinto an appropriate @pytest.mark.skip marker, and falls back to
"""
version_re = re.compile(r"""
- (?P<package>qt|pyqt|pyqtwebengine)
+ (?P<package>qt|pyqt|pyqtwebengine|python)
(?P<operator>==|>=|!=|<)
(?P<version>\d+\.\d+(\.\d+)?)
""", re.VERBOSE)
@@ -108,10 +109,11 @@ def _get_version_tag(tag):
return pytest.mark.skipif(do_skip[op], reason='Needs ' + tag)
elif package == 'pyqt':
return pytest.mark.skipif(
- not _check_hex_version(
+ not _check_version(
op_str=match.group('operator'),
running_version=PYQT_VERSION,
- version=version
+ version_str=version,
+ as_hex=True,
),
reason='Needs ' + tag,
)
@@ -123,10 +125,21 @@ def _get_version_tag(tag):
else:
running_version = PYQT_WEBENGINE_VERSION
return pytest.mark.skipif(
- not _check_hex_version(
+ not _check_version(
+ op_str=match.group('operator'),
+ running_version=running_version,
+ version_str=version,
+ as_hex=True,
+ ),
+ reason='Needs ' + tag,
+ )
+ elif package == 'python':
+ running_version = sys.version_info
+ return pytest.mark.skipif(
+ not _check_version(
op_str=match.group('operator'),
running_version=running_version,
- version=version
+ version_str=version,
),
reason='Needs ' + tag,
)
diff --git a/tests/end2end/features/tabs.feature b/tests/end2end/features/tabs.feature
index 3715d5765..54e84a10e 100644
--- a/tests/end2end/features/tabs.feature
+++ b/tests/end2end/features/tabs.feature
@@ -1741,3 +1741,22 @@ Feature: Tab management
And I run :undo
And I run :message-info "Still alive!"
Then the message "Still alive!" should be shown
+
+ Scenario: Passthrough mode override
+ When I run :set -u localhost:*/data/numbers/1.txt input.mode_override 'passthrough'
+ And I open data/numbers/1.txt
+ Then "Entering mode KeyMode.passthrough (reason: mode_override)" should be logged
+
+ Scenario: Insert mode override
+ When I run :set -u localhost:*/data/numbers/1.txt input.mode_override 'insert'
+ And I open data/numbers/1.txt
+ Then "Entering mode KeyMode.insert (reason: mode_override)" should be logged
+
+ Scenario: Mode override on tab switch
+ When I run :set -u localhost:*/data/numbers/1.txt input.mode_override 'insert'
+ And I open data/numbers/1.txt
+ And I wait for "Entering mode KeyMode.insert (reason: mode_override)" in the log
+ And I run :fake-key -g <esc>
+ And I open data/numbers/2.txt in a new tab
+ And I run :tab-prev
+ Then "Entering mode KeyMode.insert (reason: mode_override)" should be logged
diff --git a/tests/end2end/features/utilcmds.feature b/tests/end2end/features/utilcmds.feature
index bb2b3e2fb..e8172ae20 100644
--- a/tests/end2end/features/utilcmds.feature
+++ b/tests/end2end/features/utilcmds.feature
@@ -93,6 +93,7 @@ Feature: Miscellaneous utility commands exposed to the user.
## :debug-cache-stats
+ @python>=3.9.0
Scenario: :debug-cache-stats
When I run :debug-cache-stats
Then "is_valid_prefix: CacheInfo(*)" should be logged
diff --git a/tests/end2end/fixtures/quteprocess.py b/tests/end2end/fixtures/quteprocess.py
index 14f34b52c..ab8f28d26 100644
--- a/tests/end2end/fixtures/quteprocess.py
+++ b/tests/end2end/fixtures/quteprocess.py
@@ -549,8 +549,8 @@ class QuteProc(testprocess.Process):
'--debug-flag', 'werror', '--debug-flag',
'test-notification-service']
- if self.request.config.webengine:
- args += testutils.seccomp_args(qt_flag=True)
+ if self.request.config.webengine and testutils.disable_seccomp_bpf_sandbox():
+ args += testutils.DISABLE_SECCOMP_BPF_ARGS
args.append('about:blank')
return args
diff --git a/tests/end2end/fixtures/webserver.py b/tests/end2end/fixtures/webserver.py
index 0fc32cd88..2c2eab930 100644
--- a/tests/end2end/fixtures/webserver.py
+++ b/tests/end2end/fixtures/webserver.py
@@ -149,11 +149,9 @@ class WebserverProcess(testprocess.Process):
def _random_port(self) -> int:
"""Get a random free port."""
- sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
- sock.bind(('localhost', 0))
- port = sock.getsockname()[1]
- sock.close()
- return port
+ with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as sock:
+ sock.bind(('localhost', 0))
+ return sock.getsockname()[1]
def get_requests(self):
"""Get the requests to the server during this test."""
@@ -162,7 +160,7 @@ class WebserverProcess(testprocess.Process):
def _parse_line(self, line):
self._log(line)
- started_re = re.compile(r' \* Running on https?://127\.0\.0\.1:{}/ '
+ started_re = re.compile(r' \* Running on https?://127\.0\.0\.1:{}/? '
r'\(Press CTRL\+C to quit\)'.format(self.port))
if started_re.fullmatch(line):
self.ready.emit()
diff --git a/tests/end2end/fixtures/webserver_sub.py b/tests/end2end/fixtures/webserver_sub.py
index 92f3091d1..b4964d973 100644
--- a/tests/end2end/fixtures/webserver_sub.py
+++ b/tests/end2end/fixtures/webserver_sub.py
@@ -292,6 +292,8 @@ def view_user_agent():
@app.route('/favicon.ico')
def favicon():
+ # WORKAROUND for https://github.com/PyCQA/pylint/issues/5783
+ # pylint: disable-next=no-member,useless-suppression
icon_dir = END2END_DIR.parents[1] / 'icons'
return flask.send_from_directory(
icon_dir, 'qutebrowser.ico', mimetype='image/vnd.microsoft.icon')
diff --git a/tests/end2end/test_invocations.py b/tests/end2end/test_invocations.py
index b860feed0..43809cfd4 100644
--- a/tests/end2end/test_invocations.py
+++ b/tests/end2end/test_invocations.py
@@ -56,8 +56,8 @@ def _base_args(config):
else:
args += ['--backend', 'webkit']
- if config.webengine:
- args += testutils.seccomp_args(qt_flag=True)
+ if config.webengine and testutils.disable_seccomp_bpf_sandbox():
+ args += testutils.DISABLE_SECCOMP_BPF_ARGS
args.append('about:blank')
return args
@@ -826,3 +826,81 @@ def test_json_logging_without_debug(request, quteproc_new, runtime_tmpdir):
quteproc_new.exit_expected = True
quteproc_new.start(args, env={'XDG_RUNTIME_DIR': str(runtime_tmpdir)})
assert not quteproc_new.is_running()
+
+
+@pytest.mark.qtwebkit_skip
+@pytest.mark.parametrize(
+ 'sandboxing, has_namespaces, has_seccomp, has_yama, expected_result', [
+ ('enable-all', True, True, True, "You are adequately sandboxed."),
+ ('disable-seccomp-bpf', True, False, True, "You are NOT adequately sandboxed."),
+ ('disable-all', False, False, False, "You are NOT adequately sandboxed."),
+ ]
+)
+def test_sandboxing(
+ request, quteproc_new, sandboxing,
+ has_namespaces, has_seccomp, has_yama, expected_result,
+):
+ if not request.config.webengine:
+ pytest.skip("Skipped with QtWebKit")
+ elif sandboxing == "enable-all" and testutils.disable_seccomp_bpf_sandbox():
+ pytest.skip("Full sandboxing not supported")
+
+ args = _base_args(request.config) + [
+ '--temp-basedir',
+ '-s', 'qt.chromium.sandboxing', sandboxing,
+ ]
+ quteproc_new.start(args)
+
+ quteproc_new.open_url('chrome://sandbox')
+ text = quteproc_new.get_content()
+ print(text)
+
+ not_found_msg = ("The webpage at chrome://sandbox/ might be temporarily down or "
+ "it may have moved permanently to a new web address.")
+ if not_found_msg in text.split("\n"):
+ line = quteproc_new.wait_for(message='Load error: ERR_INVALID_URL')
+ line.expected = True
+ pytest.skip("chrome://sandbox/ not supported")
+
+ bpf_text = "Seccomp-BPF sandbox"
+ yama_text = "Ptrace Protection with Yama LSM"
+
+ if "\n\n\n" in text:
+ # Qt 5.12
+ header, rest = text.split("\n", maxsplit=1)
+ rest, result = rest.rsplit("\n\n", maxsplit=1)
+ lines = rest.replace("\t\n", "\t").split("\n\n\n")
+
+ expected_status = {
+ "Namespace Sandbox": "Yes" if has_namespaces else "No",
+ "Network namespaces": "Yes" if has_namespaces else "No",
+ "PID namespaces": "Yes" if has_namespaces else "No",
+ "SUID Sandbox": "No",
+
+ bpf_text: "Yes" if has_seccomp else "No",
+ f"{bpf_text} supports TSYNC": "Yes" if has_seccomp else "No",
+
+ "Yama LSM Enforcing": "Yes" if has_yama else "No",
+ }
+ else:
+ header, *lines, empty, result = text.split("\n")
+ assert not empty
+
+ expected_status = {
+ "Layer 1 Sandbox": "Namespace" if has_namespaces else "None",
+
+ "PID namespaces": "Yes" if has_namespaces else "No",
+ "Network namespaces": "Yes" if has_namespaces else "No",
+
+ bpf_text: "Yes" if has_seccomp else "No",
+ f"{bpf_text} supports TSYNC": "Yes" if has_seccomp else "No",
+
+ f"{yama_text} (Broker)": "Yes" if has_yama else "No",
+ f"{yama_text} (Non-broker)": "No",
+ }
+
+ assert header == "Sandbox Status"
+ assert result == expected_result
+
+ status = dict(line.split("\t") for line in lines)
+ assert status == expected_status
diff --git a/tests/helpers/testutils.py b/tests/helpers/testutils.py
index 8bb622133..c607718ab 100644
--- a/tests/helpers/testutils.py
+++ b/tests/helpers/testutils.py
@@ -31,14 +31,9 @@ import importlib.machinery
import pytest
-from PyQt5.QtCore import qVersion
from PyQt5.QtGui import QColor
-try:
- from PyQt5.QtWebEngine import PYQT_WEBENGINE_VERSION_STR
-except ImportError:
- PYQT_WEBENGINE_VERSION_STR = None
-from qutebrowser.utils import qtutils, log, utils
+from qutebrowser.utils import qtutils, log, utils, version
ON_CI = 'CI' in os.environ
@@ -267,35 +262,38 @@ def easyprivacy_txt():
return _decompress_gzip_datafile("easyprivacy.txt.gz")
-def seccomp_args(qt_flag):
- """Get necessary flags to disable the seccomp BPF sandbox.
+DISABLE_SECCOMP_BPF_FLAG = "--disable-seccomp-filter-sandbox"
+DISABLE_SECCOMP_BPF_ARGS = ["-s", "qt.chromium.sandboxing", "disable-seccomp-bpf"]
+
+
+def disable_seccomp_bpf_sandbox():
+ """Check whether we need to disable the seccomp BPF sandbox.
This is needed for some QtWebEngine setups, with older Qt versions but
newer kernels.
-
- Args:
- qt_flag: Add a '--qt-flag' argument.
"""
+ try:
+ from PyQt5 import QtWebEngine # pylint: disable=unused-import
+ except ImportError:
+ # no QtWebEngine available
+ return False
+
affected_versions = set()
for base, patch_range in [
- # 5.12.0 to 5.12.7 (inclusive)
- ('5.12', range(0, 8)),
+ # 5.12.0 to 5.12.10 (inclusive)
+ ('5.12', range(0, 11)),
# 5.13.0 to 5.13.2 (inclusive)
('5.13', range(0, 3)),
# 5.14.0
('5.14', [0]),
+ # 5.15.0 to 5.15.2 (inclusive)
+ ('5.15', range(0, 3)),
]:
for patch in patch_range:
- affected_versions.add('{}.{}'.format(base, patch))
-
- version = (PYQT_WEBENGINE_VERSION_STR
- if PYQT_WEBENGINE_VERSION_STR is not None
- else qVersion())
- if version in affected_versions:
- disable_arg = 'disable-seccomp-filter-sandbox'
- return ['--qt-flag', disable_arg] if qt_flag else ['--' + disable_arg]
+ affected_versions.add(utils.VersionNumber.parse(f'{base}.{patch}'))
- return []
+ versions = version.qtwebengine_versions(avoid_init=True)
+ return versions.webengine in affected_versions
def import_userscript(name):
diff --git a/tests/unit/completion/test_models.py b/tests/unit/completion/test_models.py
index 9e6743083..2c00acf68 100644
--- a/tests/unit/completion/test_models.py
+++ b/tests/unit/completion/test_models.py
@@ -867,6 +867,34 @@ def test_tab_completion_delete(qtmodeltester, fake_web_tab, win_registry,
QUrl('https://duckduckgo.com')]
+def test_tab_focus_completion_delete(qtmodeltester, fake_web_tab, win_registry,
+ tabbed_browser_stubs, info):
+ """Verify closing a tab by deleting it from the completion widget."""
+ tabbed_browser_stubs[0].widget.tabs = [
+ fake_web_tab(QUrl('https://github.com'), 'GitHub', 0),
+ fake_web_tab(QUrl('https://wikipedia.org'), 'Wikipedia', 1),
+ fake_web_tab(QUrl('https://duckduckgo.com'), 'DuckDuckGo', 2)
+ ]
+ tabbed_browser_stubs[1].widget.tabs = [
+ fake_web_tab(QUrl('https://wiki.archlinux.org'), 'ArchWiki', 0),
+ ]
+ model = miscmodels.tab_focus(info=info)
+ model.set_pattern('')
+ qtmodeltester.check(model)
+
+ parent = model.index(0, 0)
+ idx = model.index(1, 0, parent)
+
+ # sanity checks
+ assert model.data(parent) == "Tabs"
+ assert model.data(idx) == '2'
+
+ model.delete_cur_item(idx)
+ actual = [tab.url() for tab in tabbed_browser_stubs[0].widget.tabs]
+ assert actual == [QUrl('https://github.com'),
+ QUrl('https://duckduckgo.com')]
+
+
def test_tab_completion_not_sorted(qtmodeltester, fake_web_tab, win_registry,
tabbed_browser_stubs):
"""Ensure that the completion row order is the same as tab index order.
diff --git a/tests/unit/config/test_qtargs.py b/tests/unit/config/test_qtargs.py
index d95382624..076ff6e3c 100644
--- a/tests/unit/config/test_qtargs.py
+++ b/tests/unit/config/test_qtargs.py
@@ -249,7 +249,7 @@ class TestWebEngineArgs:
('single-process', True),
])
def test_process_model(self, config_stub, parser, process_model, added):
- config_stub.val.qt.process_model = process_model
+ config_stub.val.qt.chromium.process_model = process_model
parsed = parser.parse_args([])
args = qtargs.qt_args(parsed)
@@ -267,7 +267,7 @@ class TestWebEngineArgs:
('never', '--disable-low-end-device-mode'),
])
def test_low_end_device_mode(self, config_stub, parser, low_end_device_mode, arg):
- config_stub.val.qt.low_end_device_mode = low_end_device_mode
+ config_stub.val.qt.chromium.low_end_device_mode = low_end_device_mode
parsed = parser.parse_args([])
args = qtargs.qt_args(parsed)
@@ -277,6 +277,28 @@ class TestWebEngineArgs:
else:
assert arg in args
+ @pytest.mark.parametrize('sandboxing, arg', [
+ ('enable-all', None),
+ ('disable-seccomp-bpf', '--disable-seccomp-filter-sandbox'),
+ ('disable-all', '--no-sandbox'),
+ ])
+ def test_sandboxing(self, config_stub, parser, sandboxing, arg):
+ config_stub.val.qt.chromium.sandboxing = sandboxing
+ parsed = parser.parse_args([])
+ args = qtargs.qt_args(parsed)
+
+ remaining_flags = {
+ '--no-sandbox',
+ '--disable-seccomp-filter-sandbox',
+ }
+ if arg is not None:
+ remaining_flags.remove(arg)
+
+ if arg is not None:
+ assert arg in args
+
+ assert not set(args) & remaining_flags
+
@pytest.mark.parametrize('qt_version, referer, arg', [
# 'always' -> no arguments
('5.15.0', 'always', None),
diff --git a/tests/unit/misc/test_editor.py b/tests/unit/misc/test_editor.py
index 8e5597a0e..91bdce26b 100644
--- a/tests/unit/misc/test_editor.py
+++ b/tests/unit/misc/test_editor.py
@@ -19,6 +19,7 @@
"""Tests for qutebrowser.misc.editor."""
+import sys
import time
import pathlib
import os
@@ -55,31 +56,33 @@ class TestArg:
def test_placeholder(self, config_stub, editor):
"""Test starting editor with placeholder argument."""
- config_stub.val.editor.command = ['bin', 'foo', '{}', 'bar']
+ config_stub.val.editor.command = [sys.executable, 'foo', '{}', 'bar']
editor.edit("")
editor._proc._proc.start.assert_called_with(
- "bin", ["foo", editor._filename, "bar"])
+ sys.executable, ["foo", editor._filename, "bar"])
def test_placeholder_inline(self, config_stub, editor):
"""Test starting editor with placeholder arg inside of another arg."""
- config_stub.val.editor.command = ['bin', 'foo{}', 'bar']
+ config_stub.val.editor.command = [sys.executable, 'foo{}', 'bar']
editor.edit("")
editor._proc._proc.start.assert_called_with(
- "bin", ["foo" + editor._filename, "bar"])
+ sys.executable, ["foo" + editor._filename, "bar"])
class TestFileHandling:
"""Test creation/deletion of tempfile."""
- def test_ok(self, editor):
+ @pytest.mark.parametrize('remove_file', [True, False])
+ def test_ok(self, editor, remove_file, config_stub):
"""Test file handling when closing with an exit status == 0."""
+ config_stub.val.editor.remove_file = remove_file
editor.edit("")
filename = pathlib.Path(editor._filename)
assert filename.exists()
assert filename.name.startswith('qutebrowser-editor-')
editor._proc._proc.finished.emit(0, QProcess.NormalExit)
- assert not filename.exists()
+ assert filename.exists() != config_stub.val.editor.remove_file
@pytest.mark.parametrize('touch', [True, False])
def test_with_filename(self, editor, tmp_path, touch):
diff --git a/tests/unit/misc/test_guiprocess.py b/tests/unit/misc/test_guiprocess.py
index c664757fd..aaff5154e 100644
--- a/tests/unit/misc/test_guiprocess.py
+++ b/tests/unit/misc/test_guiprocess.py
@@ -319,8 +319,8 @@ def test_start_env(monkeypatch, qtbot, py_proc):
def test_start_detached(fake_proc):
"""Test starting a detached process."""
- cmd = 'foo'
- args = ['bar']
+ cmd = sys.executable
+ args = ['--version']
fake_proc._proc.startDetached.return_value = (True, 0)
fake_proc.start_detached(cmd, args)
fake_proc._proc.startDetached.assert_called_with(cmd, args, None)
diff --git a/tests/unit/utils/test_log.py b/tests/unit/utils/test_log.py
index 9f5a15065..bbc6b02db 100644
--- a/tests/unit/utils/test_log.py
+++ b/tests/unit/utils/test_log.py
@@ -27,7 +27,7 @@ import warnings
import dataclasses
import pytest
-import _pytest.logging
+import _pytest.logging # pylint: disable=import-private-name
from PyQt5 import QtCore
from qutebrowser import qutebrowser
diff --git a/tox.ini b/tox.ini
index f52d7b158..370adbc9e 100644
--- a/tox.ini
+++ b/tox.ini
@@ -14,7 +14,7 @@ setenv =
PYTEST_QT_API=pyqt5
pyqt{,512,513,514,515,5150}: LINK_PYQT_SKIP=true
cov: PYTEST_ADDOPTS=--cov --cov-report xml --cov-report=html --cov-report=
-passenv = PYTHON DISPLAY XAUTHORITY HOME USERNAME USER CI XDG_* QUTE_* DOCKER QT_QUICK_BACKEND PY_COLORS DBUS_SESSION_BUS_ADDRESS
+passenv = PYTHON DISPLAY XAUTHORITY HOME USERNAME USER CI XDG_* QUTE_* DOCKER QT_QUICK_BACKEND FORCE_COLOR DBUS_SESSION_BUS_ADDRESS
basepython =
py: {env:PYTHON:python3}
py3: {env:PYTHON:python3}
@@ -78,7 +78,7 @@ commands =
{[testenv:vulture]commands}
[testenv:pylint]
-basepython = {env:PYTHON:python3.8}
+basepython = {env:PYTHON:python3}
ignore_errors = true
passenv =
deps =