diff options
31 files changed, 245 insertions, 56 deletions
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 8e1f1341f..5978f1f97 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -90,7 +90,7 @@ jobs: - uses: actions/checkout@v2 - name: Set up problem matchers run: "python scripts/dev/ci/problemmatchers.py py38 ${{ runner.temp }}" - - run: tox -e py38 + - run: tox -e py tests: if: "!contains(github.event.head_commit.message, '[ci skip]')" @@ -112,6 +112,10 @@ jobs: - testenv: py38-pyqt514 os: ubuntu-20.04 python: 3.8 + ### PyQt 5.15.0 (Python 3.9) + - testenv: py39-pyqt5150 + os: ubuntu-20.04 + python: 3.9 ### PyQt 5.15 (Python 3.9, with coverage) - testenv: py39-pyqt515-cov os: ubuntu-20.04 diff --git a/.gitignore b/.gitignore index 50c67dee4..31c4ca3b7 100644 --- a/.gitignore +++ b/.gitignore @@ -35,6 +35,7 @@ Sessionx.vim /.pytest_cache /.testmondata /.hypothesis +/.benchmarks .mypy_cache /prof /venv diff --git a/doc/changelog.asciidoc b/doc/changelog.asciidoc index 9cfa73806..a58444606 100644 --- a/doc/changelog.asciidoc +++ b/doc/changelog.asciidoc @@ -64,6 +64,9 @@ Added settings might stop working. As a (currently undocumented) escape hatch, this version adds a `QUTE_DARKMODE_VARIANT=qt_515_2` environment variable which can be set to get the correct behavior in (transitive) situations like this. +- New userscripts: + - `kodi` to play videos in Kodi + - `qr` to generate a QR code of the current URL Changed ~~~~~~~ @@ -83,12 +86,21 @@ Changed pre-selected in the prompt shown by qutebrowser. - URLs such as `::1/foo` are now handled as a search term or local file rather than IPv6. Use `[::1]/foo` to force parsing as IPv6 instead. +- The `mkvenv.py` script now runs a "smoke test" after setting up the virtual + environment to ensure it's working as expected. +- Both qutebrowser userscripts and Greasemonkey scripts are now additionally + picked up from qutebrowser's config directory (the `userscripts` and + `greasemonkey` subdirectories of e.g. `~/.config/qutebrowser/`) rather than only + the data directory (the same subdirectories of e.g. + `~/.local/share/qutebrowser/`). Fixed ~~~~~ - With interpolated color settings (`colors.tabs.indicator.*` and `colors.downloads.*`), the alpha channel is now handled correctly. +- The `format_json` userscript now uses `env` in its shebang, making it work + correctly on systems where `bash` isn't located in `/bin`. v1.14.1 (unreleased) -------------------- @@ -178,6 +190,9 @@ Fixed installed, it was suggested to install `qt5-webengine-devtools`, which does not, in fact, exist. It's now correctly suggested to install `qt5-qtwebengine-devtools` instead. +- With Qt 5.15.2, lines/borders coming from the `readability-js` userscript + were invisible. This is now fixed by changing the border color to grey (with all + Qt versions). - Minor performance improvements. - (TODO) Fix for various functionality breaking in private windows with v1.14.0, after the last private window is closed. This includes: diff --git a/doc/help/commands.asciidoc b/doc/help/commands.asciidoc index 646d2f27b..f31283e9c 100644 --- a/doc/help/commands.asciidoc +++ b/doc/help/commands.asciidoc @@ -599,7 +599,7 @@ Syntax: +:greasemonkey-reload [*--force*]+ Re-read Greasemonkey scripts from disk. -The scripts are read from a 'greasemonkey' subdirectory in qutebrowser's data directory (see `:version`). +The scripts are read from a 'greasemonkey' subdirectory in qutebrowser's data or config directories (see `:version`). ==== optional arguments * +*-f*+, +*--force*+: For any scripts that have required dependencies, re-download them. @@ -1310,7 +1310,8 @@ Note that the command is *not* run in a shell, so things like `$VAR` or `> outpu * +*-v*+, +*--verbose*+: Show notifications when the command started/exited. * +*-o*+, +*--output*+: Show the output in a new tab. * +*-m*+, +*--output-messages*+: Show the output as messages. -* +*-d*+, +*--detach*+: Whether the command should be detached from qutebrowser. +* +*-d*+, +*--detach*+: Detach the command from qutebrowser so that it continues running when qutebrowser quits. + ==== count Given to userscripts as $QUTE_COUNT. diff --git a/doc/userscripts.asciidoc b/doc/userscripts.asciidoc index 2dc34402d..9bbc68ce0 100644 --- a/doc/userscripts.asciidoc +++ b/doc/userscripts.asciidoc @@ -18,7 +18,7 @@ mpv, a simple key binding to something like `:spawn mpv {url}` should suffice. Also note userscripts need to have the executable bit set (`chmod +x`) for qutebrowser to run them. -To call a userscript, it needs to be stored in your data directory under +To call a userscript, it needs to be stored in your config or data directory under `userscripts` (for example: `~/.local/share/qutebrowser/userscripts/myscript`), or just use an absolute path. diff --git a/misc/requirements/requirements-check-manifest.txt b/misc/requirements/requirements-check-manifest.txt index 8b8f6ba1a..e9376f0b1 100644 --- a/misc/requirements/requirements-check-manifest.txt +++ b/misc/requirements/requirements-check-manifest.txt @@ -2,8 +2,7 @@ build==0.1.0 check-manifest==0.45 -packaging==20.4 +packaging==20.7 pep517==0.9.1 pyparsing==2.4.7 -six==1.15.0 toml==0.10.2 diff --git a/misc/requirements/requirements-dev.txt b/misc/requirements/requirements-dev.txt index 6bc4325cf..578ab2b64 100644 --- a/misc/requirements/requirements-dev.txt +++ b/misc/requirements/requirements-dev.txt @@ -11,7 +11,7 @@ hunter==3.3.1 idna==2.10 jwcrypto==0.8 manhole==1.6.0 -packaging==20.4 +packaging==20.7 pycparser==2.20 Pympler==0.9 pyparsing==2.4.7 diff --git a/misc/requirements/requirements-flake8.txt b/misc/requirements/requirements-flake8.txt index d77bdbbf5..3427c0c69 100644 --- a/misc/requirements/requirements-flake8.txt +++ b/misc/requirements/requirements-flake8.txt @@ -6,7 +6,7 @@ flake8-bugbear==20.11.1 flake8-builtins==1.5.3 flake8-comprehensions==3.3.0 flake8-copyright==0.2.2 -flake8-debugger==3.2.1 +flake8-debugger==4.0.0 flake8-deprecated==1.3 flake8-docstrings==1.5.0 flake8-future-import==0.4.6 diff --git a/misc/requirements/requirements-mypy.txt b/misc/requirements/requirements-mypy.txt index b564cfa18..a046a0b5e 100644 --- a/misc/requirements/requirements-mypy.txt +++ b/misc/requirements/requirements-mypy.txt @@ -4,7 +4,7 @@ diff-cover==4.0.1 inflect==5.0.2 Jinja2==2.11.2 jinja2-pluralize==0.3.0 -lxml==4.6.1 +lxml==4.6.2 MarkupSafe==1.1.1 mypy==0.790 mypy-extensions==0.4.3 diff --git a/misc/requirements/requirements-pyqt-5.15.0.txt b/misc/requirements/requirements-pyqt-5.15.0.txt new file mode 100644 index 000000000..53a3782ae --- /dev/null +++ b/misc/requirements/requirements-pyqt-5.15.0.txt @@ -0,0 +1,5 @@ +# This file is automatically generated by scripts/dev/recompile_requirements.py + +PyQt5==5.15.2 # rq.filter: < 6 +PyQt5-sip==12.8.1 +PyQtWebEngine==5.15.0 # rq.filter: == 5.15.0 diff --git a/misc/requirements/requirements-pyqt-5.15.0.txt-raw b/misc/requirements/requirements-pyqt-5.15.0.txt-raw new file mode 100644 index 000000000..a9d16f08f --- /dev/null +++ b/misc/requirements/requirements-pyqt-5.15.0.txt-raw @@ -0,0 +1,4 @@ +#@ filter: PyQt5 < 6 +#@ filter: PyQtWebEngine == 5.15.0 +PyQt5 >= 5.15, < 6 +PyQtWebEngine == 5.15.0 diff --git a/misc/requirements/requirements-sphinx.txt b/misc/requirements/requirements-sphinx.txt index 463bb8e73..164311235 100644 --- a/misc/requirements/requirements-sphinx.txt +++ b/misc/requirements/requirements-sphinx.txt @@ -9,12 +9,11 @@ idna==2.10 imagesize==1.2.0 Jinja2==2.11.2 MarkupSafe==1.1.1 -packaging==20.4 +packaging==20.7 Pygments==2.7.2 pyparsing==2.4.7 pytz==2020.4 requests==2.25.0 -six==1.15.0 snowballstemmer==2.0.0 Sphinx==3.3.1 sphinxcontrib-applehelp==1.0.2 diff --git a/misc/requirements/requirements-tests.txt b/misc/requirements/requirements-tests.txt index 8b4eb0fba..bd77427d4 100644 --- a/misc/requirements/requirements-tests.txt +++ b/misc/requirements/requirements-tests.txt @@ -15,7 +15,7 @@ filelock==3.0.12 Flask==1.1.2 glob2==0.7 hunter==3.3.1 -hypothesis==5.41.3 +hypothesis==5.41.4 icdiff==1.9.1 idna==2.10 iniconfig==1.1.1 @@ -26,7 +26,7 @@ Mako==1.1.3 manhole==1.6.0 # MarkupSafe==1.1.1 more-itertools==8.6.0 -packaging==20.4 +packaging==20.7 parse==1.18.0 parse-type==0.5.2 pluggy==0.13.1 diff --git a/misc/requirements/requirements-tests.txt-raw b/misc/requirements/requirements-tests.txt-raw index 19becf94b..0c9e3928f 100644 --- a/misc/requirements/requirements-tests.txt-raw +++ b/misc/requirements/requirements-tests.txt-raw @@ -1,5 +1,6 @@ beautifulsoup4 -cheroot +# https://github.com/cherrypy/cheroot/issues/341 +cheroot!=8.4.8 coverage Flask hypothesis diff --git a/misc/requirements/requirements-tox.txt b/misc/requirements/requirements-tox.txt index 95dbdd654..86b3997f4 100644 --- a/misc/requirements/requirements-tox.txt +++ b/misc/requirements/requirements-tox.txt @@ -3,7 +3,7 @@ appdirs==1.4.4 distlib==0.3.1 filelock==3.0.12 -packaging==20.4 +packaging==20.7 pluggy==0.13.1 py==1.9.0 pyparsing==2.4.7 diff --git a/misc/userscripts/README.md b/misc/userscripts/README.md index a17f7164c..669bfa664 100644 --- a/misc/userscripts/README.md +++ b/misc/userscripts/README.md @@ -24,7 +24,7 @@ The following userscripts are included in the current directory. - [qutedmenu](./qutedmenu): Handle open -s && open -t with bemenu. - [readability](./readability): Executes python-readability on current page and opens the summary as new tab. -- [readability-js](./readability-js): Processes the current page with the readability +- [readability-js](./readability-js): Processes the current page with the readability library used in Firefox Reader View and opens the summary as new tab. - [ripbang](./ripbang): Adds DuckDuckGo bang as searchengine. - [rss](./rss): Keeps track of URLs in RSS feeds and opens new ones. @@ -32,6 +32,9 @@ The following userscripts are included in the current directory. - [tor_identity](./tor_identity): Change your tor identity. - [view_in_mpv](./view_in_mpv): Views the current web page in mpv using sensible mpv-flags. +- [qr](./qr): Show a QR code for the current webpage via + [qrencode](https://fukuchi.org/works/qrencode/). +- [kodi](./kodi): Play videos in Kodi. [castnow]: https://github.com/xat/castnow [youtube-dl]: https://rg3.github.io/youtube-dl/ @@ -67,6 +70,8 @@ The following userscripts can be found on their own repositories. and retrieve they when you want. - [doi](https://github.com/cadadr/configuration/blob/master/qutebrowser/userscripts/doi): Opens DOIs on Sci-Hub. +- [1password](https://github.com/tomoakley/dotfiles/blob/master/qutebrowser/userscripts/1password): + Integration with 1password on macOS. [Zotero]: https://www.zotero.org/ [Pocket]: https://getpocket.com/ diff --git a/misc/userscripts/format_json b/misc/userscripts/format_json index 541408c70..8a83c25fa 100755 --- a/misc/userscripts/format_json +++ b/misc/userscripts/format_json @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash set -euo pipefail # # Behavior: diff --git a/misc/userscripts/kodi b/misc/userscripts/kodi new file mode 100755 index 000000000..63fcc81fe --- /dev/null +++ b/misc/userscripts/kodi @@ -0,0 +1,111 @@ +#!/usr/bin/env bash +# +# Behavior: +# A qutebrowser userscript that plays Twitch, YouTube or Vimeo videos in Kodi via its +# API. +# +# Requirements: +# awk +# bash +# curl +# +# Kodi setup: +# Settings -> Services -> Control +# enable 'Allow remote control via HTTP' +# set Username and Password +# enable 'Allow remote control from applications on this system' +# Optional yet recommended, setup SSL within Kodi over via a proxy webserver +# +# userscript setup: +# create ~/.config/qutebrowser/kodi_rc with host and authentication information like: +# +# HOST="http://127.0.0.1:8080" +# or +# HOST="https://kodi.example.com" +# +# AUTH="user:password" +# or +# AUTH="bas64authenticationinformation" +# +# The base64 authentication is the output of +# `echo -ne "user:password" |base64 --wrap 0` +# reminder base64 is not encryption +# +# For vim users you might want to add '# vim: set nospell filetype=bash' to the +# kodi_rc file. +# +# qutebrowser setup: +# in ~/.config/qutebrowser/config.py add something like +# +# to send video link via hints: +# config.bind('X', 'hint links userscript kodi') +# to send current URL: +# config.bind('X', 'spawn --userscript kodi') +# +# troubleshooting: +# Errors detected within this userscript with have an exit of 231. All other exit +# codes will come from curl or awk. To test that the kodi_rc file is set up +# correctly, run the following command. It will display a 'It works!' notification within Kodi. +# +# source ~/.config/qutebrowser/kodi_rc ; curl --request POST "$HOST"/jsonrpc --header "Authorization: Basic $AUTH" --header "Content-Type: application/json" --data '{"id":1,"jsonrpc":"2.0","method":"GUI.ShowNotification","params":{"title":"It works!","message":"both HOST and AUTH are correct"}}' +# +# In case you miss the notification in Kodi the successful response is: +# +# {"id":1,"jsonrpc":"2.0","result":"OK"} +# +# Note, curl will display errors for some problems, but not all. + +if [[ -z "$QUTE_FIFO" ]] ; then + echo "This script is designed to run as a qutebrowser userscript, not as a standalone script." + exit 231 +fi + +# configuration loading adapted from the password_fill userscript +QUTE_CONFIG_DIR=${QUTE_CONFIG_DIR:-${XDG_CONFIG_HOME:-$HOME/.config}/qutebrowser/} +KODI_CONFIG=${PWFILL_CONFIG:-${QUTE_CONFIG_DIR}/kodi_rc} +if [[ -f "$KODI_CONFIG" ]] ; then + # shellcheck source=/dev/null + source "$KODI_CONFIG" + if [[ -z "$HOST" || -z "$AUTH" ]] ; then + echo "message-error 'HOST and/or AUTH not set in $KODI_CONFIG'" > "$QUTE_FIFO" + exit 231 + fi +else + echo "message-error '$KODI_CONFIG not found'" > "$QUTE_FIFO" + exit 231 +fi + +# get real URL from twitter links +if [[ "$QUTE_URL" =~ ^https:\/\/t\.co ]] ; then + QUTE_URL=$(curl -o /dev/null --silent --head --write-out '%{redirect_url}' "$QUTE_URL" ) +fi + +# regex from https://github.com/dirkjanm/firefox-send-to-xbmc/blob/master/webextension/main.js +if [[ "$QUTE_URL" =~ ^.*twitch.tv\/([a-zA-Z0-9_]+)$ ]] ; then + NAME="${BASH_REMATCH[1]}" + JSON='{"jsonrpc":"2.0","method":"Player.Open","params":{"item":{"file":"plugin://plugin.video.twitch/?mode=play&channel_name='$NAME'"}},"id":"2"}' + +elif [[ "$QUTE_URL" =~ ^.*twitch.tv\/videos\/([0-9]+)$ ]] ; then + NAME="${BASH_REMATCH[1]}" + JSON='{"jsonrpc":"2.0","method":"Player.Open","params":{"item":{"file":"plugin://plugin.video.twitch/?mode=play&video_id='$NAME'"}},"id":"2"}' + +elif [[ "$QUTE_URL" =~ ^.*vimeo.com\/([0-9]+) ]] ; then + NAME="${BASH_REMATCH[1]}" + JSON='{"jsonrpc":"2.0","method":"Player.Open","params":{"item":{"file":"plugin://plugin.video.vimeo/play/?video_id='$NAME'"}},"id":"2"}' + +elif [[ "$QUTE_URL" =~ ^.*youtu.be\/|v\/|u\/\w\/|embed\/|watch\?v=([^#\&\?]*).* ]] ; then + NAME="${BASH_REMATCH[1]}" + JSON='{"jsonrpc":"2.0","method":"Player.Open","params":{"item":{"file":"plugin://plugin.video.youtube/play/?video_id='$NAME'"}},"id":"2"}' +fi + +if [[ "$JSON" ]] ; then + curl \ + --request POST "$HOST"/jsonrpc \ + --header "Authorization: Basic $AUTH" \ + --header "Content-Type: application/json" \ + --data "$JSON" \ + --silent > /dev/null +else + URL=$(echo "$QUTE_URL" |awk -F/ '{print $3}') + echo "message-warning 'kodi userscript does not support this $URL'" > "$QUTE_FIFO" +fi diff --git a/misc/userscripts/qr b/misc/userscripts/qr new file mode 100755 index 000000000..84215249b --- /dev/null +++ b/misc/userscripts/qr @@ -0,0 +1,8 @@ +#!/usr/bin/env bash + +pngfile=$(mktemp --suffix=.png) +trap 'rm -f "$pngfile"' EXIT + +qrencode -t PNG -o "$pngfile" -s 10 "$QUTE_URL" +echo ":open -t file:///$pngfile" >> "$QUTE_FIFO" +sleep 1 # give qutebrowser time to open the file before it gets removed diff --git a/misc/userscripts/readability-js b/misc/userscripts/readability-js index e189e5ee4..310d1c081 100755 --- a/misc/userscripts/readability-js +++ b/misc/userscripts/readability-js @@ -57,7 +57,7 @@ const HEADER = ` table, th, td { - border: 1px solid currentColor; + border: 1px solid grey; border-collapse: collapse; padding: 6px; vertical-align: top; @@ -77,7 +77,7 @@ const HEADER = ` background-color: #dddddd; } blockquote { - border-inline-start: 2px solid #333333 !important; + border-inline-start: 2px solid grey !important; padding: 0; padding-inline-start: 16px; margin-inline-start: 24px; diff --git a/qutebrowser/browser/commands.py b/qutebrowser/browser/commands.py index 34c078d89..18777e250 100644 --- a/qutebrowser/browser/commands.py +++ b/qutebrowser/browser/commands.py @@ -1057,7 +1057,8 @@ class CommandDispatcher: verbose: Show notifications when the command started/exited. output: Show the output in a new tab. output_messages: Show the output as messages. - detach: Whether the command should be detached from qutebrowser. + detach: Detach the command from qutebrowser so that it continues + running when qutebrowser quits. cmdline: The commandline to execute. count: Given to userscripts as $QUTE_COUNT. """ diff --git a/qutebrowser/browser/greasemonkey.py b/qutebrowser/browser/greasemonkey.py index 0305bd589..df8b2b0c2 100644 --- a/qutebrowser/browser/greasemonkey.py +++ b/qutebrowser/browser/greasemonkey.py @@ -41,9 +41,12 @@ from qutebrowser.misc import objects gm_manager = cast('GreasemonkeyManager', None) -def _scripts_dir(): +def _scripts_dirs(): """Get the directory of the scripts.""" - return os.path.join(standarddir.data(), 'greasemonkey') + return [ + os.path.join(standarddir.data(), 'greasemonkey'), + os.path.join(standarddir.config(), 'greasemonkey'), + ] class GreasemonkeyScript: @@ -277,18 +280,19 @@ class GreasemonkeyManager(QObject): self._run_end = [] self._run_idle = [] - scripts_dir = os.path.abspath(_scripts_dir()) - log.greasemonkey.debug("Reading scripts from: {}".format(scripts_dir)) - for script_filename in glob.glob(os.path.join(scripts_dir, '*.js')): - if not os.path.isfile(script_filename): - continue - script_path = os.path.join(scripts_dir, script_filename) - with open(script_path, encoding='utf-8-sig') as script_file: - script = GreasemonkeyScript.parse(script_file.read(), - script_filename) - if not script.name: - script.name = script_filename - self.add_script(script, force) + for scripts_dir in _scripts_dirs(): + scripts_dir = os.path.abspath(scripts_dir) + log.greasemonkey.debug("Reading scripts from: {}".format(scripts_dir)) + for script_filename in glob.glob(os.path.join(scripts_dir, '*.js')): + if not os.path.isfile(script_filename): + continue + script_path = os.path.join(scripts_dir, script_filename) + with open(script_path, encoding='utf-8-sig') as script_file: + script = GreasemonkeyScript.parse(script_file.read(), + script_filename) + if not script.name: + script.name = script_filename + self.add_script(script, force) self.scripts_reloaded.emit() def add_script(self, script, force=False): @@ -325,7 +329,7 @@ class GreasemonkeyManager(QObject): log.greasemonkey.debug("Loaded script: {}".format(script.name)) def _required_url_to_file_path(self, url): - requires_dir = os.path.join(_scripts_dir(), 'requires') + requires_dir = os.path.join(_scripts_dirs()[0], 'requires') if not os.path.exists(requires_dir): os.mkdir(requires_dir) return os.path.join(requires_dir, utils.sanitize_filename(url)) @@ -426,7 +430,7 @@ def greasemonkey_reload(force=False): """Re-read Greasemonkey scripts from disk. The scripts are read from a 'greasemonkey' subdirectory in - qutebrowser's data directory (see `:version`). + qutebrowser's data or config directories (see `:version`). Args: force: For any scripts that have required dependencies, @@ -440,7 +444,8 @@ def init(): global gm_manager gm_manager = GreasemonkeyManager() - try: - os.mkdir(_scripts_dir()) - except FileExistsError: - pass + for scripts_dir in _scripts_dirs(): + try: + os.mkdir(scripts_dir) + except FileExistsError: + pass diff --git a/qutebrowser/browser/shared.py b/qutebrowser/browser/shared.py index 193a2a0e0..9234e82d8 100644 --- a/qutebrowser/browser/shared.py +++ b/qutebrowser/browser/shared.py @@ -82,7 +82,7 @@ def javascript_confirm(url, js_msg, abort_on): raise CallSuper msg = 'From <b>{}</b>:<br/>{}'.format(html.escape(url.toDisplayString()), - js_msg) + html.escape(js_msg)) urlstr = url.toString(QUrl.RemovePassword | QUrl.FullyEncoded) ans = message.ask('Javascript confirm', msg, mode=usertypes.PromptMode.yesno, @@ -99,7 +99,7 @@ def javascript_prompt(url, js_msg, default, abort_on): return (False, "") msg = '<b>{}</b> asks:<br/>{}'.format(html.escape(url.toDisplayString()), - js_msg) + html.escape(js_msg)) urlstr = url.toString(QUrl.RemovePassword | QUrl.FullyEncoded) answer = message.ask('Javascript prompt', msg, mode=usertypes.PromptMode.text, @@ -122,7 +122,7 @@ def javascript_alert(url, js_msg, abort_on): return msg = 'From <b>{}</b>:<br/>{}'.format(html.escape(url.toDisplayString()), - js_msg) + html.escape(js_msg)) urlstr = url.toString(QUrl.RemovePassword | QUrl.FullyEncoded) message.ask('Javascript alert', msg, mode=usertypes.PromptMode.alert, abort_on=abort_on, url=urlstr) diff --git a/qutebrowser/commands/userscripts.py b/qutebrowser/commands/userscripts.py index ce25d7d28..6d2c2f147 100644 --- a/qutebrowser/commands/userscripts.py +++ b/qutebrowser/commands/userscripts.py @@ -395,6 +395,7 @@ def _lookup_path(cmd): directories = [ os.path.join(standarddir.data(), "userscripts"), os.path.join(standarddir.data(system=True), "userscripts"), + os.path.join(standarddir.config(), "userscripts"), ] for directory in directories: cmd_path = os.path.join(directory, cmd) diff --git a/qutebrowser/completion/models/listcategory.py b/qutebrowser/completion/models/listcategory.py index 79dc0770a..d4193b6d8 100644 --- a/qutebrowser/completion/models/listcategory.py +++ b/qutebrowser/completion/models/listcategory.py @@ -22,7 +22,7 @@ import re from typing import Iterable, Tuple -from PyQt5.QtCore import Qt, QSortFilterProxyModel, QRegExp +from PyQt5.QtCore import QSortFilterProxyModel, QRegularExpression from PyQt5.QtGui import QStandardItem, QStandardItemModel from PyQt5.QtWidgets import QWidget @@ -63,9 +63,9 @@ class ListCategory(QSortFilterProxyModel): val = re.sub(r' +', r' ', val) # See #1919 val = re.escape(val) val = val.replace(r'\ ', '.*') - rx = QRegExp(val, Qt.CaseInsensitive) + rx = QRegularExpression(val, QRegularExpression.CaseInsensitiveOption) qtutils.ensure_valid(rx) - self.setFilterRegExp(rx) + self.setFilterRegularExpression(rx) self.invalidate() sortcol = 0 self.sort(sortcol) diff --git a/scripts/dev/ci/docker/Dockerfile.j2 b/scripts/dev/ci/docker/Dockerfile.j2 index 412c42cf2..1835f0a2f 100644 --- a/scripts/dev/ci/docker/Dockerfile.j2 +++ b/scripts/dev/ci/docker/Dockerfile.j2 @@ -24,4 +24,4 @@ WORKDIR /home/user CMD git clone /outside qutebrowser.git && \ cd qutebrowser.git && \ - tox -e py38 + tox -e py diff --git a/scripts/dev/misc_checks.py b/scripts/dev/misc_checks.py index 14373f94f..7b9ce769b 100644 --- a/scripts/dev/misc_checks.py +++ b/scripts/dev/misc_checks.py @@ -274,12 +274,35 @@ def check_userscripts_descriptions(_args: argparse.Namespace = None) -> bool: return ok +def check_userscript_shebangs(_args: argparse.Namespace) -> bool: + """Check that we're using /usr/bin/env in shebangs.""" + ok = True + folder = pathlib.Path('misc/userscripts') + + for sub in folder.iterdir(): + if sub.is_dir() or sub.name == 'README.md': + continue + + with sub.open('r', encoding='utf-8') as f: + shebang = f.readline() + assert shebang.startswith('#!'), shebang + binary = shebang.split()[0][2:] + + if binary not in ['/bin/sh', '/usr/bin/env']: + bin_name = pathlib.Path(binary).name + print(f"In {sub}, use #!/usr/bin/env {bin_name} instead of #!{binary}") + ok = False + + return ok + + def main() -> int: checkers = { 'git': check_git, 'vcs': check_vcs_conflict, 'spelling': check_spelling, - 'userscripts': check_userscripts_descriptions, + 'userscript-descriptions': check_userscripts_descriptions, + 'userscript-shebangs': check_userscript_shebangs, 'changelog-urls': check_changelog_urls, } diff --git a/scripts/dev/recompile_requirements.py b/scripts/dev/recompile_requirements.py index 094a69ebe..87740c5bb 100644 --- a/scripts/dev/recompile_requirements.py +++ b/scripts/dev/recompile_requirements.py @@ -73,7 +73,7 @@ CHANGELOG_URLS = { 'pytest-bdd': 'https://github.com/pytest-dev/pytest-bdd/blob/master/CHANGES.rst', 'snowballstemmer': 'https://github.com/snowballstem/snowball/blob/master/NEWS', 'virtualenv': 'https://virtualenv.pypa.io/en/latest/changelog.html', - 'packaging': 'https://pypi.org/project/packaging/', + 'packaging': 'https://packaging.pypa.io/en/latest/changelog.html', 'build': 'https://github.com/pypa/build/commits/master', 'attrs': 'http://www.attrs.org/en/stable/changelog.html', 'Jinja2': 'https://github.com/pallets/jinja/blob/master/CHANGES.rst', @@ -130,7 +130,7 @@ CHANGELOG_URLS = { '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://lxml.de/4.6/changes-4.6.0.html', + 'lxml': 'https://lxml.de/index.html#old-versions', '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', @@ -357,6 +357,7 @@ def _get_changed_files(): def parse_versioned_line(line): """Parse a requirements.txt line into name/version.""" if '==' in line: + line = line.rsplit('#', maxsplit=1)[0] # Strip comments name, version = line.split('==') if ';' in version: # pip environment markers version = version.split(';')[0].strip() diff --git a/tests/helpers/fixtures.py b/tests/helpers/fixtures.py index 3c0623dfb..6f80099bb 100644 --- a/tests/helpers/fixtures.py +++ b/tests/helpers/fixtures.py @@ -166,7 +166,7 @@ def fake_web_tab(stubs, tab_registry, mode_manager, qapp): @pytest.fixture -def greasemonkey_manager(monkeypatch, data_tmpdir): +def greasemonkey_manager(monkeypatch, data_tmpdir, config_tmpdir): gm_manager = greasemonkey.GreasemonkeyManager() monkeypatch.setattr(greasemonkey, 'gm_manager', gm_manager) diff --git a/tests/unit/javascript/test_greasemonkey.py b/tests/unit/javascript/test_greasemonkey.py index 5d549ca68..5f2c94b56 100644 --- a/tests/unit/javascript/test_greasemonkey.py +++ b/tests/unit/javascript/test_greasemonkey.py @@ -41,12 +41,15 @@ test_gm_script = r""" console.log("Script is running."); """ -pytestmark = pytest.mark.usefixtures('data_tmpdir') +pytestmark = [ + pytest.mark.usefixtures('data_tmpdir'), + pytest.mark.usefixtures('config_tmpdir') +] def _save_script(script_text, filename): # pylint: disable=no-member - file_path = py.path.local(greasemonkey._scripts_dir()) / filename + file_path = py.path.local(greasemonkey._scripts_dirs()[0]) / filename # pylint: enable=no-member file_path.write_text(script_text, encoding='utf-8', ensure=True) @@ -12,11 +12,12 @@ minversion = 3.15 [testenv] setenv = PYTEST_QT_API=pyqt5 - pyqt{,512,513,514,515}: LINK_PYQT_SKIP=true - pyqt{,512,513,514,515}: QUTE_BDD_WEBENGINE=true + pyqt{,512,513,514,515,5150}: LINK_PYQT_SKIP=true + pyqt{,512,513,514,515,5150}: QUTE_BDD_WEBENGINE=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 basepython = + py: {env:PYTHON:python3} py3: {env:PYTHON:python3} py36: {env:PYTHON:python3.6} py37: {env:PYTHON:python3.7} @@ -30,6 +31,7 @@ deps = pyqt513: -r{toxinidir}/misc/requirements/requirements-pyqt-5.13.txt pyqt514: -r{toxinidir}/misc/requirements/requirements-pyqt-5.14.txt pyqt515: -r{toxinidir}/misc/requirements/requirements-pyqt-5.15.txt + pyqt5150: -r{toxinidir}/misc/requirements/requirements-pyqt-5.15.0.txt commands = {envpython} scripts/link_pyqt.py --tox {envdir} {envpython} -bb -m pytest {posargs:tests} @@ -44,7 +46,7 @@ basepython = {env:PYTHON:python3} passenv = HOME deps = commands = - {envpython} scripts/dev/misc_checks.py all + {envpython} scripts/dev/misc_checks.py {posargs:all} [testenv:vulture] basepython = {env:PYTHON:python3} |