summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.github/workflows/ci.yml6
-rw-r--r--.gitignore1
-rw-r--r--doc/changelog.asciidoc15
-rw-r--r--doc/help/commands.asciidoc5
-rw-r--r--doc/userscripts.asciidoc2
-rw-r--r--misc/requirements/requirements-check-manifest.txt3
-rw-r--r--misc/requirements/requirements-dev.txt2
-rw-r--r--misc/requirements/requirements-flake8.txt2
-rw-r--r--misc/requirements/requirements-mypy.txt2
-rw-r--r--misc/requirements/requirements-pyqt-5.15.0.txt5
-rw-r--r--misc/requirements/requirements-pyqt-5.15.0.txt-raw4
-rw-r--r--misc/requirements/requirements-sphinx.txt3
-rw-r--r--misc/requirements/requirements-tests.txt4
-rw-r--r--misc/requirements/requirements-tests.txt-raw3
-rw-r--r--misc/requirements/requirements-tox.txt2
-rw-r--r--misc/userscripts/README.md7
-rwxr-xr-xmisc/userscripts/format_json2
-rwxr-xr-xmisc/userscripts/kodi111
-rwxr-xr-xmisc/userscripts/qr8
-rwxr-xr-xmisc/userscripts/readability-js4
-rw-r--r--qutebrowser/browser/commands.py3
-rw-r--r--qutebrowser/browser/greasemonkey.py45
-rw-r--r--qutebrowser/browser/shared.py6
-rw-r--r--qutebrowser/commands/userscripts.py1
-rw-r--r--qutebrowser/completion/models/listcategory.py6
-rw-r--r--scripts/dev/ci/docker/Dockerfile.j22
-rw-r--r--scripts/dev/misc_checks.py25
-rw-r--r--scripts/dev/recompile_requirements.py5
-rw-r--r--tests/helpers/fixtures.py2
-rw-r--r--tests/unit/javascript/test_greasemonkey.py7
-rw-r--r--tox.ini8
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)
diff --git a/tox.ini b/tox.ini
index 73baf89d7..abdce0b5b 100644
--- a/tox.ini
+++ b/tox.ini
@@ -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}