diff options
author | toofar <toofar@spalge.com> | 2023-11-15 20:34:00 +1300 |
---|---|---|
committer | GitHub <noreply@github.com> | 2023-11-15 20:34:00 +1300 |
commit | c13089b64b103fe0e5c8526d2fbfbb5850c8c7d8 (patch) | |
tree | 1b3665da9a9523544dcf89fe03e427d0466b2657 | |
parent | d46c87633271d70dac45b596e443997f45d8b8dd (diff) | |
parent | 5cc948aeb5702626a26ea434ca433a13bdbfd822 (diff) | |
download | qutebrowser-c13089b64b103fe0e5c8526d2fbfbb5850c8c7d8.tar.gz qutebrowser-c13089b64b103fe0e5c8526d2fbfbb5850c8c7d8.zip |
Merge pull request #7990 from qutebrowser/update-dependencies
Update dependencies
22 files changed, 125 insertions, 68 deletions
diff --git a/misc/requirements/requirements-dev.txt b/misc/requirements/requirements-dev.txt index 3d6a7760a..fae3a3762 100644 --- a/misc/requirements/requirements-dev.txt +++ b/misc/requirements/requirements-dev.txt @@ -4,17 +4,17 @@ build==1.0.3 bump2version==1.0.1 certifi==2023.7.22 cffi==1.16.0 -charset-normalizer==3.3.1 +charset-normalizer==3.3.2 cryptography==41.0.5 docutils==0.20.1 github3.py==4.0.1 hunter==3.6.1 idna==3.4 importlib-metadata==6.8.0 -importlib-resources==6.1.0 +importlib-resources==6.1.1 jaraco.classes==3.3.0 jeepney==0.8.0 -keyring==24.2.0 +keyring==24.3.0 manhole==1.8.0 markdown-it-py==3.0.0 mdurl==0.1.2 diff --git a/misc/requirements/requirements-flake8.txt b/misc/requirements/requirements-flake8.txt index 95a9cb382..6995f7ac5 100644 --- a/misc/requirements/requirements-flake8.txt +++ b/misc/requirements/requirements-flake8.txt @@ -3,10 +3,10 @@ attrs==23.1.0 flake8==6.1.0 flake8-bugbear==23.9.16 -flake8-builtins==2.1.0 +flake8-builtins==2.2.0 flake8-comprehensions==3.14.0 flake8-debugger==4.1.2 -flake8-deprecated==2.1.0 +flake8-deprecated==2.2.1 flake8-docstrings==1.7.0 flake8-future-import==0.4.7 flake8-plugin-utils==1.3.3 diff --git a/misc/requirements/requirements-mypy.txt b/misc/requirements/requirements-mypy.txt index 8b5b68b56..b8c88cfc0 100644 --- a/misc/requirements/requirements-mypy.txt +++ b/misc/requirements/requirements-mypy.txt @@ -2,7 +2,7 @@ chardet==5.2.0 diff_cover==8.0.0 -importlib-resources==6.1.0 +importlib-resources==6.1.1 Jinja2==3.1.2 lxml==4.9.3 MarkupSafe==2.1.3 @@ -14,8 +14,8 @@ PyQt5-stubs==5.15.6.0 tomli==2.0.1 types-colorama==0.4.15.12 types-docutils==0.20.0.3 -types-Pygments==2.16.0.0 +types-Pygments==2.16.0.1 types-PyYAML==6.0.12.12 -types-setuptools==68.2.0.0 +types-setuptools==68.2.0.1 typing_extensions==4.8.0 zipp==3.17.0 diff --git a/misc/requirements/requirements-pyinstaller.txt b/misc/requirements/requirements-pyinstaller.txt index d62b99df5..d1a2c18c9 100644 --- a/misc/requirements/requirements-pyinstaller.txt +++ b/misc/requirements/requirements-pyinstaller.txt @@ -3,6 +3,6 @@ altgraph==0.17.4 importlib-metadata==6.8.0 packaging==23.2 -pyinstaller==6.1.0 +pyinstaller==6.2.0 pyinstaller-hooks-contrib==2023.10 zipp==3.17.0 diff --git a/misc/requirements/requirements-pylint.txt b/misc/requirements/requirements-pylint.txt index 6cdd658f8..38c227103 100644 --- a/misc/requirements/requirements-pylint.txt +++ b/misc/requirements/requirements-pylint.txt @@ -3,7 +3,7 @@ astroid==3.0.1 certifi==2023.7.22 cffi==1.16.0 -charset-normalizer==3.3.1 +charset-normalizer==3.3.2 cryptography==41.0.5 dill==0.3.7 github3.py==4.0.1 @@ -11,7 +11,7 @@ idna==3.4 isort==5.12.0 mccabe==0.7.0 pefile==2023.2.7 -platformdirs==3.11.0 +platformdirs==4.0.0 pycparser==2.21 PyJWT==2.8.0 pylint==3.0.2 @@ -20,7 +20,7 @@ python-dateutil==2.8.2 requests==2.31.0 six==1.16.0 tomli==2.0.1 -tomlkit==0.12.1 +tomlkit==0.12.2 typing_extensions==4.8.0 uritemplate==4.1.1 # urllib3==2.0.7 diff --git a/misc/requirements/requirements-pyqt-6.6.txt b/misc/requirements/requirements-pyqt-6.6.txt new file mode 100644 index 000000000..0a9c72e25 --- /dev/null +++ b/misc/requirements/requirements-pyqt-6.6.txt @@ -0,0 +1,7 @@ +# This file is automatically generated by scripts/dev/recompile_requirements.py + +PyQt6==6.6.0 +PyQt6-Qt6==6.6.0 +PyQt6-sip==13.6.0 +PyQt6-WebEngine==6.6.0 +PyQt6-WebEngine-Qt6==6.6.0 diff --git a/misc/requirements/requirements-pyqt-6.6.txt-raw b/misc/requirements/requirements-pyqt-6.6.txt-raw new file mode 100644 index 000000000..7cfe6d34c --- /dev/null +++ b/misc/requirements/requirements-pyqt-6.6.txt-raw @@ -0,0 +1,4 @@ +PyQt6 >= 6.6, < 6.7 +PyQt6-Qt6 >= 6.6, < 6.7 +PyQt6-WebEngine >= 6.6, < 6.7 +PyQt6-WebEngine-Qt6 >= 6.6, < 6.7 diff --git a/misc/requirements/requirements-pyqt-6.txt b/misc/requirements/requirements-pyqt-6.txt index 5dca9ab74..0a9c72e25 100644 --- a/misc/requirements/requirements-pyqt-6.txt +++ b/misc/requirements/requirements-pyqt-6.txt @@ -1,7 +1,7 @@ # This file is automatically generated by scripts/dev/recompile_requirements.py -PyQt6==6.5.3 -PyQt6-Qt6==6.5.3 +PyQt6==6.6.0 +PyQt6-Qt6==6.6.0 PyQt6-sip==13.6.0 -PyQt6-WebEngine==6.5.0 -PyQt6-WebEngine-Qt6==6.5.3 +PyQt6-WebEngine==6.6.0 +PyQt6-WebEngine-Qt6==6.6.0 diff --git a/misc/requirements/requirements-pyqt.txt b/misc/requirements/requirements-pyqt.txt index 5dca9ab74..0a9c72e25 100644 --- a/misc/requirements/requirements-pyqt.txt +++ b/misc/requirements/requirements-pyqt.txt @@ -1,7 +1,7 @@ # This file is automatically generated by scripts/dev/recompile_requirements.py -PyQt6==6.5.3 -PyQt6-Qt6==6.5.3 +PyQt6==6.6.0 +PyQt6-Qt6==6.6.0 PyQt6-sip==13.6.0 -PyQt6-WebEngine==6.5.0 -PyQt6-WebEngine-Qt6==6.5.3 +PyQt6-WebEngine==6.6.0 +PyQt6-WebEngine-Qt6==6.6.0 diff --git a/misc/requirements/requirements-pyroma.txt b/misc/requirements/requirements-pyroma.txt index c171b400e..576c5574f 100644 --- a/misc/requirements/requirements-pyroma.txt +++ b/misc/requirements/requirements-pyroma.txt @@ -2,7 +2,7 @@ build==1.0.3 certifi==2023.7.22 -charset-normalizer==3.3.1 +charset-normalizer==3.3.2 docutils==0.20.1 idna==3.4 importlib-metadata==6.8.0 @@ -12,6 +12,6 @@ pyproject_hooks==1.0.0 pyroma==4.2 requests==2.31.0 tomli==2.0.1 -trove-classifiers==2023.10.18 +trove-classifiers==2023.11.9 urllib3==2.0.7 zipp==3.17.0 diff --git a/misc/requirements/requirements-sphinx.txt b/misc/requirements/requirements-sphinx.txt index 77d982519..3d5011d12 100644 --- a/misc/requirements/requirements-sphinx.txt +++ b/misc/requirements/requirements-sphinx.txt @@ -3,7 +3,7 @@ alabaster==0.7.13 Babel==2.13.1 certifi==2023.7.22 -charset-normalizer==3.3.1 +charset-normalizer==3.3.2 docutils==0.20.1 idna==3.4 imagesize==1.4.1 diff --git a/misc/requirements/requirements-tests.txt b/misc/requirements/requirements-tests.txt index 49028afa4..168acac36 100644 --- a/misc/requirements/requirements-tests.txt +++ b/misc/requirements/requirements-tests.txt @@ -2,25 +2,25 @@ attrs==23.1.0 beautifulsoup4==4.12.2 -blinker==1.6.3 +blinker==1.7.0 certifi==2023.7.22 -charset-normalizer==3.3.1 +charset-normalizer==3.3.2 cheroot==10.0.0 click==8.1.7 coverage==7.3.2 exceptiongroup==1.1.3 execnet==2.0.2 -filelock==3.13.0 +filelock==3.13.1 Flask==3.0.0 hunter==3.6.1 -hypothesis==6.88.1 +hypothesis==6.88.3 idna==3.4 importlib-metadata==6.8.0 iniconfig==2.0.0 itsdangerous==2.1.2 -jaraco.functools==3.9.0 +jaraco.functools==4.0.0 # Jinja2==3.1.2 -Mako==1.2.4 +Mako==1.3.0 manhole==1.8.0 # MarkupSafe==2.1.3 more-itertools==10.1.0 @@ -39,7 +39,7 @@ pytest-mock==3.12.0 pytest-qt==4.2.0 pytest-repeat==0.9.3 pytest-rerunfailures==12.0 -pytest-xdist==3.3.1 +pytest-xdist==3.4.0 pytest-xvfb==3.0.0 PyVirtualDisplay==3.0 requests==2.31.0 @@ -47,7 +47,7 @@ requests-file==1.5.1 six==1.16.0 sortedcontainers==2.4.0 soupsieve==2.5 -tldextract==5.0.1 +tldextract==5.1.0 toml==0.10.2 tomli==2.0.1 typing_extensions==4.8.0 diff --git a/misc/requirements/requirements-tox.txt b/misc/requirements/requirements-tox.txt index b9a2c0508..0b1b5277d 100644 --- a/misc/requirements/requirements-tox.txt +++ b/misc/requirements/requirements-tox.txt @@ -4,7 +4,7 @@ cachetools==5.3.2 chardet==5.2.0 colorama==0.4.6 distlib==0.3.7 -filelock==3.13.0 +filelock==3.13.1 packaging==23.2 pip==23.3.1 platformdirs==3.11.0 @@ -14,4 +14,4 @@ setuptools==68.2.2 tomli==2.0.1 tox==4.11.3 virtualenv==20.24.6 -wheel==0.41.2 +wheel==0.41.3 diff --git a/misc/requirements/requirements-yamllint.txt b/misc/requirements/requirements-yamllint.txt index fd9ea256f..32589d26f 100644 --- a/misc/requirements/requirements-yamllint.txt +++ b/misc/requirements/requirements-yamllint.txt @@ -2,4 +2,4 @@ pathspec==0.11.2 PyYAML==6.0.1 -yamllint==1.32.0 +yamllint==1.33.0 diff --git a/qutebrowser/browser/browsertab.py b/qutebrowser/browser/browsertab.py index 1312275dc..4d14c9cd7 100644 --- a/qutebrowser/browser/browsertab.py +++ b/qutebrowser/browser/browsertab.py @@ -2,7 +2,7 @@ # # SPDX-License-Identifier: GPL-3.0-or-later -"""Base class for a wrapper over QWebView/QWebEngineView.""" +"""Base class for a wrapper over WebView/WebEngineView.""" import enum import pathlib @@ -22,10 +22,9 @@ from qutebrowser.qt.network import QNetworkAccessManager if TYPE_CHECKING: from qutebrowser.qt.webkit import QWebHistory, QWebHistoryItem - from qutebrowser.qt.webkitwidgets import QWebPage, QWebView + from qutebrowser.qt.webkitwidgets import QWebPage from qutebrowser.qt.webenginecore import ( QWebEngineHistory, QWebEngineHistoryItem, QWebEnginePage) - from qutebrowser.qt.webenginewidgets import QWebEngineView from qutebrowser.keyinput import modeman from qutebrowser.config import config, websettings @@ -38,10 +37,12 @@ from qutebrowser.qt import sip if TYPE_CHECKING: from qutebrowser.browser import webelem from qutebrowser.browser.inspector import AbstractWebInspector + from qutebrowser.browser.webengine.webview import WebEngineView + from qutebrowser.browser.webkit.webview import WebView tab_id_gen = itertools.count(0) -_WidgetType = Union["QWebView", "QWebEngineView"] +_WidgetType = Union["WebView", "WebEngineView"] def create(win_id: int, @@ -964,7 +965,7 @@ class AbstractTabPrivate: class AbstractTab(QWidget): - """An adapter for QWebView/QWebEngineView representing a single tab.""" + """An adapter for WebView/WebEngineView representing a single tab.""" #: Signal emitted when a website requests to close this tab. window_close_requested = pyqtSignal() @@ -1058,7 +1059,7 @@ class AbstractTab(QWidget): self.before_load_started.connect(self._on_before_load_started) - def _set_widget(self, widget: Union["QWebView", "QWebEngineView"]) -> None: + def _set_widget(self, widget: _WidgetType) -> None: # pylint: disable=protected-access self._widget = widget # FIXME:v4 ignore needed for QtWebKit diff --git a/qutebrowser/browser/webengine/webengineinspector.py b/qutebrowser/browser/webengine/webengineinspector.py index 64ef24319..d37f41ba5 100644 --- a/qutebrowser/browser/webengine/webengineinspector.py +++ b/qutebrowser/browser/webengine/webengineinspector.py @@ -35,14 +35,19 @@ class WebEngineInspectorView(QWebEngineView): See WebEngineView.createWindow for details. """ - inspected_page = self.page().inspectedPage() + our_page = self.page() + assert our_page is not None + inspected_page = our_page.inspectedPage() + assert inspected_page is not None if machinery.IS_QT5: view = inspected_page.view() assert isinstance(view, QWebEngineView), view return view.createWindow(wintype) else: # Qt 6 newpage = inspected_page.createWindow(wintype) - return webview.WebEngineView.forPage(newpage) + ret = webview.WebEngineView.forPage(newpage) + assert ret is not None + return ret class WebEngineInspector(inspector.AbstractWebInspector): @@ -88,16 +93,17 @@ class WebEngineInspector(inspector.AbstractWebInspector): def inspect(self, page: QWebEnginePage) -> None: if not self._widget: view = WebEngineInspectorView() - inspector_page = QWebEnginePage( + new_page = QWebEnginePage( page.profile(), self ) - inspector_page.windowCloseRequested.connect(self._on_window_close_requested) - view.setPage(inspector_page) + new_page.windowCloseRequested.connect(self._on_window_close_requested) + view.setPage(new_page) self._settings = webenginesettings.WebEngineSettings(view.settings()) self._set_widget(view) inspector_page = self._widget.page() + assert inspector_page is not None assert inspector_page.profile() == page.profile() inspector_page.setInspectedPage(page) diff --git a/qutebrowser/browser/webengine/webenginesettings.py b/qutebrowser/browser/webengine/webenginesettings.py index d0b6b5beb..f84ac7ba2 100644 --- a/qutebrowser/browser/webengine/webenginesettings.py +++ b/qutebrowser/browser/webengine/webenginesettings.py @@ -50,8 +50,12 @@ class _SettingsWrapper: For read operations, the default profile value is always used. """ + def default_profile(self): + assert default_profile is not None + return default_profile + def _settings(self): - yield default_profile.settings() + yield self.default_profile().settings() if private_profile: yield private_profile.settings() @@ -76,19 +80,19 @@ class _SettingsWrapper: settings.setUnknownUrlSchemePolicy(policy) def testAttribute(self, attribute): - return default_profile.settings().testAttribute(attribute) + return self.default_profile().settings().testAttribute(attribute) def fontSize(self, fonttype): - return default_profile.settings().fontSize(fonttype) + return self.default_profile().settings().fontSize(fonttype) def fontFamily(self, which): - return default_profile.settings().fontFamily(which) + return self.default_profile().settings().fontFamily(which) def defaultTextEncoding(self): - return default_profile.settings().defaultTextEncoding() + return self.default_profile().settings().defaultTextEncoding() def unknownUrlSchemePolicy(self): - return default_profile.settings().unknownUrlSchemePolicy() + return self.default_profile().settings().unknownUrlSchemePolicy() class WebEngineSettings(websettings.AbstractSettings): @@ -341,7 +345,10 @@ def _init_user_agent_str(ua): def init_user_agent(): - _init_user_agent_str(QWebEngineProfile.defaultProfile().httpUserAgent()) + """Make the default WebEngine user agent available via parsed_user_agent.""" + actual_default_profile = QWebEngineProfile.defaultProfile() + assert actual_default_profile is not None + _init_user_agent_str(actual_default_profile.httpUserAgent()) def _init_profile(profile: QWebEngineProfile) -> None: diff --git a/qutebrowser/browser/webengine/webenginetab.py b/qutebrowser/browser/webengine/webenginetab.py index 9f1d04b63..1c712db5e 100644 --- a/qutebrowser/browser/webengine/webenginetab.py +++ b/qutebrowser/browser/webengine/webenginetab.py @@ -2,7 +2,7 @@ # # SPDX-License-Identifier: GPL-3.0-or-later -"""Wrapper over a QWebEngineView.""" +"""Wrapper over a WebEngineView.""" import math import struct @@ -15,7 +15,6 @@ from typing import cast, Union, Optional from qutebrowser.qt.core import (pyqtSignal, pyqtSlot, Qt, QPoint, QPointF, QTimer, QUrl, QObject, QByteArray) from qutebrowser.qt.network import QAuthenticator -from qutebrowser.qt.webenginewidgets import QWebEngineView from qutebrowser.qt.webenginecore import QWebEnginePage, QWebEngineScript, QWebEngineHistory from qutebrowser.config import config @@ -1267,7 +1266,7 @@ class WebEngineTab(browsertab.AbstractTab): abort_questions = pyqtSignal() - _widget: QWebEngineView + _widget: webview.WebEngineView search: WebEngineSearch audio: WebEngineAudio printing: WebEnginePrinting diff --git a/qutebrowser/browser/webengine/webview.py b/qutebrowser/browser/webengine/webview.py index 3c63c59e4..a6f2ae113 100644 --- a/qutebrowser/browser/webengine/webview.py +++ b/qutebrowser/browser/webengine/webview.py @@ -5,13 +5,16 @@ """The main browser widget for QtWebEngine.""" import mimetypes -from typing import List, Iterable +from typing import List, Iterable, Optional from qutebrowser.qt import machinery from qutebrowser.qt.core import pyqtSignal, pyqtSlot, QUrl from qutebrowser.qt.gui import QPalette from qutebrowser.qt.webenginewidgets import QWebEngineView -from qutebrowser.qt.webenginecore import QWebEnginePage, QWebEngineCertificateError +from qutebrowser.qt.webenginecore import ( + QWebEnginePage, QWebEngineCertificateError, QWebEngineSettings, + QWebEngineHistory, +) from qutebrowser.browser import shared from qutebrowser.browser.webengine import webenginesettings, certificateerror @@ -129,6 +132,25 @@ class WebEngineView(QWebEngineView): return super().contextMenuEvent(ev) + def page(self) -> "WebEnginePage": + """Return the page for this view.""" + maybe_page = super().page() + assert maybe_page is not None + assert isinstance(maybe_page, WebEnginePage) + return maybe_page + + def settings(self) -> "QWebEngineSettings": + """Return the settings for this view.""" + maybe_settings = super().settings() + assert maybe_settings is not None + return maybe_settings + + def history(self) -> "QWebEngineHistory": + """Return the history for this view.""" + maybe_history = super().history() + assert maybe_history is not None + return maybe_history + def extra_suffixes_workaround(upstream_mimetypes): """Return any extra suffixes for mimetypes in upstream_mimetypes. @@ -294,22 +316,28 @@ class WebEnginePage(QWebEnginePage): def chooseFiles( self, mode: QWebEnginePage.FileSelectionMode, - old_files: Iterable[str], - accepted_mimetypes: Iterable[str], + old_files: Iterable[Optional[str]], + accepted_mimetypes: Iterable[Optional[str]], ) -> List[str]: """Override chooseFiles to (optionally) invoke custom file uploader.""" - extra_suffixes = extra_suffixes_workaround(accepted_mimetypes) + accepted_mimetypes_filtered = [m for m in accepted_mimetypes if m is not None] + old_files_filtered = [f for f in old_files if f is not None] + extra_suffixes = extra_suffixes_workaround(accepted_mimetypes_filtered) if extra_suffixes: log.webview.debug( "adding extra suffixes to filepicker: " - f"before={accepted_mimetypes} " + f"before={accepted_mimetypes_filtered} " f"added={extra_suffixes}", ) - accepted_mimetypes = list(accepted_mimetypes) + list(extra_suffixes) + accepted_mimetypes_filtered = list( + accepted_mimetypes_filtered + ) + list(extra_suffixes) handler = config.val.fileselect.handler if handler == "default": - return super().chooseFiles(mode, old_files, accepted_mimetypes) + return super().chooseFiles( + mode, old_files_filtered, accepted_mimetypes_filtered, + ) assert handler == "external", handler try: qb_mode = _QB_FILESELECTION_MODES[mode] @@ -317,6 +345,8 @@ class WebEnginePage(QWebEnginePage): log.webview.warning( f"Got file selection mode {mode}, but we don't support that!" ) - return super().chooseFiles(mode, old_files, accepted_mimetypes) + return super().chooseFiles( + mode, old_files_filtered, accepted_mimetypes_filtered, + ) return shared.choose_file(qb_mode=qb_mode) diff --git a/qutebrowser/utils/version.py b/qutebrowser/utils/version.py index 75df73ffa..a139d01c5 100644 --- a/qutebrowser/utils/version.py +++ b/qutebrowser/utils/version.py @@ -686,7 +686,7 @@ class WebEngineVersions: return cls._CHROMIUM_VERSIONS.get(minor_version) @classmethod - def from_api(cls, qtwe_version: str, chromium_version: str) -> 'WebEngineVersions': + def from_api(cls, qtwe_version: str, chromium_version: Optional[str]) -> 'WebEngineVersions': """Get the versions based on the exact versions. This is called if we have proper APIs to get the versions easily @@ -796,8 +796,10 @@ def qtwebengine_versions(*, avoid_init: bool = False) -> WebEngineVersions: except ImportError: pass # Needs QtWebEngine 6.2+ with PyQtWebEngine 6.3.1+ else: + qtwe_version = qWebEngineVersion() + assert qtwe_version is not None return WebEngineVersions.from_api( - qtwe_version=qWebEngineVersion(), + qtwe_version=qtwe_version, chromium_version=qWebEngineChromiumVersion(), ) diff --git a/requirements.txt b/requirements.txt index dc07bf531..9b8b4a905 100644 --- a/requirements.txt +++ b/requirements.txt @@ -2,7 +2,7 @@ adblock==0.6.0 colorama==0.4.6 -importlib-resources==6.1.0 ; python_version=="3.8.*" +importlib-resources==6.1.1 ; python_version=="3.8.*" Jinja2==3.1.2 MarkupSafe==2.1.3 Pygments==2.16.1 @@ -51,8 +51,9 @@ deps = pyqt63: -r{toxinidir}/misc/requirements/requirements-pyqt-6.3.txt pyqt64: -r{toxinidir}/misc/requirements/requirements-pyqt-6.4.txt pyqt65: -r{toxinidir}/misc/requirements/requirements-pyqt-6.5.txt + pyqt66: -r{toxinidir}/misc/requirements/requirements-pyqt-6.6.txt commands = - !pyqt-!pyqt515-!pyqt5152-!pyqt62-!pyqt63-!pyqt64-!pyqt65: {envpython} scripts/link_pyqt.py --tox {envdir} + !pyqt-!pyqt515-!pyqt5152-!pyqt62-!pyqt63-!pyqt64-!pyqt65-!pyqt66: {envpython} scripts/link_pyqt.py --tox {envdir} {envpython} -bb -m pytest {posargs:tests} cov: {envpython} scripts/dev/check_coverage.py {posargs} |