From da70f750f3a887a80cc51e928831df5b2fe07768 Mon Sep 17 00:00:00 2001 From: Florian Bruhin Date: Sat, 9 May 2020 15:09:16 +0200 Subject: [PATCH 01/34] travis: Add archlinux-unstable --- .travis.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index 54eab192e..4ca28c375 100644 --- a/.travis.yml +++ b/.travis.yml @@ -19,8 +19,8 @@ matrix: services: docker ### Archlinux QtWebEngine with testing/KDE-Unstable - # - env: DOCKER=archlinux-webengine-unstable QUTE_BDD_WEBENGINE=true - # services: docker + - env: DOCKER=archlinux-webengine-unstable QUTE_BDD_WEBENGINE=true + services: docker ### PyQt 5.7.1 (Python 3.5) - python: 3.5 From 2afda2ed1a68377e26f46f6e52da9d7776172a03 Mon Sep 17 00:00:00 2001 From: Florian Bruhin Date: Sat, 9 May 2020 16:45:30 +0200 Subject: [PATCH 02/34] mypy: Switch back to upstream stubs See #5368 --- misc/requirements/requirements-mypy.txt | 4 +--- misc/requirements/requirements-mypy.txt-raw | 5 ++--- tox.ini | 1 - 3 files changed, 3 insertions(+), 7 deletions(-) diff --git a/misc/requirements/requirements-mypy.txt b/misc/requirements/requirements-mypy.txt index 435e5d618..3900a8c20 100644 --- a/misc/requirements/requirements-mypy.txt +++ b/misc/requirements/requirements-mypy.txt @@ -2,8 +2,6 @@ mypy==0.770 mypy-extensions==0.4.3 -# PyQt5==5.11.3 -# PyQt5-sip==4.19.19 --e git+https://github.com/qutebrowser/PyQt5-stubs.git@wip#egg=PyQt5_stubs +-e git+https://github.com/stlehmann/PyQt5-stubs.git@master#egg=PyQt5_stubs typed-ast==1.4.1 typing-extensions==3.7.4.2 diff --git a/misc/requirements/requirements-mypy.txt-raw b/misc/requirements/requirements-mypy.txt-raw index 92a35ab74..777b288ba 100644 --- a/misc/requirements/requirements-mypy.txt-raw +++ b/misc/requirements/requirements-mypy.txt-raw @@ -1,6 +1,5 @@ mypy --e git+https://github.com/qutebrowser/PyQt5-stubs.git@wip#egg=PyQt5-stubs +-e git+https://github.com/stlehmann/PyQt5-stubs.git@master#egg=PyQt5-stubs # remove @commit-id for scm installs -#@ replace: @.*# @wip# -#@ ignore: PyQt5, PyQt5-sip +#@ replace: @.*# @master# diff --git a/tox.ini b/tox.ini index baf21f69f..00b14bfb0 100644 --- a/tox.ini +++ b/tox.ini @@ -195,7 +195,6 @@ deps = -r{toxinidir}/requirements.txt -r{toxinidir}/misc/requirements/requirements-dev.txt -r{toxinidir}/misc/requirements/requirements-tests.txt - -r{toxinidir}/misc/requirements/requirements-pyqt.txt -r{toxinidir}/misc/requirements/requirements-mypy.txt commands = {envpython} -m mypy qutebrowser tests {posargs} From 80dc7ddf2a0158e0aeafaddc0c9b602083e543c7 Mon Sep 17 00:00:00 2001 From: Florian Bruhin Date: Sat, 9 May 2020 16:50:45 +0200 Subject: [PATCH 03/34] mypy: Remove unneeded casts/ignores See #5368 --- qutebrowser/app.py | 4 ++-- qutebrowser/browser/browsertab.py | 2 +- qutebrowser/browser/history.py | 2 +- .../browser/webengine/webenginequtescheme.py | 2 +- qutebrowser/browser/webengine/webenginetab.py | 2 +- qutebrowser/browser/webengine/webview.py | 3 +-- qutebrowser/mainwindow/prompt.py | 4 ++-- qutebrowser/mainwindow/tabbedbrowser.py | 13 ++++--------- qutebrowser/mainwindow/tabwidget.py | 6 +++--- qutebrowser/misc/crashdialog.py | 2 +- qutebrowser/misc/guiprocess.py | 6 +++--- qutebrowser/misc/msgbox.py | 2 +- qutebrowser/utils/log.py | 2 +- qutebrowser/utils/urlutils.py | 4 ++-- 14 files changed, 24 insertions(+), 30 deletions(-) diff --git a/qutebrowser/app.py b/qutebrowser/app.py index d4ef6f185..be238876d 100644 --- a/qutebrowser/app.py +++ b/qutebrowser/app.py @@ -159,13 +159,13 @@ def init(*, args: argparse.Namespace) -> None: eventfilter.init() log.init.debug("Connecting signals...") - q_app.focusChanged.connect(on_focus_changed) # type: ignore + q_app.focusChanged.connect(on_focus_changed) _process_args(args) for scheme in ['http', 'https', 'qute']: QDesktopServices.setUrlHandler( - scheme, open_desktopservices_url) # type: ignore + scheme, open_desktopservices_url) log.init.debug("Init done!") crashsignal.crash_handler.raise_crashdlg() diff --git a/qutebrowser/browser/browsertab.py b/qutebrowser/browser/browsertab.py index 00d46c813..fd1359f34 100644 --- a/qutebrowser/browser/browsertab.py +++ b/qutebrowser/browser/browsertab.py @@ -954,7 +954,7 @@ class AbstractTab(QWidget): log.webview.warning("Unable to find event target!") return - evt.posted = True + evt.posted = True # type: ignore QApplication.postEvent(recipient, evt) def navigation_blocked(self) -> bool: diff --git a/qutebrowser/browser/history.py b/qutebrowser/browser/history.py index d5165d364..cf0c1a59a 100644 --- a/qutebrowser/browser/history.py +++ b/qutebrowser/browser/history.py @@ -57,7 +57,7 @@ class HistoryProgress: self._progress.setMinimumDuration(500) self._progress.setLabelText(text) self._progress.setMaximum(maximum) - self._progress.setCancelButton(None) # type: ignore + self._progress.setCancelButton(None) self._progress.show() QApplication.processEvents() diff --git a/qutebrowser/browser/webengine/webenginequtescheme.py b/qutebrowser/browser/webengine/webenginequtescheme.py index a1526b777..ef4403c10 100644 --- a/qutebrowser/browser/webengine/webenginequtescheme.py +++ b/qutebrowser/browser/webengine/webenginequtescheme.py @@ -23,7 +23,7 @@ from PyQt5.QtCore import QBuffer, QIODevice, QUrl from PyQt5.QtWebEngineCore import (QWebEngineUrlSchemeHandler, QWebEngineUrlRequestJob) try: - from PyQt5.QtWebEngineCore import QWebEngineUrlScheme # type: ignore + from PyQt5.QtWebEngineCore import QWebEngineUrlScheme except ImportError: # Added in Qt 5.12 QWebEngineUrlScheme = None diff --git a/qutebrowser/browser/webengine/webenginetab.py b/qutebrowser/browser/webengine/webenginetab.py index 53d77acda..12fe9f79e 100644 --- a/qutebrowser/browser/webengine/webenginetab.py +++ b/qutebrowser/browser/webengine/webenginetab.py @@ -1714,7 +1714,7 @@ class WebEngineTab(browsertab.AbstractTab): try: # pylint: disable=unused-import - from PyQt5.QtWebEngineWidgets import ( # type: ignore + from PyQt5.QtWebEngineWidgets import ( QWebEngineClientCertificateSelection) except ImportError: pass diff --git a/qutebrowser/browser/webengine/webview.py b/qutebrowser/browser/webengine/webview.py index c41c61f7f..2b197323b 100644 --- a/qutebrowser/browser/webengine/webview.py +++ b/qutebrowser/browser/webengine/webview.py @@ -216,8 +216,7 @@ class WebEnginePage(QWebEnginePage): self.shutting_down], escape_msg=escape_msg) except shared.CallSuper: - return super().javaScriptPrompt( # type: ignore - url, js_msg, default) + return super().javaScriptPrompt(url, js_msg, default) def javaScriptAlert(self, url, js_msg): """Override javaScriptAlert to use qutebrowser prompts.""" diff --git a/qutebrowser/mainwindow/prompt.py b/qutebrowser/mainwindow/prompt.py index bfe6fb812..7696168b3 100644 --- a/qutebrowser/mainwindow/prompt.py +++ b/qutebrowser/mainwindow/prompt.py @@ -329,7 +329,7 @@ class PromptContainer(QWidget): usertypes.PromptMode.alert: AlertPrompt, } klass = classes[question.mode] - prompt = typing.cast(_BasePrompt, klass(question)) + prompt = klass(question) log.prompt.debug("Displaying prompt {}".format(prompt)) self._prompt = prompt @@ -707,7 +707,7 @@ class FilenamePrompt(_BasePrompt): # Nothing selected initially self._file_view.setCurrentIndex(QModelIndex()) # The model needs to be sorted so we get the correct first/last index - self._file_model.directoryLoaded.connect( # type: ignore + self._file_model.directoryLoaded.connect( lambda: self._file_model.sort(0)) def accept(self, value=None, save=False): diff --git a/qutebrowser/mainwindow/tabbedbrowser.py b/qutebrowser/mainwindow/tabbedbrowser.py index f5dc3277b..b86f3d27f 100644 --- a/qutebrowser/mainwindow/tabbedbrowser.py +++ b/qutebrowser/mainwindow/tabbedbrowser.py @@ -204,11 +204,9 @@ class TabbedBrowser(QWidget): self._tab_insert_idx_left = 0 self._tab_insert_idx_right = -1 self.shutting_down = False - self.widget.tabCloseRequested.connect( # type: ignore - self.on_tab_close_requested) - self.widget.new_tab_requested.connect(self.tabopen) - self.widget.currentChanged.connect( # type: ignore - self._on_current_changed) + self.widget.tabCloseRequested.connect(self.on_tab_close_requested) + self.widget.new_tab_requested.connect(self.tabopen) # type: ignore + self.widget.currentChanged.connect(self._on_current_changed) self.cur_fullscreen_requested.connect(self.widget.tabBar().maybe_hide) self.widget.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding) @@ -506,10 +504,7 @@ class TabbedBrowser(QWidget): newtab = self.widget.widget(0) use_current_tab = False else: - # FIXME:typing mypy thinks this is None due to @pyqtSlot - newtab = typing.cast( - browsertab.AbstractTab, - self.tabopen(background=False, idx=entry.index)) + newtab = self.tabopen(background=False, idx=entry.index) newtab.history.private_api.deserialize(entry.history) self.widget.set_tab_pinned(newtab, entry.pinned) diff --git a/qutebrowser/mainwindow/tabwidget.py b/qutebrowser/mainwindow/tabwidget.py index ca4014b28..9cdc21781 100644 --- a/qutebrowser/mainwindow/tabwidget.py +++ b/qutebrowser/mainwindow/tabwidget.py @@ -61,9 +61,9 @@ class TabWidget(QTabWidget): self.setStyle(TabBarStyle()) self.setTabBar(bar) bar.tabCloseRequested.connect(self.tabCloseRequested) # type: ignore - bar.tabMoved.connect(functools.partial( # type: ignore + bar.tabMoved.connect(functools.partial( QTimer.singleShot, 0, self.update_tab_titles)) - bar.currentChanged.connect(self._on_current_changed) # type: ignore + bar.currentChanged.connect(self._on_current_changed) bar.new_tab_requested.connect(self._on_new_tab_requested) self.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Fixed) self.setDocumentMode(True) @@ -544,7 +544,7 @@ class TabBar(QTabBar): idx = self.currentIndex() elif action == 'close-last': idx = self.count() - 1 - self.tabCloseRequested.emit(idx) # type: ignore + self.tabCloseRequested.emit(idx) return super().mousePressEvent(e) diff --git a/qutebrowser/misc/crashdialog.py b/qutebrowser/misc/crashdialog.py index f80004a92..f377a0eae 100644 --- a/qutebrowser/misc/crashdialog.py +++ b/qutebrowser/misc/crashdialog.py @@ -345,7 +345,7 @@ class _CrashDialog(QDialog): text: The paste text to show. """ error_dlg = ReportErrorDialog(text, self._paste_text, self) - error_dlg.finished.connect(self.finish) # type: ignore + error_dlg.finished.connect(self.finish) error_dlg.show() @pyqtSlot(str) diff --git a/qutebrowser/misc/guiprocess.py b/qutebrowser/misc/guiprocess.py index 7fc598191..a4aed18df 100644 --- a/qutebrowser/misc/guiprocess.py +++ b/qutebrowser/misc/guiprocess.py @@ -62,11 +62,11 @@ class GUIProcess(QObject): self.args = None self._proc = QProcess(self) - self._proc.errorOccurred.connect(self._on_error) # type: ignore + self._proc.errorOccurred.connect(self._on_error) self._proc.errorOccurred.connect(self.error) # type: ignore - self._proc.finished.connect(self._on_finished) # type: ignore + self._proc.finished.connect(self._on_finished) self._proc.finished.connect(self.finished) # type: ignore - self._proc.started.connect(self._on_started) # type: ignore + self._proc.started.connect(self._on_started) self._proc.started.connect(self.started) # type: ignore if additional_env is not None: diff --git a/qutebrowser/misc/msgbox.py b/qutebrowser/misc/msgbox.py index 89ade19e6..f06eccd92 100644 --- a/qutebrowser/misc/msgbox.py +++ b/qutebrowser/misc/msgbox.py @@ -60,7 +60,7 @@ def msgbox(parent, title, text, *, icon, buttons=QMessageBox.Ok, box.setIcon(icon) box.setStandardButtons(buttons) if on_finished is not None: - box.finished.connect(on_finished) # type: ignore + box.finished.connect(on_finished) if plain_text: box.setTextFormat(Qt.PlainText) elif plain_text is not None: diff --git a/qutebrowser/utils/log.py b/qutebrowser/utils/log.py index 922981511..df74fd333 100644 --- a/qutebrowser/utils/log.py +++ b/qutebrowser/utils/log.py @@ -216,7 +216,7 @@ def init_log(args: argparse.Namespace) -> None: root.setLevel(logging.NOTSET) logging.captureWarnings(True) _init_py_warnings() - QtCore.qInstallMessageHandler(qt_message_handler) # type: ignore + QtCore.qInstallMessageHandler(qt_message_handler) _log_inited = True diff --git a/qutebrowser/utils/urlutils.py b/qutebrowser/utils/urlutils.py index efd2caf75..3360a44a2 100644 --- a/qutebrowser/utils/urlutils.py +++ b/qutebrowser/utils/urlutils.py @@ -551,13 +551,13 @@ def safe_display_string(qurl: QUrl) -> str: """ ensure_valid(qurl) - host = qurl.host(QUrl.FullyEncoded) # type: ignore + host = qurl.host(QUrl.FullyEncoded) if '..' in host: # pragma: no cover # WORKAROUND for https://bugreports.qt.io/browse/QTBUG-60364 return '(unparseable URL!) {}'.format(qurl.toDisplayString()) for part in host.split('.'): - url_host = qurl.host(QUrl.FullyDecoded) # type: ignore + url_host = qurl.host(QUrl.FullyDecoded) if part.startswith('xn--') and host != url_host: return '({}) {}'.format(host, qurl.toDisplayString()) From d848c519e721a4a2c57cff437a891425fe109fd7 Mon Sep 17 00:00:00 2001 From: Florian Bruhin Date: Sat, 9 May 2020 16:52:55 +0200 Subject: [PATCH 04/34] mypy: Fix typing around browsertab/webkittab/webenginetab See #5368 See #4800 --- qutebrowser/browser/browsertab.py | 15 +++++++++------ qutebrowser/browser/webengine/webenginetab.py | 17 ++++++++++++++++- qutebrowser/browser/webkit/webkittab.py | 18 +++++++++++++++++- 3 files changed, 42 insertions(+), 8 deletions(-) diff --git a/qutebrowser/browser/browsertab.py b/qutebrowser/browser/browsertab.py index fd1359f34..2b6b0b3f2 100644 --- a/qutebrowser/browser/browsertab.py +++ b/qutebrowser/browser/browsertab.py @@ -23,6 +23,7 @@ import enum import itertools import typing import functools +import typing import attr from PyQt5.QtCore import (pyqtSignal, pyqtSlot, QUrl, QObject, QSizeF, Qt, @@ -71,7 +72,7 @@ def create(win_id: int, mode_manager = modeman.instance(win_id) if objects.backend == usertypes.Backend.QtWebEngine: from qutebrowser.browser.webengine import webenginetab - tab_class = webenginetab.WebEngineTab + tab_class = webenginetab.WebEngineTab # type: typing.Type[AbstractTab] else: from qutebrowser.browser.webkit import webkittab tab_class = webkittab.WebKitTab @@ -438,16 +439,15 @@ class AbstractCaret(QObject): follow_selected_done = pyqtSignal() def __init__(self, - tab: 'AbstractTab', mode_manager: modeman.ModeManager, parent: QWidget = None) -> None: super().__init__(parent) - self._tab = tab self._widget = typing.cast(QWidget, None) self.selection_enabled = False self._mode_manager = mode_manager mode_manager.entered.connect(self._on_mode_entered) mode_manager.left.connect(self._on_mode_left) + # self._tab is set by subclasses so mypy knows its concrete type. def _on_mode_entered(self, mode: usertypes.KeyMode) -> None: raise NotImplementedError @@ -689,9 +689,9 @@ class AbstractElements: [typing.Optional['webelem.AbstractWebElement']], None] _ErrorCallback = typing.Callable[[Exception], None] - def __init__(self, tab: 'AbstractTab') -> None: + def __init__(self) -> None: self._widget = typing.cast(QWidget, None) - self._tab = tab + # self._tab is set by subclasses so mypy knows its concrete type. def find_css(self, selector: str, callback: _MultiCallback, @@ -876,8 +876,11 @@ class AbstractTab(QWidget): # for a given hostname anyways. _insecure_hosts = set() # type: typing.Set[str] - def __init__(self, *, win_id: int, private: bool, + def __init__(self, *, win_id: int, + mode_manager: modeman.ModeManager, + private: bool, parent: QWidget = None) -> None: + utils.unused(mode_manager) # needed for mypy self.is_private = private self.win_id = win_id self.tab_id = next(tab_id_gen) diff --git a/qutebrowser/browser/webengine/webenginetab.py b/qutebrowser/browser/webengine/webenginetab.py index 12fe9f79e..de3300e1e 100644 --- a/qutebrowser/browser/webengine/webenginetab.py +++ b/qutebrowser/browser/webengine/webenginetab.py @@ -41,6 +41,7 @@ from qutebrowser.browser.webengine import (webview, webengineelem, tabhistory, from qutebrowser.misc import miscwidgets, objects from qutebrowser.utils import (usertypes, qtutils, log, javascript, utils, message, objreg, jinja, debug) +from qutebrowser.keyinput import modeman from qutebrowser.qt import sip @@ -336,6 +337,13 @@ class WebEngineCaret(browsertab.AbstractCaret): """QtWebEngine implementations related to moving the cursor/selection.""" + def __init__(self, + tab: 'WebEngineTab', + mode_manager: modeman.ModeManager, + parent: QWidget = None) -> None: + super().__init__(mode_manager, parent) + self._tab = tab + def _flags(self): """Get flags to pass to JS.""" flags = set() @@ -713,6 +721,10 @@ class WebEngineElements(browsertab.AbstractElements): """QtWebEngine implemementations related to elements on the page.""" + def __init__(self, tab: 'WebEngineTab') -> None: + super().__init__() + self._tab = tab + def _js_cb_multiple(self, callback, error_cb, js_elems): """Handle found elements coming from JS and call the real callback. @@ -1244,7 +1256,10 @@ class WebEngineTab(browsertab.AbstractTab): abort_questions = pyqtSignal() def __init__(self, *, win_id, mode_manager, private, parent=None): - super().__init__(win_id=win_id, private=private, parent=parent) + super().__init__(win_id=win_id, + mode_manager=mode_manager, + private=private, + parent=parent) widget = webview.WebEngineView(tabdata=self.data, win_id=win_id, private=private) self.history = WebEngineHistory(tab=self) diff --git a/qutebrowser/browser/webkit/webkittab.py b/qutebrowser/browser/webkit/webkittab.py index d1122b78e..e44d15f7c 100644 --- a/qutebrowser/browser/webkit/webkittab.py +++ b/qutebrowser/browser/webkit/webkittab.py @@ -27,12 +27,14 @@ from PyQt5.QtCore import pyqtSlot, Qt, QUrl, QPoint, QTimer, QSizeF, QSize from PyQt5.QtGui import QIcon from PyQt5.QtWebKitWidgets import QWebPage, QWebFrame from PyQt5.QtWebKit import QWebSettings +from PyQt5.QtWidgets import QWidget from PyQt5.QtPrintSupport import QPrinter from qutebrowser.browser import browsertab, shared from qutebrowser.browser.webkit import (webview, tabhistory, webkitelem, webkitsettings) from qutebrowser.utils import qtutils, usertypes, utils, log, debug +from qutebrowser.keyinput import modeman from qutebrowser.qt import sip @@ -172,6 +174,13 @@ class WebKitCaret(browsertab.AbstractCaret): """QtWebKit implementations related to moving the cursor/selection.""" + def __init__(self, + tab: 'WebKitTab', + mode_manager: modeman.ModeManager, + parent: QWidget = None) -> None: + super().__init__(mode_manager, parent) + self._tab = tab + @pyqtSlot(usertypes.KeyMode) def _on_mode_entered(self, mode): if mode != usertypes.KeyMode.caret: @@ -582,6 +591,10 @@ class WebKitElements(browsertab.AbstractElements): """QtWebKit implemementations related to elements on the page.""" + def __init__(self, tab: 'WebKitTab') -> None: + super().__init__() + self._tab = tab + def find_css(self, selector, callback, error_cb, *, only_visible=False): utils.unused(error_cb) mainframe = self._widget.page().mainFrame() @@ -699,7 +712,10 @@ class WebKitTab(browsertab.AbstractTab): """A QtWebKit tab in the browser.""" def __init__(self, *, win_id, mode_manager, private, parent=None): - super().__init__(win_id=win_id, private=private, parent=parent) + super().__init__(win_id=win_id, + mode_manager=mode_manager, + private=private, + parent=parent) widget = webview.WebView(win_id=win_id, tab_id=self.tab_id, private=private, tab=self) if private: From 60dd59d3a16c72ba2cae2d33f8aaf975518175ef Mon Sep 17 00:00:00 2001 From: Florian Bruhin Date: Sat, 9 May 2020 16:54:57 +0200 Subject: [PATCH 05/34] mypy: Don't use bytes() on QByteArrays See #5368 and https://bugreports.qt.io/browse/PYSIDE-1248 --- qutebrowser/browser/inspector.py | 2 +- qutebrowser/mainwindow/mainwindow.py | 2 +- qutebrowser/misc/guiprocess.py | 4 ++-- qutebrowser/utils/debug.py | 2 +- qutebrowser/utils/urlutils.py | 2 +- 5 files changed, 6 insertions(+), 6 deletions(-) diff --git a/qutebrowser/browser/inspector.py b/qutebrowser/browser/inspector.py index 02e416f41..e7c8e2a7f 100644 --- a/qutebrowser/browser/inspector.py +++ b/qutebrowser/browser/inspector.py @@ -83,7 +83,7 @@ class AbstractWebInspector(QWidget): def closeEvent(self, e): """Save the geometry when closed.""" - data = bytes(self.saveGeometry()) + data = self.saveGeometry().data() geom = base64.b64encode(data).decode('ASCII') configfiles.state['geometry']['inspector'] = geom diff --git a/qutebrowser/mainwindow/mainwindow.py b/qutebrowser/mainwindow/mainwindow.py index 03762766d..14d7dc193 100644 --- a/qutebrowser/mainwindow/mainwindow.py +++ b/qutebrowser/mainwindow/mainwindow.py @@ -446,7 +446,7 @@ class MainWindow(QWidget): def _save_geometry(self): """Save the window geometry to the state config.""" - data = bytes(self.saveGeometry()) + data = self.saveGeometry().data() geom = base64.b64encode(data).decode('ASCII') configfiles.state['geometry']['mainwindow'] = geom diff --git a/qutebrowser/misc/guiprocess.py b/qutebrowser/misc/guiprocess.py index a4aed18df..49b26e9d3 100644 --- a/qutebrowser/misc/guiprocess.py +++ b/qutebrowser/misc/guiprocess.py @@ -89,9 +89,9 @@ class GUIProcess(QObject): code, status)) encoding = locale.getpreferredencoding(do_setlocale=False) - stderr = bytes(self._proc.readAllStandardError()).decode( + stderr = self._proc.readAllStandardError().data().decode( encoding, 'replace') - stdout = bytes(self._proc.readAllStandardOutput()).decode( + stdout = self._proc.readAllStandardOutput().data().decode( encoding, 'replace') if self._output_messages: diff --git a/qutebrowser/utils/debug.py b/qutebrowser/utils/debug.py index 395f41a1b..b4237d172 100644 --- a/qutebrowser/utils/debug.py +++ b/qutebrowser/utils/debug.py @@ -69,7 +69,7 @@ def log_signals(obj: QObject) -> QObject: meta_method = metaobj.method(i) qtutils.ensure_valid(meta_method) if meta_method.methodType() == QMetaMethod.Signal: - name = bytes(meta_method.name()).decode('ascii') + name = meta_method.name().data().decode('ascii') if name != 'destroyed': signal = getattr(obj, name) try: diff --git a/qutebrowser/utils/urlutils.py b/qutebrowser/utils/urlutils.py index 3360a44a2..abad0a898 100644 --- a/qutebrowser/utils/urlutils.py +++ b/qutebrowser/utils/urlutils.py @@ -519,7 +519,7 @@ def encoded_url(url: QUrl) -> str: Args: url: The url to encode as QUrl. """ - return bytes(url.toEncoded()).decode('ascii') + return url.toEncoded().data().decode('ascii') def file_url(path: str) -> QUrl: From 24aa5097bc6b1c6f26c005c8a732453f56529a4b Mon Sep 17 00:00:00 2001 From: Florian Bruhin Date: Sat, 9 May 2020 16:56:44 +0200 Subject: [PATCH 06/34] mypy: Fix typing of qtutils.ensure_valid See #5368 --- qutebrowser/utils/qtutils.py | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/qutebrowser/utils/qtutils.py b/qutebrowser/utils/qtutils.py index eb123e634..b33537522 100644 --- a/qutebrowser/utils/qtutils.py +++ b/qutebrowser/utils/qtutils.py @@ -152,7 +152,16 @@ def check_overflow(arg: int, ctype: str, fatal: bool = True) -> int: return arg -def ensure_valid(obj: QObject) -> None: +if typing.TYPE_CHECKING: + class Validatable(typing.Protocol): + + """An object with an isValid() method (e.g. QUrl).""" + + def isValid(self) -> bool: + ... + + +def ensure_valid(obj: 'Validatable') -> None: """Ensure a Qt object with an .isValid() method is valid.""" if not obj.isValid(): raise QtValueError(obj) @@ -396,9 +405,9 @@ class QtValueError(ValueError): """Exception which gets raised by ensure_valid.""" - def __init__(self, obj: QObject) -> None: + def __init__(self, obj: 'Validatable') -> None: try: - self.reason = obj.errorString() + self.reason = obj.errorString() # type: ignore except AttributeError: self.reason = None err = "{} is not valid".format(obj) From 969755f35c265afba88a0fc14476964079fcbd10 Mon Sep 17 00:00:00 2001 From: Florian Bruhin Date: Sat, 9 May 2020 16:57:08 +0200 Subject: [PATCH 07/34] mypy: Fix typing in webkitelem See #5368 and #4800 --- qutebrowser/browser/webkit/webkitelem.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/qutebrowser/browser/webkit/webkitelem.py b/qutebrowser/browser/webkit/webkitelem.py index 1000a9965..8d30d2c4d 100644 --- a/qutebrowser/browser/webkit/webkitelem.py +++ b/qutebrowser/browser/webkit/webkitelem.py @@ -175,6 +175,10 @@ class WebKitElement(webelem.AbstractWebElement): self._elem.parent()) if elem is None or elem.isNull(): return None + + if typing.TYPE_CHECKING: + assert isinstance(self._tab, webkittab.WebKitTab) + return WebKitElement(elem, tab=self._tab) def _rect_on_view_js(self) -> typing.Optional[QRect]: From 463e5e43d3315290183fae3ae0fa20be6027a923 Mon Sep 17 00:00:00 2001 From: Florian Bruhin Date: Sat, 9 May 2020 16:58:25 +0200 Subject: [PATCH 08/34] mypy: Fix typing for some re-assigned values See #5368 --- qutebrowser/browser/webkit/webkittab.py | 4 ++-- qutebrowser/commands/userscripts.py | 9 ++++----- qutebrowser/completion/models/urlmodel.py | 4 +++- qutebrowser/mainwindow/mainwindow.py | 2 +- 4 files changed, 10 insertions(+), 9 deletions(-) diff --git a/qutebrowser/browser/webkit/webkittab.py b/qutebrowser/browser/webkit/webkittab.py index e44d15f7c..fb50a62a1 100644 --- a/qutebrowser/browser/webkit/webkittab.py +++ b/qutebrowser/browser/webkit/webkittab.py @@ -402,11 +402,11 @@ class WebKitCaret(browsertab.AbstractCaret): if selected_element is not None: try: - url = selected_element.attrib['href'] + href = selected_element.attrib['href'] except KeyError: raise browsertab.WebTabError('Anchor element without ' 'href!') - url = self._tab.url().resolved(QUrl(url)) + url = self._tab.url().resolved(QUrl(href)) if tab: self._tab.new_tab_requested.emit(url) else: diff --git a/qutebrowser/commands/userscripts.py b/qutebrowser/commands/userscripts.py index f3a706d1a..406eea8e8 100644 --- a/qutebrowser/commands/userscripts.py +++ b/qutebrowser/commands/userscripts.py @@ -418,14 +418,13 @@ def run_async(tab, cmd, *args, win_id, env, verbose=False, verbose: Show notifications when the command started/exited. output_messages: Show the output as messages. """ - tabbed_browser = objreg.get('tabbed-browser', scope='window', - window=win_id) - commandrunner = runners.CommandRunner(win_id, parent=tabbed_browser) + tb = objreg.get('tabbed-browser', scope='window', window=win_id) + commandrunner = runners.CommandRunner(win_id, parent=tb) if utils.is_posix: - runner = _POSIXUserscriptRunner(tabbed_browser) + runner = _POSIXUserscriptRunner(tb) # type: _BaseUserscriptRunner elif utils.is_windows: # pragma: no cover - runner = _WindowsUserscriptRunner(tabbed_browser) + runner = _WindowsUserscriptRunner(tb) else: # pragma: no cover raise UnsupportedError diff --git a/qutebrowser/completion/models/urlmodel.py b/qutebrowser/completion/models/urlmodel.py index 3e9b26aaa..f662aa74f 100644 --- a/qutebrowser/completion/models/urlmodel.py +++ b/qutebrowser/completion/models/urlmodel.py @@ -21,6 +21,8 @@ import typing +from PyQt5.QtCore import QAbstractItemModel + from qutebrowser.completion.models import (completionmodel, listcategory, histcategory) from qutebrowser.browser import history @@ -74,7 +76,7 @@ def url(*, info): if k != 'DEFAULT'] # pylint: enable=bad-config-option categories = config.val.completion.open_categories - models = {} + models = {} # type: typing.Dict[str, QAbstractItemModel] if searchengines and 'searchengines' in categories: models['searchengines'] = listcategory.ListCategory( diff --git a/qutebrowser/mainwindow/mainwindow.py b/qutebrowser/mainwindow/mainwindow.py index 14d7dc193..2aa7e1a80 100644 --- a/qutebrowser/mainwindow/mainwindow.py +++ b/qutebrowser/mainwindow/mainwindow.py @@ -409,7 +409,7 @@ class MainWindow(QWidget): self._vbox.removeWidget(self.tabbed_browser.widget) self._vbox.removeWidget(self._downloadview) self._vbox.removeWidget(self.status) - widgets = [self.tabbed_browser.widget] + widgets = [self.tabbed_browser.widget] # type: typing.List[QWidget] downloads_position = config.val.downloads.position if downloads_position == 'top': From 5eff9169ca736209e2139cd6e1eff743ca8f4353 Mon Sep 17 00:00:00 2001 From: Florian Bruhin Date: Sat, 9 May 2020 16:59:43 +0200 Subject: [PATCH 09/34] mypy: Add ignores/casts for some QEnum/QFlags See #5368 --- .../browser/webengine/webenginequtescheme.py | 2 +- qutebrowser/browser/webkit/webpage.py | 11 ++++++----- qutebrowser/components/misccommands.py | 3 ++- qutebrowser/mainwindow/mainwindow.py | 7 ++++--- qutebrowser/mainwindow/prompt.py | 4 +++- qutebrowser/utils/debug.py | 13 +++++++------ qutebrowser/utils/qtutils.py | 5 +++-- 7 files changed, 26 insertions(+), 19 deletions(-) diff --git a/qutebrowser/browser/webengine/webenginequtescheme.py b/qutebrowser/browser/webengine/webenginequtescheme.py index ef4403c10..efcb0e524 100644 --- a/qutebrowser/browser/webengine/webenginequtescheme.py +++ b/qutebrowser/browser/webengine/webenginequtescheme.py @@ -165,6 +165,6 @@ def init(): if QWebEngineUrlScheme is not None: assert not QWebEngineUrlScheme.schemeByName(b'qute').name() scheme = QWebEngineUrlScheme(b'qute') - scheme.setFlags(QWebEngineUrlScheme.LocalScheme | + scheme.setFlags(QWebEngineUrlScheme.LocalScheme | # type: ignore QWebEngineUrlScheme.LocalAccessAllowed) QWebEngineUrlScheme.registerScheme(scheme) diff --git a/qutebrowser/browser/webkit/webpage.py b/qutebrowser/browser/webkit/webpage.py index 52af5397b..4d38e380d 100644 --- a/qutebrowser/browser/webkit/webpage.py +++ b/qutebrowser/browser/webkit/webpage.py @@ -21,6 +21,7 @@ import html import functools +import typing from PyQt5.QtCore import pyqtSlot, pyqtSignal, Qt, QUrl, QPoint from PyQt5.QtGui import QDesktopServices @@ -348,11 +349,11 @@ class BrowserPage(QWebPage): self.setFeaturePermission, frame, feature, QWebPage.PermissionDeniedByUser) - url = frame.url().adjusted(QUrl.RemoveUserInfo | - QUrl.RemovePath | - QUrl.RemoveQuery | - QUrl.RemoveFragment) - + url = frame.url().adjusted(typing.cast(QUrl.FormattingOptions, + QUrl.RemoveUserInfo | + QUrl.RemovePath | + QUrl.RemoveQuery | + QUrl.RemoveFragment)) question = shared.feature_permission( url=url, option=options[feature], msg=messages[feature], diff --git a/qutebrowser/components/misccommands.py b/qutebrowser/components/misccommands.py index e3fecab5a..c7d3a8cff 100644 --- a/qutebrowser/components/misccommands.py +++ b/qutebrowser/components/misccommands.py @@ -71,7 +71,8 @@ def _print_preview(tab: apitypes.Tab) -> None: tab.printing.check_preview_support() diag = QPrintPreviewDialog(tab) diag.setAttribute(Qt.WA_DeleteOnClose) - diag.setWindowFlags(diag.windowFlags() | Qt.WindowMaximizeButtonHint | + diag.setWindowFlags(diag.windowFlags() | # type: ignore + Qt.WindowMaximizeButtonHint | Qt.WindowMinimizeButtonHint) diag.paintRequested.connect(functools.partial( tab.printing.to_printer, callback=print_callback)) diff --git a/qutebrowser/mainwindow/mainwindow.py b/qutebrowser/mainwindow/mainwindow.py index 2aa7e1a80..be1488d87 100644 --- a/qutebrowser/mainwindow/mainwindow.py +++ b/qutebrowser/mainwindow/mainwindow.py @@ -574,7 +574,7 @@ class MainWindow(QWidget): refresh_window = self.isVisible() if hidden: window_flags |= Qt.CustomizeWindowHint | Qt.NoDropShadowWindowHint - self.setWindowFlags(window_flags) + self.setWindowFlags(typing.cast(Qt.WindowFlags, window_flags)) if refresh_window: self.show() @@ -583,8 +583,9 @@ class MainWindow(QWidget): if not config.val.content.fullscreen.window: if on: self.state_before_fullscreen = self.windowState() - self.setWindowState(Qt.WindowFullScreen | # type: ignore - self.state_before_fullscreen) + self.setWindowState( + Qt.WindowFullScreen | # type: ignore + self.state_before_fullscreen) # type: ignore elif self.isFullScreen(): self.setWindowState(self.state_before_fullscreen) log.misc.debug('on: {}, state before fullscreen: {}'.format( diff --git a/qutebrowser/mainwindow/prompt.py b/qutebrowser/mainwindow/prompt.py index 7696168b3..eac00166b 100644 --- a/qutebrowser/mainwindow/prompt.py +++ b/qutebrowser/mainwindow/prompt.py @@ -197,7 +197,9 @@ class PromptQueue(QObject): question.completed.connect(loop.quit) question.completed.connect(loop.deleteLater) log.prompt.debug("Starting loop.exec_() for {}".format(question)) - loop.exec_(QEventLoop.ExcludeSocketNotifiers) + flags = typing.cast(QEventLoop.ProcessEventsFlags, + QEventLoop.ExcludeSocketNotifiers) + loop.exec_(flags) log.prompt.debug("Ending loop.exec_() for {}".format(question)) log.prompt.debug("Restoring old question {}".format(old_question)) diff --git a/qutebrowser/utils/debug.py b/qutebrowser/utils/debug.py index b4237d172..fa5664edc 100644 --- a/qutebrowser/utils/debug.py +++ b/qutebrowser/utils/debug.py @@ -31,6 +31,7 @@ from PyQt5.QtCore import Qt, QEvent, QMetaMethod, QObject, pyqtSignal from PyQt5.QtWidgets import QApplication from qutebrowser.utils import log, utils, qtutils, objreg +from qutebrowser.qt import sip def log_events(klass: typing.Type) -> typing.Type: @@ -97,7 +98,7 @@ def log_signals(obj: QObject) -> QObject: def qenum_key(base: typing.Type, - value: int, + value: typing.Union[int, sip.simplewrapper], add_base: bool = False, klass: typing.Type = None) -> str: """Convert a Qt Enum value to its key as a string. @@ -121,7 +122,7 @@ def qenum_key(base: typing.Type, try: idx = base.staticMetaObject.indexOfEnumerator(klass.__name__) meta_enum = base.staticMetaObject.enumerator(idx) - ret = meta_enum.valueToKey(int(value)) + ret = meta_enum.valueToKey(int(value)) # type: ignore except AttributeError: ret = None @@ -131,7 +132,7 @@ def qenum_key(base: typing.Type, ret = name break else: - ret = '0x{:04x}'.format(int(value)) + ret = '0x{:04x}'.format(int(value)) # type: ignore if add_base and hasattr(base, '__name__'): return '.'.join([base.__name__, ret]) @@ -140,7 +141,7 @@ def qenum_key(base: typing.Type, def qflags_key(base: typing.Type, - value: int, + value: typing.Union[int, sip.simplewrapper], add_base: bool = False, klass: typing.Type = None) -> str: """Convert a Qt QFlags value to its keys as string. @@ -174,7 +175,7 @@ def qflags_key(base: typing.Type, bits = [] names = [] mask = 0x01 - value = int(value) + value = int(value) # type: ignore while mask <= value: if value & mask: bits.append(mask) @@ -182,7 +183,7 @@ def qflags_key(base: typing.Type, for bit in bits: # We have to re-convert to an enum type here or we'll sometimes get an # empty string back. - names.append(qenum_key(base, klass(bit), add_base)) + names.append(qenum_key(base, klass(bit), add_base)) # type: ignore return '|'.join(names) diff --git a/qutebrowser/utils/qtutils.py b/qutebrowser/utils/qtutils.py index b33537522..22713facb 100644 --- a/qutebrowser/utils/qtutils.py +++ b/qutebrowser/utils/qtutils.py @@ -429,12 +429,13 @@ class EventLoop(QEventLoop): def exec_( self, - flags: QEventLoop.ProcessEventsFlag = QEventLoop.AllEvents + flags: QEventLoop.ProcessEventsFlags = + typing.cast(QEventLoop.ProcessEventsFlags, QEventLoop.AllEvents) ) -> int: """Override exec_ to raise an exception when re-running.""" if self._executing: raise AssertionError("Eventloop is already running!") self._executing = True - status = super().exec_(flags) # type: ignore + status = super().exec_(flags) self._executing = False return status From 16d98a4137762dfb2731d8bc185549de721d3ca6 Mon Sep 17 00:00:00 2001 From: Florian Bruhin Date: Sat, 9 May 2020 17:00:23 +0200 Subject: [PATCH 10/34] mypy: Fix wrong type annotations See #5368 --- qutebrowser/browser/network/proxy.py | 4 +++- qutebrowser/config/configtypes.py | 9 +++++++-- qutebrowser/config/configutils.py | 2 +- qutebrowser/keyinput/basekeyparser.py | 2 +- qutebrowser/utils/message.py | 2 +- qutebrowser/utils/qtutils.py | 13 ++++++++----- qutebrowser/utils/urlutils.py | 2 +- 7 files changed, 22 insertions(+), 12 deletions(-) diff --git a/qutebrowser/browser/network/proxy.py b/qutebrowser/browser/network/proxy.py index 160660f62..541403222 100644 --- a/qutebrowser/browser/network/proxy.py +++ b/qutebrowser/browser/network/proxy.py @@ -89,7 +89,9 @@ class ProxyFactory(QNetworkProxyFactory): elif isinstance(proxy, pac.PACFetcher): if objects.backend == usertypes.Backend.QtWebEngine: # Looks like query.url() is always invalid on QtWebEngine... - proxies = [urlutils.proxy_from_url(QUrl('direct://'))] + proxy = urlutils.proxy_from_url(QUrl('direct://')) + assert not isinstance(proxy, pac.PACFetcher) + proxies = [proxy] else: proxies = proxy.resolve(query) else: diff --git a/qutebrowser/config/configtypes.py b/qutebrowser/config/configtypes.py index 3d0f5c924..dbf7b05ac 100644 --- a/qutebrowser/config/configtypes.py +++ b/qutebrowser/config/configtypes.py @@ -65,6 +65,7 @@ from qutebrowser.config import configexc, configutils from qutebrowser.utils import (standarddir, utils, qtutils, urlutils, urlmatch, usertypes) from qutebrowser.keyinput import keyutils +from qutebrowser.browser.network import pac class _SystemProxy: @@ -1713,7 +1714,8 @@ class Proxy(BaseType): def to_py( self, value: _StrUnset - ) -> typing.Union[usertypes.Unset, None, QNetworkProxy, _SystemProxy]: + ) -> typing.Union[usertypes.Unset, None, + QNetworkProxy, _SystemProxy, pac.PACFetcher]: self._basic_py_validation(value, str) if isinstance(value, usertypes.Unset): return value @@ -1783,7 +1785,10 @@ class FuzzyUrl(BaseType): """A URL which gets interpreted as search if needed.""" - def to_py(self, value: _StrUnset) -> _StrUnsetNone: + def to_py( + self, + value: _StrUnset + ) -> typing.Union[None, QUrl, usertypes.Unset]: self._basic_py_validation(value, str) if isinstance(value, usertypes.Unset): return value diff --git a/qutebrowser/config/configutils.py b/qutebrowser/config/configutils.py index 135cb5c0f..3f7823772 100644 --- a/qutebrowser/config/configutils.py +++ b/qutebrowser/config/configutils.py @@ -151,7 +151,7 @@ class Values: return bool(self._vmap) def _check_pattern_support( - self, arg: typing.Optional[urlmatch.UrlPattern]) -> None: + self, arg: typing.Union[urlmatch.UrlPattern, QUrl, None]) -> None: """Make sure patterns are supported if one was given.""" if arg is not None and not self.opt.supports_pattern: raise configexc.NoPatternError(self.opt.name) diff --git a/qutebrowser/keyinput/basekeyparser.py b/qutebrowser/keyinput/basekeyparser.py index 536a7e6ee..074cb7030 100644 --- a/qutebrowser/keyinput/basekeyparser.py +++ b/qutebrowser/keyinput/basekeyparser.py @@ -174,7 +174,7 @@ class BaseKeyParser(QObject): passthrough = False supports_count = True - def __init__(self, win_id: int, parent: QWidget = None) -> None: + def __init__(self, win_id: int, parent: QObject = None) -> None: super().__init__(parent) self._win_id = win_id self._modename = None diff --git a/qutebrowser/utils/message.py b/qutebrowser/utils/message.py index 3413b5e0e..a8c7c8d85 100644 --- a/qutebrowser/utils/message.py +++ b/qutebrowser/utils/message.py @@ -88,7 +88,7 @@ def _build_question(title: str, mode: usertypes.PromptMode, default: typing.Union[None, bool, str] = None, abort_on: typing.Iterable[pyqtSignal] = (), - url: QUrl = None, + url: str = None, option: bool = None) -> usertypes.Question: """Common function for ask/ask_async.""" question = usertypes.Question() diff --git a/qutebrowser/utils/qtutils.py b/qutebrowser/utils/qtutils.py index 22713facb..ff3dbc497 100644 --- a/qutebrowser/utils/qtutils.py +++ b/qutebrowser/utils/qtutils.py @@ -38,7 +38,7 @@ import typing import pkg_resources from PyQt5.QtCore import (qVersion, QEventLoop, QDataStream, QByteArray, QIODevice, QSaveFile, QT_VERSION_STR, - PYQT_VERSION_STR, QFileDevice, QObject) + PYQT_VERSION_STR, QFileDevice, QObject, QUrl) from PyQt5.QtGui import QColor from PyQt5.QtWidgets import QApplication try: @@ -181,7 +181,10 @@ def check_qdatastream(stream: QDataStream) -> None: raise OSError(status_to_str[stream.status()]) -def serialize(obj: QObject) -> QByteArray: +_QtSerializableType = typing.Union[QObject, QByteArray, QUrl] + + +def serialize(obj: _QtSerializableType) -> QByteArray: """Serialize an object into a QByteArray.""" data = QByteArray() stream = QDataStream(data, QIODevice.WriteOnly) @@ -189,20 +192,20 @@ def serialize(obj: QObject) -> QByteArray: return data -def deserialize(data: QByteArray, obj: QObject) -> None: +def deserialize(data: QByteArray, obj: _QtSerializableType) -> None: """Deserialize an object from a QByteArray.""" stream = QDataStream(data, QIODevice.ReadOnly) deserialize_stream(stream, obj) -def serialize_stream(stream: QDataStream, obj: QObject) -> None: +def serialize_stream(stream: QDataStream, obj: _QtSerializableType) -> None: """Serialize an object into a QDataStream.""" check_qdatastream(stream) stream << obj # pylint: disable=pointless-statement check_qdatastream(stream) -def deserialize_stream(stream: QDataStream, obj: QObject) -> None: +def deserialize_stream(stream: QDataStream, obj: _QtSerializableType) -> None: """Deserialize a QDataStream into an object.""" check_qdatastream(stream) stream >> obj # pylint: disable=pointless-statement diff --git a/qutebrowser/utils/urlutils.py b/qutebrowser/utils/urlutils.py index abad0a898..e687c9c6a 100644 --- a/qutebrowser/utils/urlutils.py +++ b/qutebrowser/utils/urlutils.py @@ -584,7 +584,7 @@ class InvalidProxyTypeError(Exception): super().__init__("Invalid proxy type {}!".format(typ)) -def proxy_from_url(url: QUrl) -> QNetworkProxy: +def proxy_from_url(url: QUrl) -> typing.Union[QNetworkProxy, pac.PACFetcher]: """Create a QNetworkProxy from QUrl and a proxy type. Args: From 292bab3c18e8e04dce2a32271ddaac5c99784eb4 Mon Sep 17 00:00:00 2001 From: Florian Bruhin Date: Sat, 9 May 2020 17:01:15 +0200 Subject: [PATCH 11/34] mypy: Fix typing in keyutils See #5368 --- qutebrowser/keyinput/keyutils.py | 24 +++++++++++++++++------- 1 file changed, 17 insertions(+), 7 deletions(-) diff --git a/qutebrowser/keyinput/keyutils.py b/qutebrowser/keyinput/keyutils.py index b738ad190..245ef1e91 100644 --- a/qutebrowser/keyinput/keyutils.py +++ b/qutebrowser/keyinput/keyutils.py @@ -167,9 +167,11 @@ def _assert_plain_key(key: Qt.Key) -> None: assert not key & Qt.KeyboardModifierMask, hex(key) -def _assert_plain_modifier(key: Qt.KeyboardModifier) -> None: +def _assert_plain_modifier( + key: typing.Union[Qt.KeyboardModifier, Qt.KeyboardModifiers] +) -> None: """Make sure this is a modifier without a key mixed in.""" - assert not key & ~Qt.KeyboardModifierMask, hex(key) + assert not key & ~Qt.KeyboardModifierMask, hex(key) # type: ignore def _is_printable(key: Qt.Key) -> bool: @@ -177,7 +179,10 @@ def _is_printable(key: Qt.Key) -> bool: return key <= 0xff and key not in [Qt.Key_Space, _NIL_KEY] -def is_special_hint_mode(key: Qt.Key, modifiers: Qt.KeyboardModifier) -> bool: +def is_special_hint_mode( + key: Qt.Key, + modifiers: typing.Union[Qt.KeyboardModifier, Qt.KeyboardModifiers], +) -> bool: """Check whether this key should clear the keychain in hint mode. When we press "s", we don't want to be handled as part of @@ -192,7 +197,10 @@ def is_special_hint_mode(key: Qt.Key, modifiers: Qt.KeyboardModifier) -> bool: Qt.KeypadModifier]) -def is_special(key: Qt.Key, modifiers: Qt.KeyboardModifier) -> bool: +def is_special( + key: Qt.Key, + modifiers: typing.Union[Qt.KeyboardModifier, Qt.KeyboardModifiers], +) -> bool: """Check whether this key requires special key syntax.""" _assert_plain_key(key) _assert_plain_modifier(modifiers) @@ -388,7 +396,7 @@ class KeyInfo: modifiers = e.modifiers() _assert_plain_key(key) _assert_plain_modifier(modifiers) - return cls(key, modifiers) + return cls(key, typing.cast(Qt.KeyboardModifier, modifiers)) def __hash__(self) -> int: """Convert KeyInfo to int before hashing. @@ -565,7 +573,9 @@ class KeySequence: return infos[item] def _iter_keys(self) -> typing.Iterator[int]: - return itertools.chain.from_iterable(self._sequences) + sequences = typing.cast(typing.Iterable[typing.Iterable[int]], + self._sequences) + return itertools.chain.from_iterable(sequences) def _validate(self, keystr: str = None) -> None: for info in self: @@ -646,7 +656,7 @@ class KeySequence: if (modifiers == Qt.ShiftModifier and _is_printable(key) and not ev.text().isupper()): - modifiers = Qt.KeyboardModifiers() + modifiers = Qt.KeyboardModifiers() # type: ignore # On macOS, swap Ctrl and Meta back # WORKAROUND for https://bugreports.qt.io/browse/QTBUG-51293 From 47e72bc2228846e901c2f6541fcb803b2e885e56 Mon Sep 17 00:00:00 2001 From: Florian Bruhin Date: Sat, 9 May 2020 17:02:54 +0200 Subject: [PATCH 12/34] mypy: Use from-import in keyinput.modeman See #5368, #5396 --- qutebrowser/keyinput/modeman.py | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/qutebrowser/keyinput/modeman.py b/qutebrowser/keyinput/modeman.py index 7e29ac3f5..3afbe6320 100644 --- a/qutebrowser/keyinput/modeman.py +++ b/qutebrowser/keyinput/modeman.py @@ -20,7 +20,7 @@ """Mode manager singleton which handles the current keyboard mode.""" import functools -import typing +from typing import Mapping, Callable, MutableMapping, Union, Set, cast import attr from PyQt5.QtCore import pyqtSlot, pyqtSignal, Qt, QObject, QEvent @@ -37,8 +37,7 @@ from qutebrowser.browser import hints INPUT_MODES = [usertypes.KeyMode.insert, usertypes.KeyMode.passthrough] PROMPT_MODES = [usertypes.KeyMode.prompt, usertypes.KeyMode.yesno] -ParserDictType = typing.MutableMapping[ - usertypes.KeyMode, basekeyparser.BaseKeyParser] +ParserDictType = MutableMapping[usertypes.KeyMode, basekeyparser.BaseKeyParser] @attr.s(frozen=True) @@ -169,7 +168,7 @@ def init(win_id: int, parent: QObject) -> 'ModeManager': return modeman -def instance(win_id: typing.Union[int, str]) -> 'ModeManager': +def instance(win_id: Union[int, str]) -> 'ModeManager': """Get a modemanager object.""" return objreg.get('mode-manager', scope='window', window=win_id) @@ -223,7 +222,7 @@ class ModeManager(QObject): self.parsers = {} # type: ParserDictType self._prev_mode = usertypes.KeyMode.normal self.mode = usertypes.KeyMode.normal - self._releaseevents_to_pass = set() # type: typing.Set[KeyEvent] + self._releaseevents_to_pass = set() # type: Set[KeyEvent] def __repr__(self) -> str: return utils.get_repr(self, mode=self.mode) From 2bffb87d149115a9ab4cfeac0dc3e7e0139902ad Mon Sep 17 00:00:00 2001 From: Florian Bruhin Date: Sat, 9 May 2020 17:05:00 +0200 Subject: [PATCH 13/34] mypy: Fix typing around eventfilters See #5368 --- qutebrowser/keyinput/eventfilter.py | 4 +++- qutebrowser/keyinput/modeman.py | 7 ++++--- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/qutebrowser/keyinput/eventfilter.py b/qutebrowser/keyinput/eventfilter.py index 992d9f4ce..6ef0dd201 100644 --- a/qutebrowser/keyinput/eventfilter.py +++ b/qutebrowser/keyinput/eventfilter.py @@ -19,6 +19,8 @@ """Global Qt event filter which dispatches key events.""" +import typing + from PyQt5.QtCore import pyqtSlot, QObject, QEvent from PyQt5.QtGui import QKeyEvent, QWindow from PyQt5.QtWidgets import QApplication @@ -100,7 +102,7 @@ class EventFilter(QObject): handler = self._handlers[typ] try: - return handler(event) + return handler(typing.cast(QKeyEvent, event)) except: # If there is an exception in here and we leave the eventfilter # activated, we'll get an infinite loop and a stack overflow. diff --git a/qutebrowser/keyinput/modeman.py b/qutebrowser/keyinput/modeman.py index 3afbe6320..e0752d0e3 100644 --- a/qutebrowser/keyinput/modeman.py +++ b/qutebrowser/keyinput/modeman.py @@ -246,7 +246,8 @@ class ModeManager(QObject): match = parser.handle(event, dry_run=dry_run) is_non_alnum = ( - event.modifiers() not in [Qt.NoModifier, Qt.ShiftModifier] or + event.modifiers() not in [Qt.NoModifier, # type: ignore + Qt.ShiftModifier] or not event.text().strip()) forward_unbound_keys = config.cache['input.forward_unbound_keys'] @@ -415,9 +416,9 @@ class ModeManager(QObject): QEvent.KeyRelease: self._handle_keyrelease, QEvent.ShortcutOverride: functools.partial(self._handle_keypress, dry_run=True), - } # type: typing.Mapping[QEvent.Type, typing.Callable[[QEvent], bool]] + } # type: Mapping[QEvent.Type, Callable[[QKeyEvent], bool]] handler = handlers[event.type()] - return handler(event) + return handler(cast(QKeyEvent, event)) @cmdutils.register(instance='mode-manager', scope='window') def clear_keychain(self) -> None: From cee513a8624553e68f18f1190d276312150410c1 Mon Sep 17 00:00:00 2001 From: Florian Bruhin Date: Sat, 9 May 2020 18:09:16 +0200 Subject: [PATCH 14/34] mypy: Add cast for sip.voidptr See #5368 --- qutebrowser/commands/userscripts.py | 4 +++- qutebrowser/misc/crashsignal.py | 4 +++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/qutebrowser/commands/userscripts.py b/qutebrowser/commands/userscripts.py index 406eea8e8..f9e9af484 100644 --- a/qutebrowser/commands/userscripts.py +++ b/qutebrowser/commands/userscripts.py @@ -31,6 +31,7 @@ from qutebrowser.commands import runners from qutebrowser.config import websettings from qutebrowser.misc import guiprocess from qutebrowser.browser import downloads +from qutebrowser.qt import sip class _QtFIFOReader(QObject): @@ -59,7 +60,8 @@ class _QtFIFOReader(QObject): fd = os.open(filepath, os.O_RDWR | os.O_NONBLOCK) # pylint: enable=no-member,useless-suppression self._fifo = os.fdopen(fd, 'r') - self._notifier = QSocketNotifier(fd, QSocketNotifier.Read, self) + self._notifier = QSocketNotifier(typing.cast(sip.voidptr, fd), + QSocketNotifier.Read, self) self._notifier.activated.connect(self.read_line) # type: ignore @pyqtSlot() diff --git a/qutebrowser/misc/crashsignal.py b/qutebrowser/misc/crashsignal.py index 0f660b35d..165c0b8b7 100644 --- a/qutebrowser/misc/crashsignal.py +++ b/qutebrowser/misc/crashsignal.py @@ -44,6 +44,7 @@ from PyQt5.QtWidgets import QApplication from qutebrowser.api import cmdutils from qutebrowser.misc import earlyinit, crashdialog, ipc, objects from qutebrowser.utils import usertypes, standarddir, log, objreg, debug, utils +from qutebrowser.qt import sip if typing.TYPE_CHECKING: from qutebrowser.misc import quitter @@ -351,7 +352,8 @@ class SignalHandler(QObject): for fd in [read_fd, write_fd]: flags = fcntl.fcntl(fd, fcntl.F_GETFL) fcntl.fcntl(fd, fcntl.F_SETFL, flags | os.O_NONBLOCK) - self._notifier = QSocketNotifier(read_fd, QSocketNotifier.Read, + self._notifier = QSocketNotifier(typing.cast(sip.voidptr, read_fd), + QSocketNotifier.Read, self) self._notifier.activated.connect( # type: ignore self.handle_signal_wakeup) From 7272636abb2d540190068f510f0d7193301508f0 Mon Sep 17 00:00:00 2001 From: Florian Bruhin Date: Sat, 9 May 2020 18:09:28 +0200 Subject: [PATCH 15/34] mypy: Add ignores for unknown pyqtProperty See #5368 --- qutebrowser/mainwindow/statusbar/bar.py | 3 ++- qutebrowser/mainwindow/statusbar/url.py | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/qutebrowser/mainwindow/statusbar/bar.py b/qutebrowser/mainwindow/statusbar/bar.py index b1aa4da38..e96090d0c 100644 --- a/qutebrowser/mainwindow/statusbar/bar.py +++ b/qutebrowser/mainwindow/statusbar/bar.py @@ -21,7 +21,8 @@ import enum import attr -from PyQt5.QtCore import pyqtSignal, pyqtSlot, pyqtProperty, Qt, QSize, QTimer +from PyQt5.QtCore import (pyqtSignal, pyqtSlot, pyqtProperty, # type: ignore + Qt, QSize, QTimer) from PyQt5.QtWidgets import QWidget, QHBoxLayout, QStackedLayout, QSizePolicy from qutebrowser.browser import browsertab diff --git a/qutebrowser/mainwindow/statusbar/url.py b/qutebrowser/mainwindow/statusbar/url.py index b48f0005f..65a82fd09 100644 --- a/qutebrowser/mainwindow/statusbar/url.py +++ b/qutebrowser/mainwindow/statusbar/url.py @@ -21,7 +21,7 @@ import enum -from PyQt5.QtCore import pyqtSlot, pyqtProperty, QUrl +from PyQt5.QtCore import pyqtSlot, pyqtProperty, QUrl # type: ignore from qutebrowser.mainwindow.statusbar import textbase from qutebrowser.config import stylesheet From d98e6873f1850f0013e76f7b3f40050d182792ab Mon Sep 17 00:00:00 2001 From: Florian Bruhin Date: Sat, 9 May 2020 18:09:37 +0200 Subject: [PATCH 16/34] mypy: Add ignores for monkey-patched attributes See #5368 --- qutebrowser/browser/webengine/webenginesettings.py | 4 ++-- qutebrowser/mainwindow/tabwidget.py | 3 ++- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/qutebrowser/browser/webengine/webenginesettings.py b/qutebrowser/browser/webengine/webenginesettings.py index 9a0b3e9ac..5f08c4b7f 100644 --- a/qutebrowser/browser/webengine/webenginesettings.py +++ b/qutebrowser/browser/webengine/webenginesettings.py @@ -362,7 +362,7 @@ def _init_profiles(): default_profile = QWebEngineProfile.defaultProfile() init_user_agent() - default_profile.setter = ProfileSetter(default_profile) + default_profile.setter = ProfileSetter(default_profile) # type: ignore default_profile.setCachePath( os.path.join(standarddir.cache(), 'webengine')) default_profile.setPersistentStoragePath( @@ -372,7 +372,7 @@ def _init_profiles(): if not qtutils.is_single_process(): private_profile = QWebEngineProfile() - private_profile.setter = ProfileSetter(private_profile) + private_profile.setter = ProfileSetter(private_profile) # type: ignore assert private_profile.isOffTheRecord() private_profile.setter.init_profile() diff --git a/qutebrowser/mainwindow/tabwidget.py b/qutebrowser/mainwindow/tabwidget.py index 9cdc21781..59e701142 100644 --- a/qutebrowser/mainwindow/tabwidget.py +++ b/qutebrowser/mainwindow/tabwidget.py @@ -82,7 +82,8 @@ class TabWidget(QTabWidget): position = config.val.tabs.position selection_behavior = config.val.tabs.select_on_remove self.setTabPosition(position) - tabbar.vertical = position in [QTabWidget.West, QTabWidget.East] + tabbar.vertical = position in [ # type: ignore + QTabWidget.West, QTabWidget.East] tabbar.setSelectionBehaviorOnRemove(selection_behavior) tabbar.refresh() From 5afdd074a14377f96d0b5056ab9630b175a8a072 Mon Sep 17 00:00:00 2001 From: Florian Bruhin Date: Sat, 9 May 2020 17:13:55 +0200 Subject: [PATCH 17/34] mypy: Add ignore for conditional import See #5368 --- qutebrowser/browser/webengine/webenginequtescheme.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/qutebrowser/browser/webengine/webenginequtescheme.py b/qutebrowser/browser/webengine/webenginequtescheme.py index efcb0e524..f97d0a884 100644 --- a/qutebrowser/browser/webengine/webenginequtescheme.py +++ b/qutebrowser/browser/webengine/webenginequtescheme.py @@ -26,7 +26,7 @@ try: from PyQt5.QtWebEngineCore import QWebEngineUrlScheme except ImportError: # Added in Qt 5.12 - QWebEngineUrlScheme = None + QWebEngineUrlScheme = None # type: ignore from qutebrowser.browser import qutescheme from qutebrowser.utils import log, qtutils From c4137d37b670b1a63149a71b96c63c5e896afcaa Mon Sep 17 00:00:00 2001 From: Florian Bruhin Date: Sat, 9 May 2020 17:14:42 +0200 Subject: [PATCH 18/34] mypy: Add ignores for unknown operators See #5368 --- qutebrowser/browser/webkit/cookies.py | 5 +++-- qutebrowser/completion/completiondelegate.py | 2 +- qutebrowser/utils/qtutils.py | 6 ++++-- 3 files changed, 8 insertions(+), 5 deletions(-) diff --git a/qutebrowser/browser/webkit/cookies.py b/qutebrowser/browser/webkit/cookies.py index aaa0e61da..924c3fd01 100644 --- a/qutebrowser/browser/webkit/cookies.py +++ b/qutebrowser/browser/webkit/cookies.py @@ -95,7 +95,7 @@ class CookieJar(RAMCookieJar): """Parse cookies from lineparser and store them.""" cookies = [] # type: typing.Sequence[QNetworkCookie] for line in self._lineparser: - cookies += QNetworkCookie.parseCookies(line) + cookies += QNetworkCookie.parseCookies(line) # type: ignore self.setAllCookies(cookies) def purge_old_cookies(self): @@ -104,7 +104,8 @@ class CookieJar(RAMCookieJar): # http://doc.qt.io/qt-5/qtwebkitexamples-webkitwidgets-browser-cookiejar-cpp.html now = QDateTime.currentDateTime() cookies = [c for c in self.allCookies() - if c.isSessionCookie() or c.expirationDate() >= now] + if c.isSessionCookie() or + c.expirationDate() >= now] # type: ignore self.setAllCookies(cookies) def save(self): diff --git a/qutebrowser/completion/completiondelegate.py b/qutebrowser/completion/completiondelegate.py index b80c81654..d841249aa 100644 --- a/qutebrowser/completion/completiondelegate.py +++ b/qutebrowser/completion/completiondelegate.py @@ -290,7 +290,7 @@ class CompletionItemDelegate(QStyledItemDelegate): size = self._style.sizeFromContents(QStyle.CT_ItemViewItem, self._opt, docsize, self._opt.widget) qtutils.ensure_valid(size) - return size + QSize(10, 3) + return size + QSize(10, 3) # type: ignore def paint(self, painter, option, index): """Override the QStyledItemDelegate paint function. diff --git a/qutebrowser/utils/qtutils.py b/qutebrowser/utils/qtutils.py index ff3dbc497..4de72b17f 100644 --- a/qutebrowser/utils/qtutils.py +++ b/qutebrowser/utils/qtutils.py @@ -200,15 +200,17 @@ def deserialize(data: QByteArray, obj: _QtSerializableType) -> None: def serialize_stream(stream: QDataStream, obj: _QtSerializableType) -> None: """Serialize an object into a QDataStream.""" + # pylint: disable=pointless-statement check_qdatastream(stream) - stream << obj # pylint: disable=pointless-statement + stream << obj # type: ignore check_qdatastream(stream) def deserialize_stream(stream: QDataStream, obj: _QtSerializableType) -> None: """Deserialize a QDataStream into an object.""" + # pylint: disable=pointless-statement check_qdatastream(stream) - stream >> obj # pylint: disable=pointless-statement + stream >> obj # type: ignore check_qdatastream(stream) From d9b36797a49e0d7423b09ed6a58d6ef17c222c64 Mon Sep 17 00:00:00 2001 From: Florian Bruhin Date: Sat, 9 May 2020 17:55:53 +0200 Subject: [PATCH 19/34] mypy: Add ignore for pyqtSlot result type See #5368 --- qutebrowser/browser/network/pac.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/qutebrowser/browser/network/pac.py b/qutebrowser/browser/network/pac.py index e330abf77..32c80340e 100644 --- a/qutebrowser/browser/network/pac.py +++ b/qutebrowser/browser/network/pac.py @@ -65,7 +65,7 @@ def _js_slot(*args): # pylint: disable=protected-access return self._error_con.callAsConstructor([e]) # pylint: enable=protected-access - return pyqtSlot(*args, result=QJSValue)(new_method) + return pyqtSlot(*args, result=QJSValue)(new_method) # type: ignore return _decorator From 469729e241ba215b6160955ce98906bc0c534fb1 Mon Sep 17 00:00:00 2001 From: Florian Bruhin Date: Sat, 9 May 2020 18:03:24 +0200 Subject: [PATCH 20/34] mypy: Remove some "# type: ignore" around PyQIODevice See #5368 --- qutebrowser/utils/qtutils.py | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/qutebrowser/utils/qtutils.py b/qutebrowser/utils/qtutils.py index 4de72b17f..58385e4fa 100644 --- a/qutebrowser/utils/qtutils.py +++ b/qutebrowser/utils/qtutils.py @@ -17,8 +17,6 @@ # You should have received a copy of the GNU General Public License # along with qutebrowser. If not, see . -# FIXME:typing Can we have less "# type: ignore" in here? - """Misc. utilities related to Qt. Module attributes: @@ -232,7 +230,7 @@ def savefile_open( new_f = PyQIODevice( f) # type: typing.Union[PyQIODevice, io.TextIOWrapper] else: - new_f = io.TextIOWrapper(PyQIODevice(f), # type: ignore + new_f = io.TextIOWrapper(PyQIODevice(f), encoding=encoding) yield new_f @@ -370,7 +368,7 @@ class PyQIODevice(io.BufferedIOBase): if buf is None: raise QtOSError(self.dev) - return buf # type: ignore + return buf def seekable(self) -> bool: return not self.dev.isSequential() @@ -383,10 +381,10 @@ class PyQIODevice(io.BufferedIOBase): def writable(self) -> bool: return self.dev.isWritable() - def write(self, data: str) -> int: # type: ignore + def write(self, data: str) -> int: self._check_open() self._check_writable() - num = self.dev.write(data) # type: ignore + num = self.dev.write(data) if num == -1 or num < len(data): raise QtOSError(self.dev) return num @@ -398,7 +396,7 @@ class PyQIODevice(io.BufferedIOBase): if size in [None, -1]: buf = self.dev.readAll() else: - buf = self.dev.read(size) # type: ignore + buf = self.dev.read(size) if buf is None: raise QtOSError(self.dev) From 8f6bb474da1571266a6ccc857a126914305a72d5 Mon Sep 17 00:00:00 2001 From: Florian Bruhin Date: Sun, 10 May 2020 16:35:10 +0200 Subject: [PATCH 21/34] mypy: Fix typing for PyQIODevice See #5368 --- qutebrowser/config/configfiles.py | 2 +- qutebrowser/misc/sessions.py | 2 +- qutebrowser/utils/qtutils.py | 41 ++++++++++++++++++++----------- 3 files changed, 29 insertions(+), 16 deletions(-) diff --git a/qutebrowser/config/configfiles.py b/qutebrowser/config/configfiles.py index 0deb36065..537a72ad4 100644 --- a/qutebrowser/config/configfiles.py +++ b/qutebrowser/config/configfiles.py @@ -161,7 +161,7 @@ class YamlConfig(QObject): # Instead, create a config.py - see :help for details. """.lstrip('\n'))) - utils.yaml_dump(data, f) # type: ignore + utils.yaml_dump(data, f) def _pop_object(self, yaml_data: typing.Any, diff --git a/qutebrowser/misc/sessions.py b/qutebrowser/misc/sessions.py index 6f03aa09d..1f3b59536 100644 --- a/qutebrowser/misc/sessions.py +++ b/qutebrowser/misc/sessions.py @@ -326,7 +326,7 @@ class SessionManager(QObject): log.sessions.vdebug("Saving data: {}".format(data)) # type: ignore try: with qtutils.savefile_open(path) as f: - utils.yaml_dump(data, f) # type: ignore + utils.yaml_dump(data, f) except (OSError, UnicodeEncodeError, yaml.YAMLError) as e: raise SessionError(e) diff --git a/qutebrowser/utils/qtutils.py b/qutebrowser/utils/qtutils.py index 58385e4fa..f330504e0 100644 --- a/qutebrowser/utils/qtutils.py +++ b/qutebrowser/utils/qtutils.py @@ -67,7 +67,7 @@ class QtOSError(OSError): qt_errno: The error attribute of the given QFileDevice, if applicable. """ - def __init__(self, dev: QFileDevice, msg: str = None) -> None: + def __init__(self, dev: QIODevice, msg: str = None) -> None: if msg is None: msg = dev.errorString() @@ -217,7 +217,7 @@ def savefile_open( filename: str, binary: bool = False, encoding: str = 'utf-8' -) -> typing.Iterator[typing.Union['PyQIODevice', io.TextIOWrapper]]: +) -> typing.Iterator[typing.IO]: """Context manager to easily use a QSaveFile.""" f = QSaveFile(filename) cancelled = False @@ -226,12 +226,12 @@ def savefile_open( if not open_ok: raise QtOSError(f) + dev = typing.cast(typing.IO[bytes], PyQIODevice(f)) + if binary: - new_f = PyQIODevice( - f) # type: typing.Union[PyQIODevice, io.TextIOWrapper] + new_f = dev # type: typing.IO else: - new_f = io.TextIOWrapper(PyQIODevice(f), - encoding=encoding) + new_f = io.TextIOWrapper(dev, encoding=encoding) yield new_f @@ -347,27 +347,33 @@ class PyQIODevice(io.BufferedIOBase): def readable(self) -> bool: return self.dev.isReadable() - def readline(self, size: int = -1) -> QByteArray: + def readline(self, size: int = -1) -> bytes: self._check_open() self._check_readable() if size < 0: qt_size = 0 # no maximum size elif size == 0: - return QByteArray() + return b'' else: qt_size = size + 1 # Qt also counts the NUL byte + buf = None # type: typing.Union[QByteArray, bytes, None] if self.dev.canReadLine(): buf = self.dev.readLine(qt_size) + elif size < 0: + buf = self.dev.readAll() else: - if size < 0: - buf = self.dev.readAll() - else: - buf = self.dev.read(size) + buf = self.dev.read(size) if buf is None: raise QtOSError(self.dev) + + if isinstance(buf, QByteArray): + # The type (bytes or QByteArray) seems to depend on what data we + # feed in... + buf = buf.data() + return buf def seekable(self) -> bool: @@ -381,7 +387,7 @@ class PyQIODevice(io.BufferedIOBase): def writable(self) -> bool: return self.dev.isWritable() - def write(self, data: str) -> int: + def write(self, data: typing.Union[bytes, bytearray]) -> int: self._check_open() self._check_writable() num = self.dev.write(data) @@ -389,18 +395,25 @@ class PyQIODevice(io.BufferedIOBase): raise QtOSError(self.dev) return num - def read(self, size: typing.Optional[int] = None) -> QByteArray: + def read(self, size: typing.Optional[int] = None) -> bytes: self._check_open() self._check_readable() + buf = None # type: typing.Union[QByteArray, bytes, None] if size in [None, -1]: buf = self.dev.readAll() else: + assert size is not None buf = self.dev.read(size) if buf is None: raise QtOSError(self.dev) + if isinstance(buf, QByteArray): + # The type (bytes or QByteArray) seems to depend on what data we + # feed in... + buf = buf.data() + return buf From 75ead68811483c4bdef040cc1a2128ed1d707f52 Mon Sep 17 00:00:00 2001 From: Florian Bruhin Date: Sun, 10 May 2020 16:46:07 +0200 Subject: [PATCH 22/34] tests: Add mode_manager to FakeWebTab See #5368 --- tests/helpers/stubs.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/helpers/stubs.py b/tests/helpers/stubs.py index b3d427413..b72907d93 100644 --- a/tests/helpers/stubs.py +++ b/tests/helpers/stubs.py @@ -257,7 +257,7 @@ class FakeWebTab(browsertab.AbstractTab): scroll_pos_perc=(0, 0), load_status=usertypes.LoadStatus.success, progress=0, can_go_back=None, can_go_forward=None): - super().__init__(win_id=0, private=False) + super().__init__(win_id=0, mode_manager=None, private=False) self._load_status = load_status self._title = title self._url = url From 8a1bc5a7cd9ee65b9f5df7fff4ddb428183374ff Mon Sep 17 00:00:00 2001 From: Florian Bruhin Date: Sun, 10 May 2020 16:52:35 +0200 Subject: [PATCH 23/34] Fix lint See #5368 --- qutebrowser/browser/browsertab.py | 1 - qutebrowser/browser/webkit/webkitelem.py | 1 + qutebrowser/completion/models/urlmodel.py | 3 ++- qutebrowser/keyinput/basekeyparser.py | 1 - qutebrowser/utils/qtutils.py | 7 +++++-- 5 files changed, 8 insertions(+), 5 deletions(-) diff --git a/qutebrowser/browser/browsertab.py b/qutebrowser/browser/browsertab.py index 2b6b0b3f2..825e73a60 100644 --- a/qutebrowser/browser/browsertab.py +++ b/qutebrowser/browser/browsertab.py @@ -21,7 +21,6 @@ import enum import itertools -import typing import functools import typing diff --git a/qutebrowser/browser/webkit/webkitelem.py b/qutebrowser/browser/webkit/webkitelem.py index 8d30d2c4d..b318425a3 100644 --- a/qutebrowser/browser/webkit/webkitelem.py +++ b/qutebrowser/browser/webkit/webkitelem.py @@ -177,6 +177,7 @@ class WebKitElement(webelem.AbstractWebElement): return None if typing.TYPE_CHECKING: + # pylint: disable=used-before-assignment assert isinstance(self._tab, webkittab.WebKitTab) return WebKitElement(elem, tab=self._tab) diff --git a/qutebrowser/completion/models/urlmodel.py b/qutebrowser/completion/models/urlmodel.py index f662aa74f..ff83a598a 100644 --- a/qutebrowser/completion/models/urlmodel.py +++ b/qutebrowser/completion/models/urlmodel.py @@ -21,7 +21,8 @@ import typing -from PyQt5.QtCore import QAbstractItemModel +if typing.TYPE_CHECKING: + from PyQt5.QtCore import QAbstractItemModel from qutebrowser.completion.models import (completionmodel, listcategory, histcategory) diff --git a/qutebrowser/keyinput/basekeyparser.py b/qutebrowser/keyinput/basekeyparser.py index 074cb7030..3c4adee49 100644 --- a/qutebrowser/keyinput/basekeyparser.py +++ b/qutebrowser/keyinput/basekeyparser.py @@ -26,7 +26,6 @@ import typing import attr from PyQt5.QtCore import pyqtSignal, QObject, Qt from PyQt5.QtGui import QKeySequence, QKeyEvent -from PyQt5.QtWidgets import QWidget from qutebrowser.config import config from qutebrowser.utils import usertypes, log, utils diff --git a/qutebrowser/utils/qtutils.py b/qutebrowser/utils/qtutils.py index f330504e0..8bfdec7db 100644 --- a/qutebrowser/utils/qtutils.py +++ b/qutebrowser/utils/qtutils.py @@ -36,7 +36,7 @@ import typing import pkg_resources from PyQt5.QtCore import (qVersion, QEventLoop, QDataStream, QByteArray, QIODevice, QSaveFile, QT_VERSION_STR, - PYQT_VERSION_STR, QFileDevice, QObject, QUrl) + PYQT_VERSION_STR, QObject, QUrl) from PyQt5.QtGui import QColor from PyQt5.QtWidgets import QApplication try: @@ -44,6 +44,9 @@ try: except ImportError: # pragma: no cover qWebKitVersion = None # type: ignore # noqa: N816 +if typing.TYPE_CHECKING: + from PyQt5.QtCore import QFileDevice + from qutebrowser.misc import objects from qutebrowser.utils import usertypes @@ -61,7 +64,7 @@ MINVALS = { class QtOSError(OSError): - """An OSError triggered by a QFileDevice. + """An OSError triggered by a QIODevice. Attributes: qt_errno: The error attribute of the given QFileDevice, if applicable. From 68db8d59652e6bde3828b5fd5892530497f094cb Mon Sep 17 00:00:00 2001 From: Florian Bruhin Date: Sun, 10 May 2020 17:05:18 +0200 Subject: [PATCH 24/34] Use base class for configtypes.Font inheritance See #5368 --- qutebrowser/config/configfiles.py | 2 +- qutebrowser/config/configinit.py | 10 +++++----- qutebrowser/config/configtypes.py | 26 +++++++++++++++++--------- tests/helpers/fixtures.py | 2 +- tests/unit/config/test_configinit.py | 4 ++-- tests/unit/config/test_configtypes.py | 4 ++-- 6 files changed, 28 insertions(+), 20 deletions(-) diff --git a/qutebrowser/config/configfiles.py b/qutebrowser/config/configfiles.py index 537a72ad4..098385c7b 100644 --- a/qutebrowser/config/configfiles.py +++ b/qutebrowser/config/configfiles.py @@ -400,7 +400,7 @@ class YamlMigrations(QObject): except KeyError: continue - if not isinstance(opt.typ, configtypes.Font): + if not isinstance(opt.typ, configtypes.FontBase): continue for scope, val in self._settings[name].items(): diff --git a/qutebrowser/config/configinit.py b/qutebrowser/config/configinit.py index 351030789..b15225210 100644 --- a/qutebrowser/config/configinit.py +++ b/qutebrowser/config/configinit.py @@ -121,11 +121,11 @@ def _update_font_defaults(setting: str) -> None: if setting not in {'fonts.default_family', 'fonts.default_size'}: return - configtypes.Font.set_defaults(config.val.fonts.default_family, - config.val.fonts.default_size) + configtypes.FontBase.set_defaults(config.val.fonts.default_family, + config.val.fonts.default_size) for name, opt in configdata.DATA.items(): - if not isinstance(opt.typ, configtypes.Font): + if not isinstance(opt.typ, configtypes.FontBase): continue value = config.instance.get_obj(name) @@ -165,8 +165,8 @@ def late_init(save_manager: savemanager.SaveManager) -> None: _init_errors = None - configtypes.Font.set_defaults(config.val.fonts.default_family, - config.val.fonts.default_size) + configtypes.FontBase.set_defaults(config.val.fonts.default_family, + config.val.fonts.default_size) config.instance.changed.connect(_update_font_defaults) config.instance.init_save_manager(save_manager) diff --git a/qutebrowser/config/configtypes.py b/qutebrowser/config/configtypes.py index dbf7b05ac..d5a78cf27 100644 --- a/qutebrowser/config/configtypes.py +++ b/qutebrowser/config/configtypes.py @@ -1147,14 +1147,9 @@ class QssColor(BaseType): return value -class Font(BaseType): +class FontBase(BaseType): - """A font family, with optional style/weight/size. - - * Style: `normal`/`italic`/`oblique` - * Weight: `normal`, `bold`, `100`..`900` - * Size: _number_ `px`/`pt` - """ + """Base class for Font/QtFont/FontFamily.""" # Gets set when the config is initialized. default_family = None # type: str @@ -1230,6 +1225,19 @@ class Font(BaseType): cls.default_family = families.to_str(quote=True) cls.default_size = default_size + def to_py(self, value: typing.Any) -> typing.Any: + raise NotImplementedError + + +class Font(FontBase): + + """A font family, with optional style/weight/size. + + * Style: `normal`/`italic`/`oblique` + * Weight: `normal`, `bold`, `100`..`900` + * Size: _number_ `px`/`pt` + """ + def to_py(self, value: _StrUnset) -> _StrUnsetNone: self._basic_py_validation(value, str) if isinstance(value, usertypes.Unset): @@ -1252,7 +1260,7 @@ class Font(BaseType): return value -class FontFamily(Font): +class FontFamily(FontBase): """A Qt font family.""" @@ -1276,7 +1284,7 @@ class FontFamily(Font): return value -class QtFont(Font): +class QtFont(FontBase): """A Font which gets converted to a QFont.""" diff --git a/tests/helpers/fixtures.py b/tests/helpers/fixtures.py index 51fd98272..ced27a9f8 100644 --- a/tests/helpers/fixtures.py +++ b/tests/helpers/fixtures.py @@ -315,7 +315,7 @@ def config_stub(stubs, monkeypatch, configdata_init, yaml_config_stub, qapp): monkeypatch.setattr(config, 'cache', cache) try: - configtypes.Font.set_defaults(None, '10pt') + configtypes.FontBase.set_defaults(None, '10pt') except configexc.NoOptionError: # Completion tests patch configdata so fonts.default_family is # unavailable. diff --git a/tests/unit/config/test_configinit.py b/tests/unit/config/test_configinit.py index 694a95437..731c62a66 100644 --- a/tests/unit/config/test_configinit.py +++ b/tests/unit/config/test_configinit.py @@ -40,8 +40,8 @@ def init_patch(qapp, fake_save_manager, monkeypatch, config_tmpdir, monkeypatch.setattr(config, 'key_instance', None) monkeypatch.setattr(config, 'change_filters', []) monkeypatch.setattr(configinit, '_init_errors', None) - monkeypatch.setattr(configtypes.Font, 'default_family', None) - monkeypatch.setattr(configtypes.Font, 'default_size', None) + monkeypatch.setattr(configtypes.FontBase, 'default_family', None) + monkeypatch.setattr(configtypes.FontBase, 'default_size', None) yield try: objreg.delete('config-commands') diff --git a/tests/unit/config/test_configtypes.py b/tests/unit/config/test_configtypes.py index fe954ca3d..e49075500 100644 --- a/tests/unit/config/test_configtypes.py +++ b/tests/unit/config/test_configtypes.py @@ -212,7 +212,7 @@ class TestAll: """ for _name, member in inspect.getmembers(configtypes, inspect.isclass): if member in [configtypes.BaseType, configtypes.MappingType, - configtypes._Numeric]: + configtypes._Numeric, configtypes.FontBase]: pass elif (member is configtypes.List or member is configtypes.ListOrValue): @@ -1480,7 +1480,7 @@ class TestFont: klass().to_py(val) def test_defaults_replacement(self, klass, monkeypatch): - configtypes.Font.set_defaults(['Terminus'], '23pt') + configtypes.FontBase.set_defaults(['Terminus'], '23pt') if klass is configtypes.Font: expected = '23pt Terminus' elif klass is configtypes.QtFont: From 12c738b5c6162b67c8a1fe705a69d76f1d2cb299 Mon Sep 17 00:00:00 2001 From: Florian Bruhin Date: Sun, 10 May 2020 17:19:54 +0200 Subject: [PATCH 25/34] Fix argument type for HintManager._hint_strings call We accidentally passed a list of HintLabels instead of a list of elements there. That worked by accident, because all we use with number hints is actually the length of that list. See #5368 --- qutebrowser/browser/hints.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/qutebrowser/browser/hints.py b/qutebrowser/browser/hints.py index 08a850fc8..e564e7249 100644 --- a/qutebrowser/browser/hints.py +++ b/qutebrowser/browser/hints.py @@ -893,7 +893,7 @@ class HintManager(QObject): if self._context.hint_mode == 'number': # renumber filtered hints - strings = self._hint_strings(visible) + strings = self._hint_strings([label.elem for label in visible]) self._context.labels = {} for label, string in zip(visible, strings): label.update_text('', string) From 3f79308ffdf703a28c2b8a81e08affd5b64729b1 Mon Sep 17 00:00:00 2001 From: Florian Bruhin Date: Sun, 10 May 2020 17:28:33 +0200 Subject: [PATCH 26/34] mypy: Improve typing in keyutils See #5368 --- qutebrowser/keyinput/keyutils.py | 28 +++++++++++----------------- 1 file changed, 11 insertions(+), 17 deletions(-) diff --git a/qutebrowser/keyinput/keyutils.py b/qutebrowser/keyinput/keyutils.py index 245ef1e91..2917161db 100644 --- a/qutebrowser/keyinput/keyutils.py +++ b/qutebrowser/keyinput/keyutils.py @@ -53,6 +53,8 @@ _MODIFIER_MAP = { _NIL_KEY = Qt.Key(0) +_ModifierType = typing.Union[Qt.KeyboardModifier, Qt.KeyboardModifiers] + def _build_special_names() -> typing.Mapping[Qt.Key, str]: """Build _SPECIAL_NAMES dict from the special_names_str mapping below. @@ -167,9 +169,7 @@ def _assert_plain_key(key: Qt.Key) -> None: assert not key & Qt.KeyboardModifierMask, hex(key) -def _assert_plain_modifier( - key: typing.Union[Qt.KeyboardModifier, Qt.KeyboardModifiers] -) -> None: +def _assert_plain_modifier(key: _ModifierType) -> None: """Make sure this is a modifier without a key mixed in.""" assert not key & ~Qt.KeyboardModifierMask, hex(key) # type: ignore @@ -179,10 +179,7 @@ def _is_printable(key: Qt.Key) -> bool: return key <= 0xff and key not in [Qt.Key_Space, _NIL_KEY] -def is_special_hint_mode( - key: Qt.Key, - modifiers: typing.Union[Qt.KeyboardModifier, Qt.KeyboardModifiers], -) -> bool: +def is_special_hint_mode(key: Qt.Key, modifiers: _ModifierType) -> bool: """Check whether this key should clear the keychain in hint mode. When we press "s", we don't want to be handled as part of @@ -197,10 +194,7 @@ def is_special_hint_mode( Qt.KeypadModifier]) -def is_special( - key: Qt.Key, - modifiers: typing.Union[Qt.KeyboardModifier, Qt.KeyboardModifiers], -) -> bool: +def is_special(key: Qt.Key, modifiers: _ModifierType) -> bool: """Check whether this key requires special key syntax.""" _assert_plain_key(key) _assert_plain_modifier(modifiers) @@ -252,7 +246,7 @@ def _remap_unicode(key: Qt.Key, text: str) -> Qt.Key: def _check_valid_utf8(s: str, - data: typing.Union[Qt.Key, Qt.KeyboardModifier]) -> None: + data: typing.Union[Qt.Key, _ModifierType]) -> None: """Make sure the given string is valid UTF-8. Makes sure there are no chars where Qt did fall back to weird UTF-16 @@ -262,7 +256,7 @@ def _check_valid_utf8(s: str, s.encode('utf-8') except UnicodeEncodeError as e: # pragma: no cover raise ValueError("Invalid encoding in 0x{:x} -> {}: {}" - .format(data, s, e)) + .format(int(data), s, e)) def _key_to_string(key: Qt.Key) -> str: @@ -284,14 +278,14 @@ def _key_to_string(key: Qt.Key) -> str: return result -def _modifiers_to_string(modifiers: Qt.KeyboardModifier) -> str: +def _modifiers_to_string(modifiers: _ModifierType) -> str: """Convert the given Qt::KeyboardModifiers to a string. Handles Qt.GroupSwitchModifier because Qt doesn't handle that as a modifier. """ _assert_plain_modifier(modifiers) - if modifiers & Qt.GroupSwitchModifier: + if modifiers & Qt.GroupSwitchModifier: # type: ignore modifiers &= ~Qt.GroupSwitchModifier # type: ignore result = 'AltGr+' else: @@ -383,7 +377,7 @@ class KeyInfo: """ key = attr.ib() # type: Qt.Key - modifiers = attr.ib() # type: Qt.KeyboardModifier + modifiers = attr.ib() # type: _ModifierType @classmethod def from_event(cls, e: QKeyEvent) -> 'KeyInfo': @@ -459,7 +453,7 @@ class KeyInfo: return '' text = QKeySequence(self.key).toString() - if not self.modifiers & Qt.ShiftModifier: + if not self.modifiers & Qt.ShiftModifier: # type: ignore text = text.lower() return text From 341aa4ea751bc7e15a421e0ffe91d1d06812ae97 Mon Sep 17 00:00:00 2001 From: Florian Bruhin Date: Sun, 10 May 2020 19:11:18 +0200 Subject: [PATCH 27/34] mypy: Use explicit "type: ignore[...]" ignores See #5368 --- qutebrowser/api/cmdutils.py | 6 ++--- qutebrowser/app.py | 2 +- qutebrowser/browser/browsertab.py | 7 ++--- qutebrowser/browser/commands.py | 9 ++++--- qutebrowser/browser/downloadview.py | 2 +- qutebrowser/browser/hints.py | 12 +++++---- qutebrowser/browser/navigate.py | 8 +++--- qutebrowser/browser/network/pac.py | 13 +++++---- qutebrowser/browser/network/proxy.py | 26 +++++++++++------- qutebrowser/browser/pdfjs.py | 7 +++-- qutebrowser/browser/qutescheme.py | 2 +- qutebrowser/browser/webelem.py | 2 +- .../browser/webengine/webenginedownloads.py | 11 +++++--- .../browser/webengine/webengineelem.py | 2 +- .../browser/webengine/webenginequtescheme.py | 7 ++--- .../browser/webengine/webenginesettings.py | 6 +++-- qutebrowser/browser/webengine/webenginetab.py | 27 ++++++++++++------- qutebrowser/browser/webkit/cookies.py | 5 ++-- .../browser/webkit/network/networkmanager.py | 7 ++--- .../browser/webkit/network/networkreply.py | 21 ++++++++------- qutebrowser/browser/webkit/webkitelem.py | 2 +- qutebrowser/browser/webkit/webkittab.py | 9 ++++--- qutebrowser/browser/webkit/webpage.py | 24 ++++++++++------- qutebrowser/browser/webkit/webview.py | 9 ++++--- qutebrowser/commands/command.py | 7 ++--- qutebrowser/commands/runners.py | 2 +- qutebrowser/commands/userscripts.py | 5 ++-- qutebrowser/completion/completiondelegate.py | 2 +- qutebrowser/completion/completionwidget.py | 2 +- .../completion/models/completionmodel.py | 8 +++--- qutebrowser/components/misccommands.py | 7 ++--- qutebrowser/config/config.py | 2 +- qutebrowser/config/configfiles.py | 4 +-- qutebrowser/config/configtypes.py | 14 +++++----- qutebrowser/config/stylesheet.py | 2 +- qutebrowser/config/websettings.py | 9 ++++--- qutebrowser/extensions/loader.py | 6 ++--- qutebrowser/keyinput/keyutils.py | 14 +++++----- qutebrowser/keyinput/modeman.py | 9 ++++--- qutebrowser/mainwindow/mainwindow.py | 16 ++++++----- qutebrowser/mainwindow/prompt.py | 8 +++--- qutebrowser/mainwindow/statusbar/bar.py | 4 +-- qutebrowser/mainwindow/statusbar/command.py | 9 ++++--- qutebrowser/mainwindow/statusbar/url.py | 3 ++- qutebrowser/mainwindow/tabbedbrowser.py | 11 ++++---- qutebrowser/mainwindow/tabwidget.py | 9 ++++--- qutebrowser/misc/autoupdate.py | 2 +- qutebrowser/misc/checkpyver.py | 8 +++--- qutebrowser/misc/crashdialog.py | 4 +-- qutebrowser/misc/crashsignal.py | 4 +-- qutebrowser/misc/earlyinit.py | 7 ++--- qutebrowser/misc/editor.py | 2 +- qutebrowser/misc/guiprocess.py | 9 ++++--- qutebrowser/misc/ipc.py | 13 +++++---- qutebrowser/misc/miscwidgets.py | 11 ++++---- qutebrowser/misc/sessions.py | 3 ++- qutebrowser/misc/split.py | 3 ++- qutebrowser/misc/sql.py | 3 ++- qutebrowser/qt.py | 2 +- qutebrowser/utils/debug.py | 17 ++++++------ qutebrowser/utils/jinja.py | 8 +++--- qutebrowser/utils/log.py | 26 ++++++++++-------- qutebrowser/utils/objreg.py | 2 +- qutebrowser/utils/qtutils.py | 8 +++--- qutebrowser/utils/standarddir.py | 2 +- qutebrowser/utils/urlutils.py | 10 +++---- qutebrowser/utils/usertypes.py | 5 ++-- qutebrowser/utils/utils.py | 12 ++++----- qutebrowser/utils/version.py | 6 ++--- tests/conftest.py | 2 +- 70 files changed, 313 insertions(+), 235 deletions(-) diff --git a/qutebrowser/api/cmdutils.py b/qutebrowser/api/cmdutils.py index 1498cc051..5d74991c1 100644 --- a/qutebrowser/api/cmdutils.py +++ b/qutebrowser/api/cmdutils.py @@ -208,12 +208,12 @@ class argument: # noqa: N801,N806 pylint: disable=invalid-name raise ValueError("{} has no argument {}!".format(funcname, self._argname)) if not hasattr(func, 'qute_args'): - func.qute_args = {} # type: ignore - elif func.qute_args is None: # type: ignore + func.qute_args = {} # type: ignore[attr-defined] + elif func.qute_args is None: # type: ignore[attr-defined] raise ValueError("@cmdutils.argument got called above (after) " "@cmdutils.register for {}!".format(funcname)) arginfo = command.ArgInfo(**self._kwargs) - func.qute_args[self._argname] = arginfo # type: ignore + func.qute_args[self._argname] = arginfo # type: ignore[attr-defined] return func diff --git a/qutebrowser/app.py b/qutebrowser/app.py index be238876d..6d01e0ddd 100644 --- a/qutebrowser/app.py +++ b/qutebrowser/app.py @@ -482,7 +482,7 @@ class Application(QApplication): log.init.debug("Initializing application...") self.launch_time = datetime.datetime.now() - self.focusObjectChanged.connect( # type: ignore + self.focusObjectChanged.connect( # type: ignore[attr-defined] self.on_focus_object_changed) self.setAttribute(Qt.AA_UseHighDpiPixmaps, True) diff --git a/qutebrowser/browser/browsertab.py b/qutebrowser/browser/browsertab.py index 825e73a60..0a52180e6 100644 --- a/qutebrowser/browser/browsertab.py +++ b/qutebrowser/browser/browsertab.py @@ -541,7 +541,7 @@ class AbstractScroller(QObject): @pyqtSlot() def _log_scroll_pos_change(self) -> None: - log.webview.vdebug( # type: ignore + log.webview.vdebug( # type: ignore[attr-defined] "Scroll position changed to {}".format(self.pos_px())) def _init_widget(self, widget: QWidget) -> None: @@ -956,7 +956,7 @@ class AbstractTab(QWidget): log.webview.warning("Unable to find event target!") return - evt.posted = True # type: ignore + evt.posted = True # type: ignore[attr-defined] QApplication.postEvent(recipient, evt) def navigation_blocked(self) -> bool: @@ -1139,7 +1139,8 @@ class AbstractTab(QWidget): def __repr__(self) -> str: try: qurl = self.url() - url = qurl.toDisplayString(QUrl.EncodeUnicode) # type: ignore + url = qurl.toDisplayString( + QUrl.EncodeUnicode) # type: ignore[arg-type] except (AttributeError, RuntimeError) as exc: url = '<{}>'.format(exc.__class__.__name__) else: diff --git a/qutebrowser/browser/commands.py b/qutebrowser/browser/commands.py index e4d768b85..08eb98fd2 100644 --- a/qutebrowser/browser/commands.py +++ b/qutebrowser/browser/commands.py @@ -647,11 +647,12 @@ class CommandDispatcher: def _yank_url(self, what): """Helper method for yank() to get the URL to copy.""" assert what in ['url', 'pretty-url'], what - flags = QUrl.RemovePassword + if what == 'pretty-url': - flags |= QUrl.DecodeReserved # type: ignore + flags = QUrl.RemovePassword | QUrl.DecodeReserved else: - flags |= QUrl.FullyEncoded # type: ignore + flags = QUrl.RemovePassword | QUrl.FullyEncoded + url = QUrl(self._current_url()) url_query = QUrlQuery() url_query_str = urlutils.query_string(url) @@ -662,7 +663,7 @@ class CommandDispatcher: if key in config.val.url.yank_ignored_parameters: url_query.removeQueryItem(key) url.setQuery(url_query) - return url.toString(flags) # type: ignore + return url.toString(flags) # type: ignore[arg-type] @cmdutils.register(instance='command-dispatcher', scope='window') @cmdutils.argument('what', choices=['selection', 'url', 'pretty-url', diff --git a/qutebrowser/browser/downloadview.py b/qutebrowser/browser/downloadview.py index b16b44218..edae7550f 100644 --- a/qutebrowser/browser/downloadview.py +++ b/qutebrowser/browser/downloadview.py @@ -105,7 +105,7 @@ class DownloadView(QListView): def __repr__(self): model = self.model() if model is None: - count = 'None' # type: ignore + count = 'None' # type: ignore[unreachable] else: count = model.rowCount() return utils.get_repr(self, count=count) diff --git a/qutebrowser/browser/hints.py b/qutebrowser/browser/hints.py index e564e7249..ba4aaac51 100644 --- a/qutebrowser/browser/hints.py +++ b/qutebrowser/browser/hints.py @@ -235,7 +235,7 @@ class HintActions: flags = QUrl.FullyEncoded | QUrl.RemovePassword if url.scheme() == 'mailto': flags |= QUrl.RemoveScheme - urlstr = url.toString(flags) # type: ignore + urlstr = url.toString(flags) # type: ignore[arg-type] new_content = urlstr @@ -256,14 +256,15 @@ class HintActions: def run_cmd(self, url: QUrl, context: HintContext) -> None: """Run the command based on a hint URL.""" - urlstr = url.toString(QUrl.FullyEncoded) # type: ignore + urlstr = url.toString(QUrl.FullyEncoded) # type: ignore[arg-type] args = context.get_args(urlstr) commandrunner = runners.CommandRunner(self._win_id) commandrunner.run_safely(' '.join(args)) def preset_cmd_text(self, url: QUrl, context: HintContext) -> None: """Preset a commandline text based on a hint URL.""" - urlstr = url.toDisplayString(QUrl.FullyEncoded) # type: ignore + flags = QUrl.FullyEncoded + urlstr = url.toDisplayString(flags) # type: ignore[arg-type] args = context.get_args(urlstr) text = ' '.join(args) if text[0] not in modeparsers.STARTCHARS: @@ -308,7 +309,8 @@ class HintActions: } url = elem.resolve_url(context.baseurl) if url is not None: - env['QUTE_URL'] = url.toString(QUrl.FullyEncoded) # type: ignore + flags = QUrl.FullyEncoded + env['QUTE_URL'] = url.toString(flags) # type: ignore[arg-type] try: userscripts.run_async(context.tab, cmd, *args, win_id=self._win_id, @@ -328,7 +330,7 @@ class HintActions: context: The HintContext to use. """ urlstr = url.toString( - QUrl.FullyEncoded | QUrl.RemovePassword) # type: ignore + QUrl.FullyEncoded | QUrl.RemovePassword) # type: ignore[arg-type] args = context.get_args(urlstr) commandrunner = runners.CommandRunner(self._win_id) commandrunner.run_safely('spawn ' + ' '.join(args)) diff --git a/qutebrowser/browser/navigate.py b/qutebrowser/browser/navigate.py index db467aa56..11be02c67 100644 --- a/qutebrowser/browser/navigate.py +++ b/qutebrowser/browser/navigate.py @@ -162,8 +162,8 @@ def _find_prevnext(prev, elems): # pylint: disable=bad-config-option for regex in getattr(config.val.hints, option): # pylint: enable=bad-config-option - log.hints.vdebug("== Checking regex '{}'." # type: ignore - .format(regex.pattern)) + log.hints.vdebug( # type: ignore[attr-defined] + "== Checking regex '{}'.".format(regex.pattern)) for e in elems: text = str(e) if not text: @@ -173,8 +173,8 @@ def _find_prevnext(prev, elems): regex.pattern, text)) return e else: - log.hints.vdebug("No match on '{}'!" # type: ignore - .format(text)) + log.hints.vdebug( # type: ignore[attr-defined] + "No match on '{}'!".format(text)) return None diff --git a/qutebrowser/browser/network/pac.py b/qutebrowser/browser/network/pac.py index 32c80340e..6ae01c7d8 100644 --- a/qutebrowser/browser/network/pac.py +++ b/qutebrowser/browser/network/pac.py @@ -65,7 +65,9 @@ def _js_slot(*args): # pylint: disable=protected-access return self._error_con.callAsConstructor([e]) # pylint: enable=protected-access - return pyqtSlot(*args, result=QJSValue)(new_method) # type: ignore + + deco = pyqtSlot(*args, result=QJSValue) # type: ignore[arg-type] + return deco(new_method) return _decorator @@ -215,10 +217,10 @@ class PACResolver: if from_file: string_flags = QUrl.PrettyDecoded else: - string_flags = QUrl.RemoveUserInfo # type: ignore + string_flags = QUrl.RemoveUserInfo # type: ignore[assignment] if query.url().scheme() == 'https': - string_flags |= QUrl.RemovePath # type: ignore - string_flags |= QUrl.RemoveQuery # type: ignore + string_flags |= QUrl.RemovePath # type: ignore[assignment] + string_flags |= QUrl.RemoveQuery # type: ignore[assignment] result = self._resolver.call([query.url().toString(string_flags), query.peerHostName()]) @@ -266,7 +268,8 @@ class PACFetcher(QObject): """Fetch the proxy from the remote URL.""" assert self._manager is not None self._reply = self._manager.get(QNetworkRequest(self._pac_url)) - self._reply.finished.connect(self._finish) # type: ignore + self._reply.finished.connect( # type: ignore[attr-defined] + self._finish) @pyqtSlot() def _finish(self): diff --git a/qutebrowser/browser/network/proxy.py b/qutebrowser/browser/network/proxy.py index 541403222..18d2f060b 100644 --- a/qutebrowser/browser/network/proxy.py +++ b/qutebrowser/browser/network/proxy.py @@ -52,7 +52,8 @@ def _warn_for_pac(): @pyqtSlot() def shutdown(): - QNetworkProxyFactory.setApplicationProxyFactory(None) # type: ignore + QNetworkProxyFactory.setApplicationProxyFactory( + None) # type: ignore[arg-type] class ProxyFactory(QNetworkProxyFactory): @@ -71,6 +72,18 @@ class ProxyFactory(QNetworkProxyFactory): else: return None + def _set_capabilities(self, proxy): + if proxy.type() == QNetworkProxy.NoProxy: + return + + capabilities = proxy.capabilities() + lookup_cap = QNetworkProxy.HostNameLookupCapability + if config.val.content.proxy_dns_requests: + capabilities |= lookup_cap + else: + capabilities &= ~lookup_cap + proxy.setCapabilities(capabilities) + def queryProxy(self, query): """Get the QNetworkProxies for a query. @@ -96,13 +109,6 @@ class ProxyFactory(QNetworkProxyFactory): proxies = proxy.resolve(query) else: proxies = [proxy] - for p in proxies: - if p.type() != QNetworkProxy.NoProxy: - capabilities = p.capabilities() - lookup_cap = QNetworkProxy.HostNameLookupCapability - if config.val.content.proxy_dns_requests: - capabilities |= lookup_cap # type: ignore - else: - capabilities &= ~lookup_cap # type: ignore - p.setCapabilities(capabilities) + for proxy in proxies: + self._set_capabilities(proxy) return proxies diff --git a/qutebrowser/browser/pdfjs.py b/qutebrowser/browser/pdfjs.py index 7c696ac0b..cca21abcb 100644 --- a/qutebrowser/browser/pdfjs.py +++ b/qutebrowser/browser/pdfjs.py @@ -84,6 +84,9 @@ def _generate_pdfjs_script(filename): url_query.addQueryItem('filename', filename) url.setQuery(url_query) + js_url = javascript.to_js( + url.toString(QUrl.FullyEncoded)) # type: ignore[arg-type] + return jinja.js_environment.from_string(""" document.addEventListener("DOMContentLoaded", function() { if (typeof window.PDFJS !== 'undefined') { @@ -105,7 +108,7 @@ def _generate_pdfjs_script(filename): viewer.open({{ url }}); }); """).render( - url=javascript.to_js(url.toString(QUrl.FullyEncoded)), # type: ignore + url=js_url, # WORKAROUND for https://bugreports.qt.io/browse/QTBUG-70420 disable_create_object_url=( not qtutils.version_check('5.12') and @@ -243,7 +246,7 @@ def get_main_url(filename: str, original_url: QUrl) -> QUrl: query = QUrlQuery() query.addQueryItem('filename', filename) # read from our JS query.addQueryItem('file', '') # to avoid pdfjs opening the default PDF - urlstr = original_url.toString(QUrl.FullyEncoded) # type: ignore + urlstr = original_url.toString(QUrl.FullyEncoded) # type: ignore[arg-type] query.addQueryItem('source', urlstr) url.setQuery(query) return url diff --git a/qutebrowser/browser/qutescheme.py b/qutebrowser/browser/qutescheme.py index 6c61fbc22..d40dc4530 100644 --- a/qutebrowser/browser/qutescheme.py +++ b/qutebrowser/browser/qutescheme.py @@ -39,7 +39,7 @@ try: import secrets except ImportError: # New in Python 3.6 - secrets = None # type: ignore + secrets = None # type: ignore[assignment] from PyQt5.QtCore import QUrlQuery, QUrl, qVersion diff --git a/qutebrowser/browser/webelem.py b/qutebrowser/browser/webelem.py index ba3c1e226..a35549571 100644 --- a/qutebrowser/browser/webelem.py +++ b/qutebrowser/browser/webelem.py @@ -392,7 +392,7 @@ class AbstractWebElement(collections.abc.MutableMapping): window.show() # FIXME:typing Why can't mypy determine the type of # window.tabbed_browser? - window.tabbed_browser.tabopen(url) # type: ignore + window.tabbed_browser.tabopen(url) # type: ignore[has-type] else: raise ValueError("Unknown ClickTarget {}".format(click_target)) diff --git a/qutebrowser/browser/webengine/webenginedownloads.py b/qutebrowser/browser/webengine/webenginedownloads.py index 2e0129605..44d11f728 100644 --- a/qutebrowser/browser/webengine/webenginedownloads.py +++ b/qutebrowser/browser/webengine/webenginedownloads.py @@ -42,13 +42,15 @@ class DownloadItem(downloads.AbstractDownloadItem): def __init__(self, qt_item: QWebEngineDownloadItem, parent=None): super().__init__(parent) self._qt_item = qt_item - qt_item.downloadProgress.connect( # type: ignore + qt_item.downloadProgress.connect( # type: ignore[attr-defined] self.stats.on_download_progress) - qt_item.stateChanged.connect(self._on_state_changed) # type: ignore + qt_item.stateChanged.connect( # type: ignore[attr-defined] + self._on_state_changed) # Ensure wrapped qt_item is deleted manually when the wrapper object # is deleted. See https://github.com/qutebrowser/qutebrowser/issues/3373 - self.destroyed.connect(self._qt_item.deleteLater) # type: ignore + self.destroyed.connect( # type: ignore[attr-defined] + self._qt_item.deleteLater) def _is_page_download(self): """Check if this item is a page (i.e. mhtml) download.""" @@ -93,7 +95,8 @@ class DownloadItem(downloads.AbstractDownloadItem): "{}".format(state_name)) def _do_die(self): - self._qt_item.downloadProgress.disconnect() # type: ignore + progress_signal = self._qt_item.downloadProgress + progress_signal.disconnect() # type: ignore[attr-defined] if self._qt_item.state() != QWebEngineDownloadItem.DownloadInterrupted: self._qt_item.cancel() diff --git a/qutebrowser/browser/webengine/webengineelem.py b/qutebrowser/browser/webengine/webengineelem.py index ffaaf346c..d765483fe 100644 --- a/qutebrowser/browser/webengine/webengineelem.py +++ b/qutebrowser/browser/webengine/webengineelem.py @@ -248,7 +248,7 @@ class WebEngineElement(webelem.AbstractWebElement): # (it does so with a 0ms QTimer...) # This is also used in Qt's tests: # https://github.com/qt/qtwebengine/commit/5e572e88efa7ba7c2b9138ec19e606d3e345ac90 - QApplication.processEvents( # type: ignore + QApplication.processEvents( # type: ignore[call-overload] QEventLoop.ExcludeSocketNotifiers | QEventLoop.ExcludeUserInputEvents) diff --git a/qutebrowser/browser/webengine/webenginequtescheme.py b/qutebrowser/browser/webengine/webenginequtescheme.py index f97d0a884..2c69b521f 100644 --- a/qutebrowser/browser/webengine/webenginequtescheme.py +++ b/qutebrowser/browser/webengine/webenginequtescheme.py @@ -26,7 +26,7 @@ try: from PyQt5.QtWebEngineCore import QWebEngineUrlScheme except ImportError: # Added in Qt 5.12 - QWebEngineUrlScheme = None # type: ignore + QWebEngineUrlScheme = None # type: ignore[misc, assignment] from qutebrowser.browser import qutescheme from qutebrowser.utils import log, qtutils @@ -165,6 +165,7 @@ def init(): if QWebEngineUrlScheme is not None: assert not QWebEngineUrlScheme.schemeByName(b'qute').name() scheme = QWebEngineUrlScheme(b'qute') - scheme.setFlags(QWebEngineUrlScheme.LocalScheme | # type: ignore - QWebEngineUrlScheme.LocalAccessAllowed) + scheme.setFlags( + QWebEngineUrlScheme.LocalScheme | # type: ignore[arg-type] + QWebEngineUrlScheme.LocalAccessAllowed) QWebEngineUrlScheme.registerScheme(scheme) diff --git a/qutebrowser/browser/webengine/webenginesettings.py b/qutebrowser/browser/webengine/webenginesettings.py index 5f08c4b7f..d5d654dbf 100644 --- a/qutebrowser/browser/webengine/webenginesettings.py +++ b/qutebrowser/browser/webengine/webenginesettings.py @@ -362,7 +362,8 @@ def _init_profiles(): default_profile = QWebEngineProfile.defaultProfile() init_user_agent() - default_profile.setter = ProfileSetter(default_profile) # type: ignore + default_profile.setter = ProfileSetter( # type: ignore[attr-defined] + default_profile) default_profile.setCachePath( os.path.join(standarddir.cache(), 'webengine')) default_profile.setPersistentStoragePath( @@ -372,7 +373,8 @@ def _init_profiles(): if not qtutils.is_single_process(): private_profile = QWebEngineProfile() - private_profile.setter = ProfileSetter(private_profile) # type: ignore + private_profile.setter = ProfileSetter( # type: ignore[attr-defined] + private_profile) assert private_profile.isOffTheRecord() private_profile.setter.init_profile() diff --git a/qutebrowser/browser/webengine/webenginetab.py b/qutebrowser/browser/webengine/webenginetab.py index de3300e1e..9f946beb9 100644 --- a/qutebrowser/browser/webengine/webenginetab.py +++ b/qutebrowser/browser/webengine/webenginetab.py @@ -125,7 +125,7 @@ class WebEngineAction(browsertab.AbstractAction): tb = objreg.get('tabbed-browser', scope='window', window=self._tab.win_id) urlstr = self._tab.url().toString( - QUrl.RemoveUserInfo) # type: ignore + QUrl.RemoveUserInfo) # type: ignore[arg-type] # The original URL becomes the path of a view-source: URL # (without a host), but query/fragment should stay. url = QUrl('view-source:' + urlstr) @@ -239,9 +239,11 @@ class WebEngineSearch(browsertab.AbstractSearch): back yet. """ + _NO_FLAGS = QWebEnginePage.FindFlags(0) # type: ignore[call-overload] + def __init__(self, tab, parent=None): super().__init__(tab, parent) - self._flags = QWebEnginePage.FindFlags(0) # type: ignore + self._flags = self._NO_FLAGS self._pending_searches = 0 # The API necessary to stop wrapping was added in this version self._wrap_handler = _WebEngineSearchWrapHandler() @@ -296,7 +298,7 @@ class WebEngineSearch(browsertab.AbstractSearch): return self.text = text - self._flags = QWebEnginePage.FindFlags(0) # type: ignore + self._flags = self._NO_FLAGS self._wrap_handler.reset_match_data() self._wrap_handler.flag_wrap = wrap if self._is_case_sensitive(ignore_case): @@ -315,7 +317,8 @@ class WebEngineSearch(browsertab.AbstractSearch): def prev_result(self, *, result_cb=None): # The int() here makes sure we get a copy of the flags. - flags = QWebEnginePage.FindFlags(int(self._flags)) # type: ignore + flags = QWebEnginePage.FindFlags( + int(self._flags)) # type: ignore[call-overload] if flags & QWebEnginePage.FindBackward: if self._wrap_handler.prevent_wrapping(going_up=False): return @@ -1411,7 +1414,7 @@ class WebEngineTab(browsertab.AbstractTab): title_url = QUrl(url) title_url.setScheme('') title_url_str = title_url.toDisplayString( - QUrl.RemoveScheme) # type: ignore + QUrl.RemoveScheme) # type: ignore[arg-type] if title == title_url_str.strip('/'): title = "" @@ -1433,12 +1436,15 @@ class WebEngineTab(browsertab.AbstractTab): title="Proxy authentication required", text=msg, mode=usertypes.PromptMode.user_pwd, abort_on=[self.abort_questions], url=urlstr) + if answer is not None: authenticator.setUser(answer.user) authenticator.setPassword(answer.password) else: try: - sip.assign(authenticator, QAuthenticator()) # type: ignore + sip.assign( # type: ignore[attr-defined] + authenticator, + QAuthenticator()) except AttributeError: self._show_error_page(url, "Proxy authentication required") @@ -1459,7 +1465,8 @@ class WebEngineTab(browsertab.AbstractTab): if not netrc_success and answer is None: log.network.debug("Aborting auth") try: - sip.assign(authenticator, QAuthenticator()) # type: ignore + sip.assign( # type: ignore[attr-defined] + authenticator, QAuthenticator()) except AttributeError: # WORKAROUND for # https://www.riverbankcomputing.com/pipermail/pyqt/2016-December/038400.html @@ -1748,8 +1755,10 @@ class WebEngineTab(browsertab.AbstractTab): page.loadFinished.connect(self._on_load_finished) self.before_load_started.connect(self._on_before_load_started) - self.shutting_down.connect(self.abort_questions) # type: ignore - self.load_started.connect(self.abort_questions) # type: ignore + self.shutting_down.connect( + self.abort_questions) # type: ignore[arg-type] + self.load_started.connect( + self.abort_questions) # type: ignore[arg-type] # pylint: disable=protected-access self.audio._connect_signals() diff --git a/qutebrowser/browser/webkit/cookies.py b/qutebrowser/browser/webkit/cookies.py index 924c3fd01..4b2070f1d 100644 --- a/qutebrowser/browser/webkit/cookies.py +++ b/qutebrowser/browser/webkit/cookies.py @@ -95,7 +95,8 @@ class CookieJar(RAMCookieJar): """Parse cookies from lineparser and store them.""" cookies = [] # type: typing.Sequence[QNetworkCookie] for line in self._lineparser: - cookies += QNetworkCookie.parseCookies(line) # type: ignore + line_cookies = QNetworkCookie.parseCookies(line) + cookies += line_cookies # type: ignore[operator] self.setAllCookies(cookies) def purge_old_cookies(self): @@ -105,7 +106,7 @@ class CookieJar(RAMCookieJar): now = QDateTime.currentDateTime() cookies = [c for c in self.allCookies() if c.isSessionCookie() or - c.expirationDate() >= now] # type: ignore + c.expirationDate() >= now] # type: ignore[operator] self.setAllCookies(cookies) def save(self): diff --git a/qutebrowser/browser/webkit/network/networkmanager.py b/qutebrowser/browser/webkit/network/networkmanager.py index 66b5132c4..039ed0dba 100644 --- a/qutebrowser/browser/webkit/network/networkmanager.py +++ b/qutebrowser/browser/webkit/network/networkmanager.py @@ -169,14 +169,15 @@ class NetworkManager(QNetworkAccessManager): } self._set_cookiejar() self._set_cache() - self.sslErrors.connect(self.on_ssl_errors) # type: ignore + self.sslErrors.connect( # type: ignore[attr-defined] + self.on_ssl_errors) self._rejected_ssl_errors = collections.defaultdict( list) # type: _SavedErrorsType self._accepted_ssl_errors = collections.defaultdict( list) # type: _SavedErrorsType - self.authenticationRequired.connect( # type: ignore + self.authenticationRequired.connect( # type: ignore[attr-defined] self.on_authentication_required) - self.proxyAuthenticationRequired.connect( # type: ignore + self.proxyAuthenticationRequired.connect( # type: ignore[attr-defined] self.on_proxy_authentication_required) self.netrc_used = False diff --git a/qutebrowser/browser/webkit/network/networkreply.py b/qutebrowser/browser/webkit/network/networkreply.py index ff2c1ece5..182292ac1 100644 --- a/qutebrowser/browser/webkit/network/networkreply.py +++ b/qutebrowser/browser/webkit/network/networkreply.py @@ -59,12 +59,15 @@ class FixedDataNetworkReply(QNetworkReply): # For some reason, a segfault will be triggered if these lambdas aren't # there. # pylint: disable=unnecessary-lambda - QTimer.singleShot(0, lambda: - self.metaDataChanged.emit()) # type: ignore - QTimer.singleShot(0, lambda: - self.readyRead.emit()) # type: ignore - QTimer.singleShot(0, lambda: - self.finished.emit()) # type: ignore + QTimer.singleShot( + 0, + lambda: self.metaDataChanged.emit()) # type: ignore[attr-defined] + QTimer.singleShot( + 0, + lambda: self.readyRead.emit()) # type: ignore[attr-defined] + QTimer.singleShot( + 0, + lambda: self.finished.emit()) # type: ignore[attr-defined] @pyqtSlot() def abort(self): @@ -120,9 +123,9 @@ class ErrorNetworkReply(QNetworkReply): self.setOpenMode(QIODevice.ReadOnly) self.setError(error, errorstring) QTimer.singleShot(0, lambda: - self.error.emit(error)) # type: ignore + self.error.emit(error)) # type: ignore[attr-defined] QTimer.singleShot(0, lambda: - self.finished.emit()) # type: ignore + self.finished.emit()) # type: ignore[attr-defined] def abort(self): """Do nothing since it's a fake reply.""" @@ -150,7 +153,7 @@ class RedirectNetworkReply(QNetworkReply): super().__init__(parent) self.setAttribute(QNetworkRequest.RedirectionTargetAttribute, new_url) QTimer.singleShot(0, lambda: - self.finished.emit()) # type: ignore + self.finished.emit()) # type: ignore[attr-defined] def abort(self): """Called when there's e.g. a redirection limit.""" diff --git a/qutebrowser/browser/webkit/webkitelem.py b/qutebrowser/browser/webkit/webkitelem.py index b318425a3..de4fbd860 100644 --- a/qutebrowser/browser/webkit/webkitelem.py +++ b/qutebrowser/browser/webkit/webkitelem.py @@ -194,7 +194,7 @@ class WebKitElement(webelem.AbstractWebElement): return None text = utils.compact_text(self._elem.toOuterXml(), 500) - log.webelem.vdebug( # type: ignore + log.webelem.vdebug( # type: ignore[attr-defined] "Client rectangles of element '{}': {}".format(text, rects)) for i in range(int(rects.get("length", 0))): diff --git a/qutebrowser/browser/webkit/webkittab.py b/qutebrowser/browser/webkit/webkittab.py index fb50a62a1..361e2a402 100644 --- a/qutebrowser/browser/webkit/webkittab.py +++ b/qutebrowser/browser/webkit/webkittab.py @@ -85,9 +85,11 @@ class WebKitSearch(browsertab.AbstractSearch): """QtWebKit implementations related to searching on the page.""" + _NO_FLAGS = QWebPage.FindFlags(0) # type: ignore[call-overload] + def __init__(self, tab, parent=None): super().__init__(tab, parent) - self._flags = QWebPage.FindFlags(0) # type: ignore + self._flags = self._NO_FLAGS def _call_cb(self, callback, found, text, flags, caller): """Call the given callback if it's non-None. @@ -139,7 +141,7 @@ class WebKitSearch(browsertab.AbstractSearch): self.text = text self.search_displayed = True - self._flags = QWebPage.FindFlags(0) # type: ignore + self._flags = self._NO_FLAGS if self._is_case_sensitive(ignore_case): self._flags |= QWebPage.FindCaseSensitively if reverse: @@ -161,7 +163,8 @@ class WebKitSearch(browsertab.AbstractSearch): def prev_result(self, *, result_cb=None): self.search_displayed = True # The int() here makes sure we get a copy of the flags. - flags = QWebPage.FindFlags(int(self._flags)) # type: ignore + flags = QWebPage.FindFlags( + int(self._flags)) # type: ignore[call-overload] if flags & QWebPage.FindBackward: flags &= ~QWebPage.FindBackward else: diff --git a/qutebrowser/browser/webkit/webpage.py b/qutebrowser/browser/webkit/webpage.py index 4d38e380d..9055bff24 100644 --- a/qutebrowser/browser/webkit/webpage.py +++ b/qutebrowser/browser/webkit/webpage.py @@ -78,22 +78,24 @@ class BrowserPage(QWebPage): self.setNetworkAccessManager(self._networkmanager) self.setForwardUnsupportedContent(True) self.reloading.connect(self._networkmanager.clear_rejected_ssl_errors) - self.printRequested.connect( # type: ignore + self.printRequested.connect( # type: ignore[attr-defined] self.on_print_requested) - self.downloadRequested.connect( # type: ignore + self.downloadRequested.connect( # type: ignore[attr-defined] self.on_download_requested) - self.unsupportedContent.connect( # type: ignore + self.unsupportedContent.connect( # type: ignore[attr-defined] self.on_unsupported_content) - self.loadStarted.connect(self.on_load_started) # type: ignore - self.featurePermissionRequested.connect( # type: ignore + self.loadStarted.connect( # type: ignore[attr-defined] + self.on_load_started) + self.featurePermissionRequested.connect( # type: ignore[attr-defined] self._on_feature_permission_requested) - self.saveFrameStateRequested.connect( # type: ignore + self.saveFrameStateRequested.connect( # type: ignore[attr-defined] self.on_save_frame_state_requested) - self.restoreFrameStateRequested.connect( # type: ignore + self.restoreFrameStateRequested.connect( # type: ignore[attr-defined] self.on_restore_frame_state_requested) - self.loadFinished.connect( # type: ignore + self.loadFinished.connect( # type: ignore[attr-defined] functools.partial(self._inject_userjs, self.mainFrame())) - self.frameCreated.connect(self._connect_userjs_signals) # type: ignore + self.frameCreated.connect( # type: ignore[attr-defined] + self._connect_userjs_signals) @pyqtSlot('QWebFrame*') def _connect_userjs_signals(self, frame): @@ -206,8 +208,10 @@ class BrowserPage(QWebPage): suggested_file = "" if info.suggestedFileNames: suggested_file = info.suggestedFileNames[0] + files.fileNames, _ = QFileDialog.getOpenFileNames( - None, None, suggested_file) # type: ignore + None, None, suggested_file) # type: ignore[arg-type] + return True def shutdown(self): diff --git a/qutebrowser/browser/webkit/webview.py b/qutebrowser/browser/webkit/webview.py index e200e4b6a..6706848dd 100644 --- a/qutebrowser/browser/webkit/webview.py +++ b/qutebrowser/browser/webkit/webview.py @@ -87,7 +87,8 @@ class WebView(QWebView): stylesheet.set_register(self) def __repr__(self): - urlstr = self.url().toDisplayString(QUrl.EncodeUnicode) # type: ignore + flags = QUrl.EncodeUnicode + urlstr = self.url().toDisplayString(flags) # type: ignore[arg-type] url = utils.elide(urlstr, 100) return utils.get_repr(self, tab_id=self._tab_id, url=url) @@ -97,7 +98,7 @@ class WebView(QWebView): # Copied from: # https://code.google.com/p/webscraping/source/browse/webkit.py#325 try: - self.setPage(None) # type: ignore + self.setPage(None) # type: ignore[arg-type] except RuntimeError: # It seems sometimes Qt has already deleted the QWebView and we # get: RuntimeError: wrapped C/C++ object of type WebView has been @@ -180,9 +181,9 @@ class WebView(QWebView): This is not needed for QtWebEngine, so it's in here. """ menu = self.page().createStandardContextMenu() - self.shutting_down.connect(menu.close) # type: ignore + self.shutting_down.connect(menu.close) # type: ignore[arg-type] mm = modeman.instance(self.win_id) - mm.entered.connect(menu.close) # type: ignore + mm.entered.connect(menu.close) # type: ignore[arg-type] menu.exec_(e.globalPos()) def showEvent(self, e): diff --git a/qutebrowser/commands/command.py b/qutebrowser/commands/command.py index 44d639e0a..4eefe481c 100644 --- a/qutebrowser/commands/command.py +++ b/qutebrowser/commands/command.py @@ -244,7 +244,7 @@ class Command: args = self._param_to_argparse_args(param, is_bool) callsig = debug_utils.format_call(self.parser.add_argument, args, kwargs, full=False) - log.commands.vdebug( # type: ignore + log.commands.vdebug( # type: ignore[attr-defined] 'Adding arg {} of type {} -> {}' .format(param.name, typ, callsig)) self.parser.add_argument(*args, **kwargs) @@ -409,7 +409,8 @@ class Command: if hasattr(typing, 'UnionMeta'): # Python 3.5.2 # pylint: disable=no-member,useless-suppression - is_union = isinstance(typ, typing.UnionMeta) # type: ignore + is_union = isinstance( + typ, typing.UnionMeta) # type: ignore[attr-defined] else: is_union = getattr(typ, '__origin__', None) is typing.Union @@ -575,7 +576,7 @@ class Command: def register(self): """Register this command in objects.commands.""" - log.commands.vdebug( # type: ignore + log.commands.vdebug( # type: ignore[attr-defined] "Registering command {} (from {}:{})".format( self.name, self.handler.__module__, self.handler.__qualname__)) if self.name in objects.commands: diff --git a/qutebrowser/commands/runners.py b/qutebrowser/commands/runners.py index 2537e1d9c..ec052b263 100644 --- a/qutebrowser/commands/runners.py +++ b/qutebrowser/commands/runners.py @@ -94,7 +94,7 @@ def _init_variable_replacements() -> typing.Mapping[str, _ReplacementFunction]: modified_key = '{' + key + '}' # x = modified_key is to avoid binding x as a closure replacements[modified_key] = ( - lambda _, x=modified_key: x) # type: ignore + lambda _, x=modified_key: x) # type: ignore[misc] return replacements diff --git a/qutebrowser/commands/userscripts.py b/qutebrowser/commands/userscripts.py index f9e9af484..b9ef41f01 100644 --- a/qutebrowser/commands/userscripts.py +++ b/qutebrowser/commands/userscripts.py @@ -62,7 +62,8 @@ class _QtFIFOReader(QObject): self._fifo = os.fdopen(fd, 'r') self._notifier = QSocketNotifier(typing.cast(sip.voidptr, fd), QSocketNotifier.Read, self) - self._notifier.activated.connect(self.read_line) # type: ignore + self._notifier.activated.connect( # type: ignore[attr-defined] + self.read_line) @pyqtSlot() def read_line(self): @@ -265,7 +266,7 @@ class _POSIXUserscriptRunner(_BaseUserscriptRunner): return self._reader = _QtFIFOReader(self._filepath) - self._reader.got_line.connect(self.got_cmd) # type: ignore + self._reader.got_line.connect(self.got_cmd) # type: ignore[arg-type] @pyqtSlot() def on_proc_finished(self): diff --git a/qutebrowser/completion/completiondelegate.py b/qutebrowser/completion/completiondelegate.py index d841249aa..11e7c96d8 100644 --- a/qutebrowser/completion/completiondelegate.py +++ b/qutebrowser/completion/completiondelegate.py @@ -290,7 +290,7 @@ class CompletionItemDelegate(QStyledItemDelegate): size = self._style.sizeFromContents(QStyle.CT_ItemViewItem, self._opt, docsize, self._opt.widget) qtutils.ensure_valid(size) - return size + QSize(10, 3) # type: ignore + return size + QSize(10, 3) # type: ignore[operator] def paint(self, painter, option, index): """Override the QStyledItemDelegate paint function. diff --git a/qutebrowser/completion/completionwidget.py b/qutebrowser/completion/completionwidget.py index 4187ee28e..b9be0bd9d 100644 --- a/qutebrowser/completion/completionwidget.py +++ b/qutebrowser/completion/completionwidget.py @@ -278,7 +278,7 @@ class CompletionView(QTreeView): selmodel.setCurrentIndex( idx, - QItemSelectionModel.ClearAndSelect | # type: ignore + QItemSelectionModel.ClearAndSelect | # type: ignore[arg-type] QItemSelectionModel.Rows) # if the last item is focused, try to fetch more diff --git a/qutebrowser/completion/models/completionmodel.py b/qutebrowser/completion/models/completionmodel.py index d28ca8907..e5f28e0e7 100644 --- a/qutebrowser/completion/models/completionmodel.py +++ b/qutebrowser/completion/models/completionmodel.py @@ -183,11 +183,13 @@ class CompletionModel(QAbstractItemModel): # WORKAROUND: # layoutChanged is broken in PyQt 5.7.1, so we must use metaObject # https://www.riverbankcomputing.com/pipermail/pyqt/2017-January/038483.html - self.metaObject().invokeMethod(self, # type: ignore - "layoutAboutToBeChanged") + meta = self.metaObject() + meta.invokeMethod(self, # type: ignore[misc, call-overload] + "layoutAboutToBeChanged") for cat in self._categories: cat.set_pattern(pattern) - self.metaObject().invokeMethod(self, "layoutChanged") # type: ignore + meta.invokeMethod(self, # type: ignore[misc, call-overload] + "layoutChanged") def first_item(self): """Return the index of the first child (non-category) in the model.""" diff --git a/qutebrowser/components/misccommands.py b/qutebrowser/components/misccommands.py index c7d3a8cff..b8c4b98b4 100644 --- a/qutebrowser/components/misccommands.py +++ b/qutebrowser/components/misccommands.py @@ -71,9 +71,10 @@ def _print_preview(tab: apitypes.Tab) -> None: tab.printing.check_preview_support() diag = QPrintPreviewDialog(tab) diag.setAttribute(Qt.WA_DeleteOnClose) - diag.setWindowFlags(diag.windowFlags() | # type: ignore - Qt.WindowMaximizeButtonHint | - Qt.WindowMinimizeButtonHint) + diag.setWindowFlags( + diag.windowFlags() | # type: ignore[operator, arg-type] + Qt.WindowMaximizeButtonHint | + Qt.WindowMinimizeButtonHint) diag.paintRequested.connect(functools.partial( tab.printing.to_printer, callback=print_callback)) diag.exec_() diff --git a/qutebrowser/config/config.py b/qutebrowser/config/config.py index a38001e75..007b44404 100644 --- a/qutebrowser/config/config.py +++ b/qutebrowser/config/config.py @@ -206,7 +206,7 @@ class KeyConfig: 'mode'.format(key, mode)) self._validate(key, mode) - log.keyboard.vdebug( # type: ignore + log.keyboard.vdebug( # type: ignore[attr-defined] "Adding binding {} -> {} in mode {}.".format(key, command, mode)) bindings = self._config.get_mutable_obj('bindings.commands') diff --git a/qutebrowser/config/configfiles.py b/qutebrowser/config/configfiles.py index 098385c7b..7d567ebd3 100644 --- a/qutebrowser/config/configfiles.py +++ b/qutebrowser/config/configfiles.py @@ -692,8 +692,8 @@ def read_config_py(filename: str, raising: bool = False) -> None: basename = os.path.basename(filename) module = types.ModuleType('config') - module.config = api # type: ignore - module.c = container # type: ignore + module.config = api # type: ignore[attr-defined] + module.c = container # type: ignore[attr-defined] module.__file__ = filename try: diff --git a/qutebrowser/config/configtypes.py b/qutebrowser/config/configtypes.py index d5a78cf27..6eec13293 100644 --- a/qutebrowser/config/configtypes.py +++ b/qutebrowser/config/configtypes.py @@ -703,8 +703,10 @@ class Bool(BaseType): super().__init__(none_ok) self.valid_values = ValidValues('true', 'false', generate_docs=False) - def to_py(self, value: typing.Optional[bool]) -> typing.Optional[bool]: + def to_py(self, + value: typing.Union[bool, str, None]) -> typing.Optional[bool]: self._basic_py_validation(value, bool) + assert not isinstance(value, str) return value def from_str(self, value: str) -> typing.Optional[bool]: @@ -734,15 +736,15 @@ class BoolAsk(Bool): super().__init__(none_ok) self.valid_values = ValidValues('true', 'false', 'ask') - def to_py(self, # type: ignore + def to_py(self, # type: ignore[override] value: typing.Union[bool, str]) -> typing.Union[bool, str, None]: # basic validation unneeded if it's == 'ask' and done by Bool if we # call super().to_py if isinstance(value, str) and value.lower() == 'ask': return 'ask' - return super().to_py(value) # type: ignore + return super().to_py(value) - def from_str(self, # type: ignore + def from_str(self, # type: ignore[override] value: str) -> typing.Union[bool, str, None]: # basic validation unneeded if it's == 'ask' and done by Bool if we # call super().from_str @@ -1347,7 +1349,7 @@ class QtFont(FontBase): families = self._parse_families(family_str) if hasattr(font, 'setFamilies'): # Added in Qt 5.13 - font.setFamily(families.family) # type: ignore + font.setFamily(families.family) # type: ignore[arg-type] font.setFamilies(list(families)) else: # pragma: no cover font.setFamily(families.to_str(quote=False)) @@ -1832,7 +1834,7 @@ class Padding(Dict): fixed_keys=['top', 'bottom', 'left', 'right'], none_ok=none_ok) - def to_py( # type: ignore + def to_py( # type: ignore[override] self, value: typing.Union[usertypes.Unset, typing.Dict, None], ) -> typing.Union[usertypes.Unset, PaddingValues]: diff --git a/qutebrowser/config/stylesheet.py b/qutebrowser/config/stylesheet.py index 276d37094..10e6e4e52 100644 --- a/qutebrowser/config/stylesheet.py +++ b/qutebrowser/config/stylesheet.py @@ -109,7 +109,7 @@ class _StyleSheetObserver(QObject): def register(self) -> None: """Do a first update and listen for more.""" qss = self._get_stylesheet() - log.config.vdebug( # type: ignore + log.config.vdebug( # type: ignore[attr-defined] "stylesheet for {}: {}".format(self._obj.__class__.__name__, qss)) self._obj.setStyleSheet(qss) if self._update: diff --git a/qutebrowser/config/websettings.py b/qutebrowser/config/websettings.py index f92d03cc2..007758254 100644 --- a/qutebrowser/config/websettings.py +++ b/qutebrowser/config/websettings.py @@ -103,6 +103,9 @@ class AbstractSettings: def __init__(self, settings: typing.Any) -> None: self._settings = settings + def _assert_not_unset(self, value: typing.Any) -> None: + assert value is not usertypes.UNSET + def set_attribute(self, name: str, value: typing.Any) -> bool: """Set the given QWebSettings/QWebEngineSettings attribute. @@ -139,7 +142,7 @@ class AbstractSettings: Return: True if there was a change, False otherwise. """ - assert value is not usertypes.UNSET # type: ignore + self._assert_not_unset(value) family = self._FONT_SIZES[name] old_value = self._settings.fontSize(family) self._settings.setFontSize(family, value) @@ -154,7 +157,7 @@ class AbstractSettings: Return: True if there was a change, False otherwise. """ - assert value is not usertypes.UNSET # type: ignore + self._assert_not_unset(value) family = self._FONT_FAMILIES[name] if value is None: font = QFont() @@ -172,7 +175,7 @@ class AbstractSettings: Return: True if there was a change, False otherwise. """ - assert encoding is not usertypes.UNSET # type: ignore + self._assert_not_unset(encoding) old_value = self._settings.defaultTextEncoding() self._settings.setDefaultTextEncoding(encoding) return old_value != encoding diff --git a/qutebrowser/extensions/loader.py b/qutebrowser/extensions/loader.py index 928f2856c..41b9c63fd 100644 --- a/qutebrowser/extensions/loader.py +++ b/qutebrowser/extensions/loader.py @@ -82,8 +82,8 @@ def add_module_info(module: types.ModuleType) -> ModuleInfo: """Add ModuleInfo to a module (if not added yet).""" # pylint: disable=protected-access if not hasattr(module, '__qute_module_info'): - module.__qute_module_info = ModuleInfo() # type: ignore - return module.__qute_module_info # type: ignore + module.__qute_module_info = ModuleInfo() # type: ignore[attr-defined] + return module.__qute_module_info # type: ignore[attr-defined] def load_components(*, skip_hooks: bool = False) -> None: @@ -109,7 +109,7 @@ def _walk_normal() -> typing.Iterator[ExtensionInfo]: for _finder, name, ispkg in pkgutil.walk_packages( # Only packages have a __path__ attribute, # but we're sure this is one. - path=components.__path__, # type: ignore + path=components.__path__, # type: ignore[attr-defined] prefix=components.__name__ + '.', onerror=_on_walk_error): if ispkg: diff --git a/qutebrowser/keyinput/keyutils.py b/qutebrowser/keyinput/keyutils.py index 2917161db..142bedd2f 100644 --- a/qutebrowser/keyinput/keyutils.py +++ b/qutebrowser/keyinput/keyutils.py @@ -171,7 +171,8 @@ def _assert_plain_key(key: Qt.Key) -> None: def _assert_plain_modifier(key: _ModifierType) -> None: """Make sure this is a modifier without a key mixed in.""" - assert not key & ~Qt.KeyboardModifierMask, hex(key) # type: ignore + mask = Qt.KeyboardModifierMask + assert not key & ~mask, hex(key) # type: ignore[operator] def _is_printable(key: Qt.Key) -> bool: @@ -285,8 +286,9 @@ def _modifiers_to_string(modifiers: _ModifierType) -> str: modifier. """ _assert_plain_modifier(modifiers) - if modifiers & Qt.GroupSwitchModifier: # type: ignore - modifiers &= ~Qt.GroupSwitchModifier # type: ignore + altgr = Qt.GroupSwitchModifier + if modifiers & altgr: # type: ignore[operator] + modifiers &= ~altgr # type: ignore[operator, assignment] result = 'AltGr+' else: result = '' @@ -453,7 +455,7 @@ class KeyInfo: return '' text = QKeySequence(self.key).toString() - if not self.modifiers & Qt.ShiftModifier: # type: ignore + if not self.modifiers & Qt.ShiftModifier: # type: ignore[operator] text = text.lower() return text @@ -510,7 +512,7 @@ class KeySequence: """Iterate over KeyInfo objects.""" for key_and_modifiers in self._iter_keys(): key = Qt.Key(int(key_and_modifiers) & ~Qt.KeyboardModifierMask) - modifiers = Qt.KeyboardModifiers( # type: ignore + modifiers = Qt.KeyboardModifiers( # type: ignore[call-overload] int(key_and_modifiers) & Qt.KeyboardModifierMask) yield KeyInfo(key=key, modifiers=modifiers) @@ -650,7 +652,7 @@ class KeySequence: if (modifiers == Qt.ShiftModifier and _is_printable(key) and not ev.text().isupper()): - modifiers = Qt.KeyboardModifiers() # type: ignore + modifiers = Qt.KeyboardModifiers() # type: ignore[assignment] # On macOS, swap Ctrl and Meta back # WORKAROUND for https://bugreports.qt.io/browse/QTBUG-51293 diff --git a/qutebrowser/keyinput/modeman.py b/qutebrowser/keyinput/modeman.py index e0752d0e3..880b1ec93 100644 --- a/qutebrowser/keyinput/modeman.py +++ b/qutebrowser/keyinput/modeman.py @@ -245,10 +245,11 @@ class ModeManager(QObject): "{}".format(curmode, utils.qualname(parser))) match = parser.handle(event, dry_run=dry_run) - is_non_alnum = ( - event.modifiers() not in [Qt.NoModifier, # type: ignore - Qt.ShiftModifier] or - not event.text().strip()) + has_modifier = event.modifiers() not in [ + Qt.NoModifier, + Qt.ShiftModifier, + ] # type: ignore[comparison-overlap] + is_non_alnum = has_modifier or not event.text().strip() forward_unbound_keys = config.cache['input.forward_unbound_keys'] diff --git a/qutebrowser/mainwindow/mainwindow.py b/qutebrowser/mainwindow/mainwindow.py index be1488d87..27f0bb0c1 100644 --- a/qutebrowser/mainwindow/mainwindow.py +++ b/qutebrowser/mainwindow/mainwindow.py @@ -102,7 +102,7 @@ def raise_window(window, alert=True): window.setWindowState(window.windowState() | Qt.WindowActive) window.raise_() # WORKAROUND for https://bugreports.qt.io/browse/QTBUG-69568 - QCoreApplication.processEvents( # type: ignore + QCoreApplication.processEvents( # type: ignore[call-overload] QEventLoop.ExcludeUserInputEvents | QEventLoop.ExcludeSocketNotifiers) window.activateWindow() @@ -384,7 +384,9 @@ class MainWindow(QWidget): self._command_dispatcher, command_only=True, scope='window', window=self.win_id) - self.tabbed_browser.widget.destroyed.connect( # type: ignore + + widget = self.tabbed_browser.widget + widget.destroyed.connect( # type: ignore[attr-defined] functools.partial(objreg.delete, 'command-dispatcher', scope='window', window=self.win_id)) @@ -491,15 +493,15 @@ class MainWindow(QWidget): mode_manager.left.connect(self.status.on_mode_left) mode_manager.left.connect(self.status.cmd.on_mode_left) mode_manager.left.connect( - message.global_bridge.mode_left) # type: ignore + message.global_bridge.mode_left) # type: ignore[arg-type] # commands normal_parser = mode_manager.parsers[usertypes.KeyMode.normal] normal_parser.keystring_updated.connect( self.status.keystring.setText) - self.status.cmd.got_cmd[str].connect( # type: ignore + self.status.cmd.got_cmd[str].connect( # type: ignore[index] self._commandrunner.run_safely) - self.status.cmd.got_cmd[str, int].connect( # type: ignore + self.status.cmd.got_cmd[str, int].connect( # type: ignore[index] self._commandrunner.run_safely) self.status.cmd.returnPressed.connect( self.tabbed_browser.on_cmd_return_pressed) @@ -584,8 +586,8 @@ class MainWindow(QWidget): if on: self.state_before_fullscreen = self.windowState() self.setWindowState( - Qt.WindowFullScreen | # type: ignore - self.state_before_fullscreen) # type: ignore + Qt.WindowFullScreen | # type: ignore[arg-type] + self.state_before_fullscreen) # type: ignore[operator] elif self.isFullScreen(): self.setWindowState(self.state_before_fullscreen) log.misc.debug('on: {}, state before fullscreen: {}'.format( diff --git a/qutebrowser/mainwindow/prompt.py b/qutebrowser/mainwindow/prompt.py index eac00166b..a0a0ca47a 100644 --- a/qutebrowser/mainwindow/prompt.py +++ b/qutebrowser/mainwindow/prompt.py @@ -192,7 +192,7 @@ class PromptQueue(QObject): if blocking: loop = qtutils.EventLoop() self._loops.append(loop) - loop.destroyed.connect( # type: ignore + loop.destroyed.connect( # type: ignore[attr-defined] lambda: self._loops.remove(loop)) question.completed.connect(loop.quit) question.completed.connect(loop.deleteLater) @@ -757,7 +757,7 @@ class FilenamePrompt(_BasePrompt): selmodel.setCurrentIndex( idx, - QItemSelectionModel.ClearAndSelect | # type: ignore + QItemSelectionModel.ClearAndSelect | # type: ignore[arg-type] QItemSelectionModel.Rows) self._insert_path(idx, clicked=False) @@ -784,7 +784,7 @@ class DownloadFilenamePrompt(FilenamePrompt): def __init__(self, question, parent=None): super().__init__(question, parent) self._file_model.setFilter( - QDir.AllDirs | QDir.Drives | QDir.NoDot) # type: ignore + QDir.AllDirs | QDir.Drives | QDir.NoDot) # type: ignore[arg-type] def accept(self, value=None, save=False): done = super().accept(value, save) @@ -962,5 +962,5 @@ def init(): """Initialize global prompt objects.""" global prompt_queue prompt_queue = PromptQueue() - message.global_bridge.ask_question.connect( # type: ignore + message.global_bridge.ask_question.connect( # type: ignore[call-arg] prompt_queue.ask_question, Qt.DirectConnection) diff --git a/qutebrowser/mainwindow/statusbar/bar.py b/qutebrowser/mainwindow/statusbar/bar.py index e96090d0c..119b16584 100644 --- a/qutebrowser/mainwindow/statusbar/bar.py +++ b/qutebrowser/mainwindow/statusbar/bar.py @@ -21,8 +21,8 @@ import enum import attr -from PyQt5.QtCore import (pyqtSignal, pyqtSlot, pyqtProperty, # type: ignore - Qt, QSize, QTimer) +from PyQt5.QtCore import (pyqtSignal, pyqtSlot, # type: ignore[attr-defined] + pyqtProperty, Qt, QSize, QTimer) from PyQt5.QtWidgets import QWidget, QHBoxLayout, QStackedLayout, QSizePolicy from qutebrowser.browser import browsertab diff --git a/qutebrowser/mainwindow/statusbar/command.py b/qutebrowser/mainwindow/statusbar/command.py index 7ead6936a..ebd9d3921 100644 --- a/qutebrowser/mainwindow/statusbar/command.py +++ b/qutebrowser/mainwindow/statusbar/command.py @@ -72,8 +72,9 @@ class Command(misc.MinimalLineEditMixin, misc.CommandLineEdit): self.setSizePolicy(QSizePolicy.MinimumExpanding, QSizePolicy.Ignored) self.cursorPositionChanged.connect( - self.update_completion) # type: ignore - self.textChanged.connect(self.update_completion) # type: ignore + self.update_completion) # type: ignore[arg-type] + self.textChanged.connect( + self.update_completion) # type: ignore[arg-type] self.textChanged.connect(self.updateGeometry) self.textChanged.connect(self._incremental_search) @@ -148,7 +149,7 @@ class Command(misc.MinimalLineEditMixin, misc.CommandLineEdit): raise cmdutils.CommandError( "Invalid command text '{}'.".format(text)) if run_on_count and count is not None: - self.got_cmd[str, int].emit(text, count) # type: ignore + self.got_cmd[str, int].emit(text, count) # type: ignore[index] else: self.set_cmd_text(text) @@ -198,7 +199,7 @@ class Command(misc.MinimalLineEditMixin, misc.CommandLineEdit): 'cmd accept') if not was_search: - self.got_cmd[str].emit(text[1:]) # type: ignore + self.got_cmd[str].emit(text[1:]) # type: ignore[index] @cmdutils.register(instance='status-command', scope='window') def edit_command(self, run: bool = False) -> None: diff --git a/qutebrowser/mainwindow/statusbar/url.py b/qutebrowser/mainwindow/statusbar/url.py index 65a82fd09..c8300dc97 100644 --- a/qutebrowser/mainwindow/statusbar/url.py +++ b/qutebrowser/mainwindow/statusbar/url.py @@ -21,7 +21,8 @@ import enum -from PyQt5.QtCore import pyqtSlot, pyqtProperty, QUrl # type: ignore +from PyQt5.QtCore import (pyqtSlot, pyqtProperty, # type: ignore[attr-defined] + QUrl) from qutebrowser.mainwindow.statusbar import textbase from qutebrowser.config import stylesheet diff --git a/qutebrowser/mainwindow/tabbedbrowser.py b/qutebrowser/mainwindow/tabbedbrowser.py index b86f3d27f..e94dee831 100644 --- a/qutebrowser/mainwindow/tabbedbrowser.py +++ b/qutebrowser/mainwindow/tabbedbrowser.py @@ -205,7 +205,8 @@ class TabbedBrowser(QWidget): self._tab_insert_idx_right = -1 self.shutting_down = False self.widget.tabCloseRequested.connect(self.on_tab_close_requested) - self.widget.new_tab_requested.connect(self.tabopen) # type: ignore + self.widget.new_tab_requested.connect( + self.tabopen) # type: ignore[arg-type] self.widget.currentChanged.connect(self._on_current_changed) self.cur_fullscreen_requested.connect(self.widget.tabBar().maybe_hide) self.widget.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding) @@ -284,7 +285,7 @@ class TabbedBrowser(QWidget): for i in range(self.widget.count()): widget = self.widget.widget(i) if widget is None: - log.webview.debug( # type: ignore + log.webview.debug( # type: ignore[unreachable] "Got None-widget in tabbedbrowser!") else: widgets.append(widget) @@ -528,7 +529,7 @@ class TabbedBrowser(QWidget): """Close a tab via an index.""" tab = self.widget.widget(idx) if tab is None: - log.webview.debug( # type: ignore + log.webview.debug( # type: ignore[unreachable] "Got invalid tab {} for index {}!".format(tab, idx)) return self.tab_close_prompt_if_pinned( @@ -800,7 +801,7 @@ class TabbedBrowser(QWidget): """Give focus to current tab if command mode was left.""" widget = self.widget.currentWidget() if widget is None: - return # type: ignore + return # type: ignore[unreachable] if mode in [usertypes.KeyMode.command] + modeman.PROMPT_MODES: log.modes.debug("Left status-input mode, focusing {!r}".format( widget)) @@ -817,7 +818,7 @@ class TabbedBrowser(QWidget): return tab = self.widget.widget(idx) if tab is None: - log.webview.debug( # type: ignore + log.webview.debug( # type: ignore[unreachable] "on_current_changed got called with invalid index {}" .format(idx)) return diff --git a/qutebrowser/mainwindow/tabwidget.py b/qutebrowser/mainwindow/tabwidget.py index 59e701142..dd36fa415 100644 --- a/qutebrowser/mainwindow/tabwidget.py +++ b/qutebrowser/mainwindow/tabwidget.py @@ -60,7 +60,8 @@ class TabWidget(QTabWidget): bar = TabBar(win_id, self) self.setStyle(TabBarStyle()) self.setTabBar(bar) - bar.tabCloseRequested.connect(self.tabCloseRequested) # type: ignore + bar.tabCloseRequested.connect( + self.tabCloseRequested) # type: ignore[arg-type] bar.tabMoved.connect(functools.partial( QTimer.singleShot, 0, self.update_tab_titles)) bar.currentChanged.connect(self._on_current_changed) @@ -82,7 +83,7 @@ class TabWidget(QTabWidget): position = config.val.tabs.position selection_behavior = config.val.tabs.select_on_remove self.setTabPosition(position) - tabbar.vertical = position in [ # type: ignore + tabbar.vertical = position in [ # type: ignore[attr-defined] QTabWidget.West, QTabWidget.East] tabbar.setSelectionBehaviorOnRemove(selection_behavior) tabbar.refresh() @@ -164,7 +165,7 @@ class TabWidget(QTabWidget): """Get the tab field data.""" tab = self.widget(idx) if tab is None: - log.misc.debug( # type: ignore + log.misc.debug( # type: ignore[unreachable] "Got None-tab in get_tab_fields!") page_title = self.page_title(idx) @@ -331,7 +332,7 @@ class TabWidget(QTabWidget): """ tab = self.widget(idx) if tab is None: - url = QUrl() # type: ignore + url = QUrl() # type: ignore[unreachable] else: url = tab.url() # It's possible for url to be invalid, but the caller will handle that. diff --git a/qutebrowser/misc/autoupdate.py b/qutebrowser/misc/autoupdate.py index e51ecfb19..4838d55ed 100644 --- a/qutebrowser/misc/autoupdate.py +++ b/qutebrowser/misc/autoupdate.py @@ -55,7 +55,7 @@ class PyPIVersionClient(QObject): self._client = httpclient.HTTPClient(self) else: self._client = client - self._client.error.connect(self.error) # type: ignore + self._client.error.connect(self.error) # type: ignore[arg-type] self._client.success.connect(self.on_client_success) def get_version(self, package='qutebrowser'): diff --git a/qutebrowser/misc/checkpyver.py b/qutebrowser/misc/checkpyver.py index d3829a0bf..8283dd13e 100644 --- a/qutebrowser/misc/checkpyver.py +++ b/qutebrowser/misc/checkpyver.py @@ -30,12 +30,12 @@ try: except ImportError: # pragma: no cover try: # Python2 - from Tkinter import Tk # type: ignore + from Tkinter import Tk # type: ignore[import, no-redef] import tkMessageBox as messagebox # type: ignore # noqa: N813 except ImportError: # Some Python without Tk - Tk = None # type: ignore - messagebox = None # type: ignore + Tk = None # type: ignore[misc, assignment] + messagebox = None # type: ignore[assignment] # First we check the version of Python. This code should run fine with python2 @@ -49,7 +49,7 @@ def check_python_version(): version_str = '.'.join(map(str, sys.version_info[:3])) text = ("At least Python 3.5.2 is required to run qutebrowser, but " + "it's running with " + version_str + ".\n") - if (Tk and # type: ignore + if (Tk and # type: ignore[unreachable] '--no-err-windows' not in sys.argv): # pragma: no cover root = Tk() root.withdraw() diff --git a/qutebrowser/misc/crashdialog.py b/qutebrowser/misc/crashdialog.py index f377a0eae..6bd6ec325 100644 --- a/qutebrowser/misc/crashdialog.py +++ b/qutebrowser/misc/crashdialog.py @@ -532,7 +532,7 @@ class FatalCrashDialog(_CrashDialog): if self._chk_history.isChecked(): try: if history.web_history is None: - history_data = '' # type: ignore + history_data = '' # type: ignore[unreachable] else: history_data = '\n'.join(str(e) for e in history.web_history.get_recent()) @@ -629,7 +629,7 @@ class ReportErrorDialog(QDialog): hbox = QHBoxLayout() hbox.addStretch() btn = QPushButton("Close") - btn.clicked.connect(self.close) # type: ignore + btn.clicked.connect(self.close) # type: ignore[arg-type] hbox.addWidget(btn) vbox.addLayout(hbox) diff --git a/qutebrowser/misc/crashsignal.py b/qutebrowser/misc/crashsignal.py index 165c0b8b7..5d8bd0ff5 100644 --- a/qutebrowser/misc/crashsignal.py +++ b/qutebrowser/misc/crashsignal.py @@ -185,7 +185,7 @@ class CrashHandler(QObject): if sys.__stderr__ is not None: faulthandler.enable(sys.__stderr__) else: - faulthandler.disable() # type: ignore + faulthandler.disable() # type: ignore[unreachable] try: self._crash_log_file.close() os.remove(self._crash_log_file.name) @@ -355,7 +355,7 @@ class SignalHandler(QObject): self._notifier = QSocketNotifier(typing.cast(sip.voidptr, read_fd), QSocketNotifier.Read, self) - self._notifier.activated.connect( # type: ignore + self._notifier.activated.connect( # type: ignore[attr-defined] self.handle_signal_wakeup) self._orig_wakeup_fd = signal.set_wakeup_fd(write_fd) # pylint: enable=import-error,no-member,useless-suppression diff --git a/qutebrowser/misc/earlyinit.py b/qutebrowser/misc/earlyinit.py index 99ddd3701..c02b2f03c 100644 --- a/qutebrowser/misc/earlyinit.py +++ b/qutebrowser/misc/earlyinit.py @@ -38,7 +38,7 @@ import datetime try: import tkinter except ImportError: - tkinter = None # type: ignore + tkinter = None # type: ignore[assignment] # NOTE: No qutebrowser or PyQt import should be done here, as some early # initialization needs to take place before that! @@ -251,7 +251,8 @@ def configure_pyqt(): from PyQt5 import QtCore QtCore.pyqtRemoveInputHook() try: - QtCore.pyqt5_enable_new_onexit_scheme(True) # type: ignore + QtCore.pyqt5_enable_new_onexit_scheme( # type: ignore[attr-defined] + True) except AttributeError: # Added in PyQt 5.13 somewhere, going to be the default in 5.14 pass @@ -259,7 +260,7 @@ def configure_pyqt(): from qutebrowser.qt import sip try: # Added in sip 4.19.4 - sip.enableoverflowchecking(True) # type: ignore + sip.enableoverflowchecking(True) # type: ignore[attr-defined] except AttributeError: pass diff --git a/qutebrowser/misc/editor.py b/qutebrowser/misc/editor.py index 1dcc3a532..5ef451e4e 100644 --- a/qutebrowser/misc/editor.py +++ b/qutebrowser/misc/editor.py @@ -187,7 +187,7 @@ class ExternalEditor(QObject): if not ok: log.procs.error("Failed to watch path: {}" .format(self._filename)) - self._watcher.fileChanged.connect( # type: ignore + self._watcher.fileChanged.connect( # type: ignore[attr-defined] self._on_file_changed) args = [self._sub_placeholder(arg, line, column) for arg in editor[1:]] diff --git a/qutebrowser/misc/guiprocess.py b/qutebrowser/misc/guiprocess.py index 49b26e9d3..3702715c4 100644 --- a/qutebrowser/misc/guiprocess.py +++ b/qutebrowser/misc/guiprocess.py @@ -63,11 +63,11 @@ class GUIProcess(QObject): self._proc = QProcess(self) self._proc.errorOccurred.connect(self._on_error) - self._proc.errorOccurred.connect(self.error) # type: ignore + self._proc.errorOccurred.connect(self.error) # type: ignore[arg-type] self._proc.finished.connect(self._on_finished) - self._proc.finished.connect(self.finished) # type: ignore + self._proc.finished.connect(self.finished) # type: ignore[arg-type] self._proc.started.connect(self._on_started) - self._proc.started.connect(self.started) # type: ignore + self._proc.started.connect(self.started) # type: ignore[arg-type] if additional_env is not None: procenv = QProcessEnvironment.systemEnvironment() @@ -163,7 +163,8 @@ class GUIProcess(QObject): """Convenience wrapper around QProcess::startDetached.""" log.procs.debug("Starting detached.") self._pre_start(cmd, args) - ok, _pid = self._proc.startDetached(cmd, args, None) # type: ignore + ok, _pid = self._proc.startDetached( + cmd, args, None) # type: ignore[call-arg] if not ok: message.error("Error while spawning {}".format(self._what)) diff --git a/qutebrowser/misc/ipc.py b/qutebrowser/misc/ipc.py index ad1d3eb56..2cceb2df1 100644 --- a/qutebrowser/misc/ipc.py +++ b/qutebrowser/misc/ipc.py @@ -178,7 +178,7 @@ class IPCServer(QObject): self._atime_timer.setTimerType(Qt.VeryCoarseTimer) self._server = QLocalServer(self) - self._server.newConnection.connect( # type: ignore + self._server.newConnection.connect( # type: ignore[attr-defined] self.handle_connection) self._socket = None @@ -252,21 +252,24 @@ class IPCServer(QObject): return socket = self._server.nextPendingConnection() if socket is None: - log.ipc.debug("No new connection to handle.") # type: ignore + log.ipc.debug( # type: ignore[unreachable] + "No new connection to handle.") return log.ipc.debug("Client connected (socket 0x{:x}).".format(id(socket))) self._timer.start() self._socket = socket - socket.readyRead.connect(self.on_ready_read) # type: ignore + socket.readyRead.connect( # type: ignore[attr-defined] + self.on_ready_read) if socket.canReadLine(): log.ipc.debug("We can read a line immediately.") self.on_ready_read() - socket.error.connect(self.on_error) # type: ignore + socket.error.connect(self.on_error) # type: ignore[attr-defined] if socket.error() not in [QLocalSocket.UnknownSocketError, QLocalSocket.PeerClosedError]: log.ipc.debug("We got an error immediately.") self.on_error(socket.error()) - socket.disconnected.connect(self.on_disconnected) # type: ignore + socket.disconnected.connect( # type: ignore[attr-defined] + self.on_disconnected) if socket.state() == QLocalSocket.UnconnectedState: log.ipc.debug("Socket was disconnected immediately.") self.on_disconnected() diff --git a/qutebrowser/misc/miscwidgets.py b/qutebrowser/misc/miscwidgets.py index b43a99fec..2d72af780 100644 --- a/qutebrowser/misc/miscwidgets.py +++ b/qutebrowser/misc/miscwidgets.py @@ -37,7 +37,7 @@ class MinimalLineEditMixin: """A mixin to give a QLineEdit a minimal look and nicer repr().""" def __init__(self): - self.setStyleSheet( # type: ignore + self.setStyleSheet( # type: ignore[attr-defined] """ QLineEdit { border: 0px; @@ -46,7 +46,8 @@ class MinimalLineEditMixin: } """ ) - self.setAttribute(Qt.WA_MacShowFocusRect, False) # type: ignore + self.setAttribute( # type: ignore[attr-defined] + Qt.WA_MacShowFocusRect, False) def keyPressEvent(self, e): """Override keyPressEvent to paste primary selection on Shift + Ins.""" @@ -57,9 +58,9 @@ class MinimalLineEditMixin: e.ignore() else: e.accept() - self.insert(text) # type: ignore + self.insert(text) # type: ignore[attr-defined] return - super().keyPressEvent(e) # type: ignore + super().keyPressEvent(e) # type: ignore[misc] def __repr__(self): return utils.get_repr(self) @@ -260,7 +261,7 @@ class WrapperLayout(QLayout): widget.setParent(container) def unwrap(self): - self._widget.setParent(None) # type: ignore + self._widget.setParent(None) # type: ignore[call-overload] self._widget.deleteLater() diff --git a/qutebrowser/misc/sessions.py b/qutebrowser/misc/sessions.py index 1f3b59536..95d73125d 100644 --- a/qutebrowser/misc/sessions.py +++ b/qutebrowser/misc/sessions.py @@ -323,7 +323,8 @@ class SessionManager(QObject): else: data = self._save_all(only_window=only_window, with_private=with_private) - log.sessions.vdebug("Saving data: {}".format(data)) # type: ignore + log.sessions.vdebug( # type: ignore[attr-defined] + "Saving data: {}".format(data)) try: with qtutils.savefile_open(path) as f: utils.yaml_dump(data, f) diff --git a/qutebrowser/misc/split.py b/qutebrowser/misc/split.py index dc46a28d7..b08d34cae 100644 --- a/qutebrowser/misc/split.py +++ b/qutebrowser/misc/split.py @@ -138,7 +138,8 @@ def split(s, keep=False): out = [] spaces = "" - log.shlexer.vdebug("{!r} -> {!r}".format(s, tokens)) # type: ignore + log.shlexer.vdebug( # type: ignore[attr-defined] + "{!r} -> {!r}".format(s, tokens)) for t in tokens: if t.isspace(): diff --git a/qutebrowser/misc/sql.py b/qutebrowser/misc/sql.py index 39208819d..8f9265419 100644 --- a/qutebrowser/misc/sql.py +++ b/qutebrowser/misc/sql.py @@ -190,7 +190,8 @@ class Query: raise BugError("Cannot iterate inactive query") rec = self.query.record() fields = [rec.fieldName(i) for i in range(rec.count())] - rowtype = collections.namedtuple('ResultRow', fields) # type: ignore + rowtype = collections.namedtuple( # type: ignore[misc] + 'ResultRow', fields) while self.query.next(): rec = self.query.record() diff --git a/qutebrowser/qt.py b/qutebrowser/qt.py index cc6197982..5b44530bb 100644 --- a/qutebrowser/qt.py +++ b/qutebrowser/qt.py @@ -25,4 +25,4 @@ try: from PyQt5 import sip except ImportError: - import sip # type: ignore + import sip # type: ignore[import, no-redef] diff --git a/qutebrowser/utils/debug.py b/qutebrowser/utils/debug.py index fa5664edc..66cfeed9e 100644 --- a/qutebrowser/utils/debug.py +++ b/qutebrowser/utils/debug.py @@ -80,7 +80,7 @@ def log_signals(obj: QObject) -> QObject: pass if inspect.isclass(obj): - old_init = obj.__init__ # type: ignore + old_init = obj.__init__ # type: ignore[misc] @functools.wraps(old_init) def new_init(self: typing.Any, @@ -90,7 +90,7 @@ def log_signals(obj: QObject) -> QObject: old_init(self, *args, **kwargs) connect_log_slot(self) - obj.__init__ = new_init # type: ignore + obj.__init__ = new_init # type: ignore[misc] else: connect_log_slot(obj) @@ -122,7 +122,7 @@ def qenum_key(base: typing.Type, try: idx = base.staticMetaObject.indexOfEnumerator(klass.__name__) meta_enum = base.staticMetaObject.enumerator(idx) - ret = meta_enum.valueToKey(int(value)) # type: ignore + ret = meta_enum.valueToKey(int(value)) # type: ignore[arg-type] except AttributeError: ret = None @@ -132,7 +132,7 @@ def qenum_key(base: typing.Type, ret = name break else: - ret = '0x{:04x}'.format(int(value)) # type: ignore + ret = '0x{:04x}'.format(int(value)) # type: ignore[arg-type] if add_base and hasattr(base, '__name__'): return '.'.join([base.__name__, ret]) @@ -175,7 +175,7 @@ def qflags_key(base: typing.Type, bits = [] names = [] mask = 0x01 - value = int(value) # type: ignore + value = int(value) # type: ignore[arg-type] while mask <= value: if value & mask: bits.append(mask) @@ -183,7 +183,8 @@ def qflags_key(base: typing.Type, for bit in bits: # We have to re-convert to an enum type here or we'll sometimes get an # empty string back. - names.append(qenum_key(base, klass(bit), add_base)) # type: ignore + enum_value = klass(bit) # type: ignore[call-arg] + names.append(qenum_key(base, enum_value, add_base)) return '|'.join(names) @@ -209,14 +210,14 @@ def signal_name(sig: pyqtSignal) -> str: # sig.signal == '2signal1' # sig.signal == '2signal2(QString,QString)' m = re.fullmatch(r'[0-9]+(?P.*)\(.*\)', - sig.signal) # type: ignore + sig.signal) # type: ignore[attr-defined] elif hasattr(sig, 'signatures'): # Unbound signal, PyQt >= 5.11 # Examples: # sig.signatures == ('signal1()',) # sig.signatures == ('signal2(QString,QString)',) m = re.fullmatch(r'(?P.*)\(.*\)', - sig.signatures[0]) # type: ignore + sig.signatures[0]) # type: ignore[attr-defined] else: # pragma: no cover # Unbound signal, PyQt < 5.11 # Examples: diff --git a/qutebrowser/utils/jinja.py b/qutebrowser/utils/jinja.py index a7bb1f686..78663645d 100644 --- a/qutebrowser/utils/jinja.py +++ b/qutebrowser/utils/jinja.py @@ -112,7 +112,7 @@ class Environment(jinja2.Environment): """ image = utils.resource_filename(path) url = QUrl.fromLocalFile(image) - urlstr = url.toString(QUrl.FullyEncoded) # type: ignore + urlstr = url.toString(QUrl.FullyEncoded) # type: ignore[arg-type] return urlstr def _data_url(self, path: str) -> str: @@ -156,11 +156,11 @@ def template_config_variables(template: str) -> typing.FrozenSet[str]: # For example it's ['ab', 'c', 'd'] for 'conf.d.c.ab'. attrlist = [] # type: typing.List[str] while isinstance(node, jinja2.nodes.Getattr): - attrlist.append(node.attr) # type: ignore - node = node.node # type: ignore + attrlist.append(node.attr) # type: ignore[attr-defined] + node = node.node # type: ignore[attr-defined] if isinstance(node, jinja2.nodes.Name): - if node.name == 'conf': # type: ignore + if node.name == 'conf': # type: ignore[attr-defined] result.add('.'.join(reversed(attrlist))) # otherwise, the node is a Name node so it doesn't have any # child nodes diff --git a/qutebrowser/utils/log.py b/qutebrowser/utils/log.py index df74fd333..2718f10ba 100644 --- a/qutebrowser/utils/log.py +++ b/qutebrowser/utils/log.py @@ -81,10 +81,10 @@ LOG_COLORS = { # mypy doesn't know about this, so we need to ignore it. VDEBUG_LEVEL = 9 logging.addLevelName(VDEBUG_LEVEL, 'VDEBUG') -logging.VDEBUG = VDEBUG_LEVEL # type: ignore +logging.VDEBUG = VDEBUG_LEVEL # type: ignore[attr-defined] LOG_LEVELS = { - 'VDEBUG': logging.VDEBUG, # type: ignore + 'VDEBUG': logging.VDEBUG, # type: ignore[attr-defined] 'DEBUG': logging.DEBUG, 'INFO': logging.INFO, 'WARNING': logging.WARNING, @@ -109,7 +109,7 @@ def vdebug(self: logging.Logger, # pylint: enable=protected-access -logging.Logger.vdebug = vdebug # type: ignore +logging.Logger.vdebug = vdebug # type: ignore[attr-defined] # The different loggers used. @@ -278,7 +278,7 @@ def _init_handlers( level, color, force_color, json_logging) if sys.stderr is None: - console_handler = None # type: ignore + console_handler = None # type: ignore[unreachable] else: strip = False if force_color else None if use_colorama: @@ -337,14 +337,17 @@ def _init_formatters( use_colors=False) html_formatter = HTMLFormatter(EXTENDED_FMT_HTML, DATEFMT, log_colors=LOG_COLORS) + + use_colorama = False + if sys.stderr is None: - return None, ram_formatter, html_formatter, False # type: ignore + console_formatter = None # type: ignore[unreachable] + return console_formatter, ram_formatter, html_formatter, use_colorama if json_logging: json_formatter = JSONFormatter() - return json_formatter, ram_formatter, html_formatter, False + return json_formatter, ram_formatter, html_formatter, use_colorama - use_colorama = False color_supported = os.name == 'posix' or colorama if color_supported and (sys.stderr.isatty() or force_color) and color: @@ -481,13 +484,13 @@ def qt_message_handler(msg_type: QtCore.QtMsgType, level = qt_to_logging[msg_type] if context.function is None: - func = 'none' # type: ignore + func = 'none' # type: ignore[unreachable] elif ':' in context.function: func = '"{}"'.format(context.function) else: func = context.function - if (context.category is None or # type: ignore + if (context.category is None or # type: ignore[unreachable] context.category == 'default'): name = 'qt' else: @@ -692,9 +695,10 @@ class HTMLFormatter(logging.Formatter): record_clone.__dict__.update(self._colordict) if record_clone.levelname in self._log_colors: color = self._log_colors[record_clone.levelname] - record_clone.log_color = self._colordict[color] # type: ignore + color_str = self._colordict[color] + record_clone.log_color = color_str # type: ignore[attr-defined] else: - record_clone.log_color = '' # type: ignore + record_clone.log_color = '' # type: ignore[attr-defined] for field in ['msg', 'filename', 'funcName', 'levelname', 'module', 'name', 'pathname', 'processName', 'threadName']: data = str(getattr(record_clone, field)) diff --git a/qutebrowser/utils/objreg.py b/qutebrowser/utils/objreg.py index 037d2cdc5..3de0cbcd3 100644 --- a/qutebrowser/utils/objreg.py +++ b/qutebrowser/utils/objreg.py @@ -86,7 +86,7 @@ class ObjectRegistry(collections.UserDict): if isinstance(obj, QObject): func = functools.partial(self.on_destroyed, name) - obj.destroyed.connect(func) # type: ignore + obj.destroyed.connect(func) # type: ignore[attr-defined] self._partial_objs[name] = func super().__setitem__(name, obj) diff --git a/qutebrowser/utils/qtutils.py b/qutebrowser/utils/qtutils.py index 8bfdec7db..3e8afae3f 100644 --- a/qutebrowser/utils/qtutils.py +++ b/qutebrowser/utils/qtutils.py @@ -42,7 +42,7 @@ from PyQt5.QtWidgets import QApplication try: from PyQt5.QtWebKit import qWebKitVersion except ImportError: # pragma: no cover - qWebKitVersion = None # type: ignore # noqa: N816 + qWebKitVersion = None # type: ignore[assignment] # noqa: N816 if typing.TYPE_CHECKING: from PyQt5.QtCore import QFileDevice @@ -203,7 +203,7 @@ def serialize_stream(stream: QDataStream, obj: _QtSerializableType) -> None: """Serialize an object into a QDataStream.""" # pylint: disable=pointless-statement check_qdatastream(stream) - stream << obj # type: ignore + stream << obj # type: ignore[operator] check_qdatastream(stream) @@ -211,7 +211,7 @@ def deserialize_stream(stream: QDataStream, obj: _QtSerializableType) -> None: """Deserialize a QDataStream into an object.""" # pylint: disable=pointless-statement check_qdatastream(stream) - stream >> obj # type: ignore + stream >> obj # type: ignore[operator] check_qdatastream(stream) @@ -426,7 +426,7 @@ class QtValueError(ValueError): def __init__(self, obj: 'Validatable') -> None: try: - self.reason = obj.errorString() # type: ignore + self.reason = obj.errorString() # type: ignore[attr-defined] except AttributeError: self.reason = None err = "{} is not valid".format(obj) diff --git a/qutebrowser/utils/standarddir.py b/qutebrowser/utils/standarddir.py index a122f4f80..8e5a91c30 100644 --- a/qutebrowser/utils/standarddir.py +++ b/qutebrowser/utils/standarddir.py @@ -68,7 +68,7 @@ def _unset_organization() -> typing.Iterator[None]: qapp = QApplication.instance() if qapp is not None: orgname = qapp.organizationName() - qapp.setOrganizationName(None) # type: ignore + qapp.setOrganizationName(None) # type: ignore[arg-type] try: yield finally: diff --git a/qutebrowser/utils/urlutils.py b/qutebrowser/utils/urlutils.py index e687c9c6a..7c8cec7a5 100644 --- a/qutebrowser/utils/urlutils.py +++ b/qutebrowser/utils/urlutils.py @@ -125,9 +125,9 @@ def _get_search_url(txt: str) -> QUrl: url = qurl_from_user_input(evaluated) else: url = qurl_from_user_input(config.val.url.searchengines[engine]) - url.setPath(None) # type: ignore - url.setFragment(None) # type: ignore - url.setQuery(None) # type: ignore + url.setPath(None) # type: ignore[arg-type] + url.setFragment(None) # type: ignore[arg-type] + url.setQuery(None) # type: ignore[call-overload] qtutils.ensure_valid(url) return url @@ -522,14 +522,14 @@ def encoded_url(url: QUrl) -> str: return url.toEncoded().data().decode('ascii') -def file_url(path: str) -> QUrl: +def file_url(path: str) -> str: """Return a file:// url (as string) to the given local path. Arguments: path: The absolute path to the local file """ url = QUrl.fromLocalFile(path) - return url.toString(QUrl.FullyEncoded) # type: ignore + return url.toString(QUrl.FullyEncoded) # type: ignore[arg-type] def data_url(mimetype: str, data: bytes) -> QUrl: diff --git a/qutebrowser/utils/usertypes.py b/qutebrowser/utils/usertypes.py index 3df2593ad..247946497 100644 --- a/qutebrowser/utils/usertypes.py +++ b/qutebrowser/utils/usertypes.py @@ -91,7 +91,7 @@ class NeighborList(typing.Sequence[_T]): self._mode = mode self.fuzzyval = None # type: typing.Optional[int] - def __getitem__(self, key: int) -> _T: # type: ignore + def __getitem__(self, key: int) -> _T: # type: ignore[override] return self._items[key] def __len__(self) -> int: @@ -120,7 +120,8 @@ class NeighborList(typing.Sequence[_T]): if items: item = min( items, - key=lambda tpl: abs(self.fuzzyval - tpl[1])) # type: ignore + key=lambda tpl: + abs(self.fuzzyval - tpl[1])) # type: ignore[operator] else: sorted_items = sorted(enumerate(self.items), key=lambda e: e[1]) idx = 0 if offset < 0 else -1 diff --git a/qutebrowser/utils/utils.py b/qutebrowser/utils/utils.py index 58e89c2a1..368cb0ab6 100644 --- a/qutebrowser/utils/utils.py +++ b/qutebrowser/utils/utils.py @@ -47,7 +47,7 @@ try: CSafeDumper as YamlDumper) YAML_C_EXT = True except ImportError: # pragma: no cover - from yaml import (SafeLoader as YamlLoader, # type: ignore + from yaml import (SafeLoader as YamlLoader, # type: ignore[misc] SafeDumper as YamlDumper) YAML_C_EXT = False @@ -324,7 +324,7 @@ class FakeIOStream(io.TextIOBase): def __init__(self, write_func: typing.Callable[[str], int]) -> None: super().__init__() - self.write = write_func # type: ignore + self.write = write_func # type: ignore[assignment] @contextlib.contextmanager @@ -338,16 +338,16 @@ def fake_io(write_func: typing.Callable[[str], int]) -> typing.Iterator[None]: old_stderr = sys.stderr fake_stderr = FakeIOStream(write_func) fake_stdout = FakeIOStream(write_func) - sys.stderr = fake_stderr # type: ignore - sys.stdout = fake_stdout # type: ignore + sys.stderr = fake_stderr # type: ignore[assignment] + sys.stdout = fake_stdout # type: ignore[assignment] try: yield finally: # If the code we did run did change sys.stdout/sys.stderr, we leave it # unchanged. Otherwise, we reset it. - if sys.stdout is fake_stdout: # type: ignore + if sys.stdout is fake_stdout: # type: ignore[comparison-overlap] sys.stdout = old_stdout - if sys.stderr is fake_stderr: # type: ignore + if sys.stderr is fake_stderr: # type: ignore[comparison-overlap] sys.stderr = old_stderr diff --git a/qutebrowser/utils/version.py b/qutebrowser/utils/version.py index 52d29ee4b..f6e7167be 100644 --- a/qutebrowser/utils/version.py +++ b/qutebrowser/utils/version.py @@ -43,7 +43,7 @@ from PyQt5.QtWidgets import QApplication try: from PyQt5.QtWebKit import qWebKitVersion except ImportError: # pragma: no cover - qWebKitVersion = None # type: ignore # noqa: N816 + qWebKitVersion = None # type: ignore[assignment] # noqa: N816 import qutebrowser from qutebrowser.utils import log, utils, standarddir, usertypes, message @@ -54,7 +54,7 @@ from qutebrowser.config import config try: from qutebrowser.browser.webengine import webenginesettings except ImportError: # pragma: no cover - webenginesettings = None # type: ignore + webenginesettings = None # type: ignore[assignment] @attr.s @@ -373,7 +373,7 @@ def _chromium_version() -> str: and https://chromereleases.googleblog.com/ """ if webenginesettings is None: - return 'unavailable' # type: ignore + return 'unavailable' # type: ignore[unreachable] if webenginesettings.parsed_user_agent is None: webenginesettings.init_user_agent() diff --git a/tests/conftest.py b/tests/conftest.py index f5e1fd1bb..c6b6c2efc 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -110,7 +110,7 @@ def _apply_platform_markers(config, item): "https://bugreports.qt.io/browse/QTBUG-60673"), ('qtwebkit6021_xfail', pytest.mark.xfail, - version.qWebKitVersion and # type: ignore + version.qWebKitVersion and # type: ignore[unreachable] version.qWebKitVersion() == '602.1', "Broken on WebKit 602.1") ] From b737874c52776be26b8aebe648e904e856673592 Mon Sep 17 00:00:00 2001 From: Florian Bruhin Date: Sun, 10 May 2020 19:16:49 +0200 Subject: [PATCH 28/34] mypy: Reorganize --strict flags --- mypy.ini | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/mypy.ini b/mypy.ini index 5d404f4b2..33b6556db 100644 --- a/mypy.ini +++ b/mypy.ini @@ -4,23 +4,24 @@ python_version = 3.6 # --strict -warn_redundant_casts = True -warn_unused_ignores = True +warn_unused_configs = True +# disallow_any_generics = True disallow_subclassing_any = True -disallow_untyped_decorators = True -## https://github.com/python/mypy/issues/5957 -# warn_unused_configs = True # disallow_untyped_calls = True # disallow_untyped_defs = True ## https://github.com/python/mypy/issues/5954 # disallow_incomplete_defs = True check_untyped_defs = True +disallow_untyped_decorators = True # no_implicit_optional = True +warn_redundant_casts = True +warn_unused_ignores = True # warn_return_any = True -warn_unreachable = True +# no_implicit_reexport = True +strict_equality = True # Other strictness flags -strict_equality = True +warn_unreachable = True # Output show_error_codes = True From 3a63db5e4889557e80086203c0af2dded1bbf045 Mon Sep 17 00:00:00 2001 From: Florian Bruhin Date: Sun, 10 May 2020 19:17:31 +0200 Subject: [PATCH 29/34] mypy: Remove unused config sections --- mypy.ini | 8 -------- 1 file changed, 8 deletions(-) diff --git a/mypy.ini b/mypy.ini index 33b6556db..7ed04e37e 100644 --- a/mypy.ini +++ b/mypy.ini @@ -92,14 +92,6 @@ disallow_incomplete_defs = True disallow_untyped_defs = True disallow_incomplete_defs = True -[mypy-qutebrowser.misc.eventfilter] -disallow_untyped_defs = True -disallow_incomplete_defs = True - -[mypy-qutebrowser.commands.cmdutils] -disallow_untyped_defs = True -disallow_incomplete_defs = True - [mypy-qutebrowser.config.*] disallow_untyped_defs = True disallow_incomplete_defs = True From e25a96de948f96365e87c6f53bba0753abd17711 Mon Sep 17 00:00:00 2001 From: Florian Bruhin Date: Sun, 10 May 2020 19:30:25 +0200 Subject: [PATCH 30/34] mypy: Improve typing for qutescheme --- qutebrowser/browser/qutescheme.py | 54 +++++++++++++++++-------------- 1 file changed, 29 insertions(+), 25 deletions(-) diff --git a/qutebrowser/browser/qutescheme.py b/qutebrowser/browser/qutescheme.py index d40dc4530..53004e66a 100644 --- a/qutebrowser/browser/qutescheme.py +++ b/qutebrowser/browser/qutescheme.py @@ -98,8 +98,8 @@ class Redirect(Exception): # Return value: (mimetype, data) (encoded as utf-8 if a str is returned) -_Handler = TypeVar('_Handler', - bound=Callable[[QUrl], Tuple[str, Union[str, bytes]]]) +_HandlerRet = Tuple[str, Union[str, bytes]] +_Handler = TypeVar('_Handler', bound=Callable[[QUrl], _HandlerRet]) class add_handler: # noqa: N801,N806 pylint: disable=invalid-name @@ -125,7 +125,7 @@ class add_handler: # noqa: N801,N806 pylint: disable=invalid-name return self._function(*args, **kwargs) -def data_for_url(url): +def data_for_url(url: QUrl) -> typing.Tuple[str, bytes]: """Get the data to show for the given URL. Args: @@ -134,8 +134,9 @@ def data_for_url(url): Return: A (mimetype, data) tuple. """ - norm_url = url.adjusted(QUrl.NormalizePathSegments | - QUrl.StripTrailingSlash) + norm_url = url.adjusted( + QUrl.NormalizePathSegments | # type: ignore[arg-type] + QUrl.StripTrailingSlash) if norm_url != url: raise Redirect(norm_url) @@ -181,7 +182,7 @@ def data_for_url(url): @add_handler('bookmarks') -def qute_bookmarks(_url): +def qute_bookmarks(_url: QUrl) -> _HandlerRet: """Handler for qute://bookmarks. Display all quickmarks / bookmarks.""" bookmarks = sorted(objreg.get('bookmark-manager').marks.items(), key=lambda x: x[1]) # Sort by title @@ -196,7 +197,7 @@ def qute_bookmarks(_url): @add_handler('tabs') -def qute_tabs(_url): +def qute_tabs(_url: QUrl) -> _HandlerRet: """Handler for qute://tabs. Display information about all open tabs.""" tabs = collections.defaultdict( list) # type: typing.Dict[str, typing.List[typing.Tuple[str, str]]] @@ -217,7 +218,10 @@ def qute_tabs(_url): return 'text/html', src -def history_data(start_time, offset=None): +def history_data( + start_time: float, + offset: int = None +) -> typing.Sequence[typing.Dict[str, typing.Union[str, int]]]: """Return history data. Arguments: @@ -240,7 +244,7 @@ def history_data(start_time, offset=None): @add_handler('history') -def qute_history(url): +def qute_history(url: QUrl) -> _HandlerRet: """Handler for qute://history. Display and serve history.""" if url.path() == '/data': q_offset = QUrlQuery(url).queryItemValue("offset") @@ -266,7 +270,7 @@ def qute_history(url): @add_handler('javascript') -def qute_javascript(url): +def qute_javascript(url: QUrl) -> _HandlerRet: """Handler for qute://javascript. Return content of file given as query parameter. @@ -280,14 +284,14 @@ def qute_javascript(url): @add_handler('pyeval') -def qute_pyeval(_url): +def qute_pyeval(_url: QUrl) -> _HandlerRet: """Handler for qute://pyeval.""" src = jinja.render('pre.html', title='pyeval', content=pyeval_output) return 'text/html', src @add_handler('spawn-output') -def qute_spawn_output(_url): +def qute_spawn_output(_url: QUrl) -> _HandlerRet: """Handler for qute://spawn-output.""" src = jinja.render('pre.html', title='spawn output', content=spawn_output) return 'text/html', src @@ -304,7 +308,7 @@ def qute_version(_url): @add_handler('plainlog') -def qute_plainlog(url): +def qute_plainlog(url: QUrl) -> _HandlerRet: """Handler for qute://plainlog. An optional query parameter specifies the minimum log level to print. @@ -323,7 +327,7 @@ def qute_plainlog(url): @add_handler('log') -def qute_log(url): +def qute_log(url: QUrl) -> _HandlerRet: """Handler for qute://log. An optional query parameter specifies the minimum log level to print. @@ -343,12 +347,12 @@ def qute_log(url): @add_handler('gpl') -def qute_gpl(_url): +def qute_gpl(_url: QUrl) -> _HandlerRet: """Handler for qute://gpl. Return HTML content as string.""" return 'text/html', utils.read_file('html/license.html') -def _asciidoc_fallback_path(html_path): +def _asciidoc_fallback_path(html_path: str) -> typing.Optional[str]: """Fall back to plaintext asciidoc if the HTML is unavailable.""" path = html_path.replace('.html', '.asciidoc') try: @@ -358,7 +362,7 @@ def _asciidoc_fallback_path(html_path): @add_handler('help') -def qute_help(url): +def qute_help(url: QUrl) -> _HandlerRet: """Handler for qute://help.""" urlpath = url.path() if not urlpath or urlpath == '/': @@ -407,7 +411,7 @@ def qute_help(url): return 'text/html', data -def _qute_settings_set(url): +def _qute_settings_set(url: QUrl) -> _HandlerRet: """Handler for qute://settings/set.""" query = QUrlQuery(url) option = query.queryItemValue('option', QUrl.FullyDecoded) @@ -429,7 +433,7 @@ def _qute_settings_set(url): @add_handler('settings') -def qute_settings(url): +def qute_settings(url: QUrl) -> _HandlerRet: """Handler for qute://settings. View/change qute configuration.""" global csrf_token @@ -457,7 +461,7 @@ def qute_settings(url): @add_handler('bindings') -def qute_bindings(_url): +def qute_bindings(_url: QUrl) -> _HandlerRet: """Handler for qute://bindings. View keybindings.""" bindings = {} defaults = config.val.bindings.default @@ -475,7 +479,7 @@ def qute_bindings(_url): @add_handler('back') -def qute_back(url): +def qute_back(url: QUrl) -> _HandlerRet: """Handler for qute://back. Simple page to free ram / lazy load a site, goes back on focusing the tab. @@ -487,7 +491,7 @@ def qute_back(url): @add_handler('configdiff') -def qute_configdiff(url): +def qute_configdiff(url: QUrl) -> _HandlerRet: """Handler for qute://configdiff.""" if url.path() == '/old': try: @@ -502,7 +506,7 @@ def qute_configdiff(url): @add_handler('pastebin-version') -def qute_pastebin_version(_url): +def qute_pastebin_version(_url: QUrl) -> _HandlerRet: """Handler that pastebins the version string.""" version.pastebin_version() return 'text/plain', b'Paste called.' @@ -515,7 +519,7 @@ def _pdf_path(filename: str) -> str: @add_handler('pdfjs') -def qute_pdfjs(url: QUrl): +def qute_pdfjs(url: QUrl) -> _HandlerRet: """Handler for qute://pdfjs. Return the pdf.js viewer or redirect to original URL if the file does not @@ -566,7 +570,7 @@ def qute_pdfjs(url: QUrl): @add_handler('warning') -def qute_warning(url): +def qute_warning(url: QUrl) -> _HandlerRet: """Handler for qute://warning.""" path = url.path() if path == '/old-qt': From 3679d3a05b273cbac206c52632ae0c369e7cccf2 Mon Sep 17 00:00:00 2001 From: Florian Bruhin Date: Sun, 10 May 2020 19:32:38 +0200 Subject: [PATCH 31/34] mypy: Disallow incomplete defs globally --- mypy.ini | 20 +------------------ qutebrowser/browser/commands.py | 2 +- qutebrowser/browser/downloads.py | 2 +- qutebrowser/browser/downloadview.py | 5 ++++- .../browser/webengine/webenginedownloads.py | 5 +++-- qutebrowser/commands/runners.py | 2 +- qutebrowser/mainwindow/tabbedbrowser.py | 2 +- qutebrowser/mainwindow/tabwidget.py | 2 +- qutebrowser/misc/sessions.py | 2 +- 9 files changed, 14 insertions(+), 28 deletions(-) diff --git a/mypy.ini b/mypy.ini index 7ed04e37e..98150f002 100644 --- a/mypy.ini +++ b/mypy.ini @@ -9,8 +9,7 @@ warn_unused_configs = True disallow_subclassing_any = True # disallow_untyped_calls = True # disallow_untyped_defs = True -## https://github.com/python/mypy/issues/5954 -# disallow_incomplete_defs = True +disallow_incomplete_defs = True check_untyped_defs = True disallow_untyped_decorators = True # no_implicit_optional = True @@ -66,68 +65,51 @@ disallow_subclassing_any = False [mypy-qutebrowser.browser.browsertab] disallow_untyped_defs = True -disallow_incomplete_defs = True [mypy-qutebrowser.browser.hints] disallow_untyped_defs = True -disallow_incomplete_defs = True [mypy-qutebrowser.misc.objects] disallow_untyped_defs = True -disallow_incomplete_defs = True [mypy-qutebrowser.misc.debugcachestats] disallow_untyped_defs = True -disallow_incomplete_defs = True [mypy-qutebrowser.misc.utilcmds] disallow_untyped_defs = True -disallow_incomplete_defs = True [mypy-qutebrowser.misc.throttle] disallow_untyped_defs = True -disallow_incomplete_defs = True [mypy-qutebrowser.misc.backendproblem] disallow_untyped_defs = True -disallow_incomplete_defs = True [mypy-qutebrowser.config.*] disallow_untyped_defs = True -disallow_incomplete_defs = True [mypy-qutebrowser.api.*] disallow_untyped_defs = True -disallow_incomplete_defs = True [mypy-qutebrowser.components.*] disallow_untyped_defs = True -disallow_incomplete_defs = True [mypy-qutebrowser.extensions.*] disallow_untyped_defs = True -disallow_incomplete_defs = True [mypy-qutebrowser.browser.webelem] disallow_untyped_defs = True -disallow_incomplete_defs = True [mypy-qutebrowser.browser.webkit.webkitelem] disallow_untyped_defs = True -disallow_incomplete_defs = True [mypy-qutebrowser.browser.webengine.webengineelem] disallow_untyped_defs = True -disallow_incomplete_defs = True [mypy-qutebrowser.keyinput.*] disallow_untyped_defs = True -disallow_incomplete_defs = True [mypy-qutebrowser.utils.*] disallow_untyped_defs = True -disallow_incomplete_defs = True [mypy-qutebrowser.mainwindow.statusbar.command] disallow_untyped_defs = True -disallow_incomplete_defs = True diff --git a/qutebrowser/browser/commands.py b/qutebrowser/browser/commands.py index 08eb98fd2..6628d6f21 100644 --- a/qutebrowser/browser/commands.py +++ b/qutebrowser/browser/commands.py @@ -157,7 +157,7 @@ class CommandDispatcher: else: return None - def _tab_focus_stack(self, mode: str, *, show_error=True): + def _tab_focus_stack(self, mode: str, *, show_error: bool = True) -> None: """Select the tab which was last focused.""" tab_deque = self._tabbed_browser.tab_deque cur_tab = self._cntwidget() diff --git a/qutebrowser/browser/downloads.py b/qutebrowser/browser/downloads.py index d2991c40e..54445f011 100644 --- a/qutebrowser/browser/downloads.py +++ b/qutebrowser/browser/downloads.py @@ -860,7 +860,7 @@ class AbstractDownloadManager(QObject): self.data_changed.emit(-1) @pyqtSlot(str, QUrl) - def _on_pdfjs_requested(self, filename: str, original_url: QUrl): + def _on_pdfjs_requested(self, filename: str, original_url: QUrl) -> None: """Open PDF.js when a download requests it.""" tabbed_browser = objreg.get('tabbed-browser', scope='window', window='last-focused') diff --git a/qutebrowser/browser/downloadview.py b/qutebrowser/browser/downloadview.py index edae7550f..66416030d 100644 --- a/qutebrowser/browser/downloadview.py +++ b/qutebrowser/browser/downloadview.py @@ -132,7 +132,10 @@ class DownloadView(QListView): item.open_file() item.remove() - def _get_menu_actions(self, item) -> _ActionListType: + def _get_menu_actions( + self, + item: downloads.AbstractDownloadItem + ) -> _ActionListType: """Get the available context menu actions for a given DownloadItem. Args: diff --git a/qutebrowser/browser/webengine/webenginedownloads.py b/qutebrowser/browser/webengine/webenginedownloads.py index 44d11f728..dfb08b552 100644 --- a/qutebrowser/browser/webengine/webenginedownloads.py +++ b/qutebrowser/browser/webengine/webenginedownloads.py @@ -24,7 +24,7 @@ import os.path import urllib import functools -from PyQt5.QtCore import pyqtSlot, Qt, QUrl +from PyQt5.QtCore import pyqtSlot, Qt, QUrl, QObject from PyQt5.QtWebEngineWidgets import QWebEngineDownloadItem from qutebrowser.browser import downloads, pdfjs @@ -39,7 +39,8 @@ class DownloadItem(downloads.AbstractDownloadItem): _qt_item: The wrapped item. """ - def __init__(self, qt_item: QWebEngineDownloadItem, parent=None): + def __init__(self, qt_item: QWebEngineDownloadItem, + parent: QObject = None) -> None: super().__init__(parent) self._qt_item = qt_item qt_item.downloadProgress.connect( # type: ignore[attr-defined] diff --git a/qutebrowser/commands/runners.py b/qutebrowser/commands/runners.py index ec052b263..76ae1d64f 100644 --- a/qutebrowser/commands/runners.py +++ b/qutebrowser/commands/runners.py @@ -332,7 +332,7 @@ class CommandRunner(AbstractCommandRunner): self._win_id = win_id @contextlib.contextmanager - def _handle_error(self, safely) -> typing.Iterator[None]: + def _handle_error(self, safely: bool) -> typing.Iterator[None]: """Show exceptions as errors if safely=True is given.""" try: yield diff --git a/qutebrowser/mainwindow/tabbedbrowser.py b/qutebrowser/mainwindow/tabbedbrowser.py index e94dee831..c1179abbf 100644 --- a/qutebrowser/mainwindow/tabbedbrowser.py +++ b/qutebrowser/mainwindow/tabbedbrowser.py @@ -97,7 +97,7 @@ class TabDeque: self._ignore_next = True return tab - def next(self, cur_tab: QWidget, *, keep_overflow=True) -> QWidget: + def next(self, cur_tab: QWidget, *, keep_overflow: bool = True) -> QWidget: """Get the 'next' tab in the stack. Throws IndexError on failure. diff --git a/qutebrowser/mainwindow/tabwidget.py b/qutebrowser/mainwindow/tabwidget.py index dd36fa415..558713fdf 100644 --- a/qutebrowser/mainwindow/tabwidget.py +++ b/qutebrowser/mainwindow/tabwidget.py @@ -352,7 +352,7 @@ class TabWidget(QTabWidget): if config.val.tabs.tabs_are_windows: self.window().setWindowIcon(self.window().windowIcon()) - def setTabIcon(self, idx: int, icon: QIcon): + def setTabIcon(self, idx: int, icon: QIcon) -> None: """Always show tab icons for pinned tabs in some circumstances.""" tab = typing.cast(typing.Optional[browsertab.AbstractTab], self.widget(idx)) diff --git a/qutebrowser/misc/sessions.py b/qutebrowser/misc/sessions.py index 95d73125d..52a66b1a0 100644 --- a/qutebrowser/misc/sessions.py +++ b/qutebrowser/misc/sessions.py @@ -587,7 +587,7 @@ def session_save(name: ArgType = default, *, @cmdutils.register() @cmdutils.argument('name', completion=miscmodels.session) -def session_delete(name, *, force: bool = False) -> None: +def session_delete(name: str, *, force: bool = False) -> None: """Delete a session. Args: From 00d316c0112e80a0ff2873269ed92ad45a42d16e Mon Sep 17 00:00:00 2001 From: Florian Bruhin Date: Sun, 10 May 2020 19:35:28 +0200 Subject: [PATCH 32/34] Check git tree before doing a release See #5426 --- scripts/dev/build_release.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/scripts/dev/build_release.py b/scripts/dev/build_release.py index b6f49a5a6..2f037ac68 100755 --- a/scripts/dev/build_release.py +++ b/scripts/dev/build_release.py @@ -44,7 +44,7 @@ sys.path.insert(0, os.path.join(os.path.dirname(__file__), os.pardir, import qutebrowser from scripts import utils -from scripts.dev import update_3rdparty +from scripts.dev import update_3rdparty, misc_checks def call_script(name, *args, python=sys.executable): @@ -473,6 +473,10 @@ def main(): import github3 # pylint: disable=unused-import read_github_token() + if not misc_checks.check_git(): + utils.print_error("Refusing to do a release with a dirty git tree") + sys.exit(1) + if args.no_asciidoc: os.makedirs(os.path.join('qutebrowser', 'html', 'doc'), exist_ok=True) else: From dc3dde593fe36865817a59da2581c812a7970783 Mon Sep 17 00:00:00 2001 From: Florian Bruhin Date: Sun, 10 May 2020 20:11:48 +0200 Subject: [PATCH 33/34] Fix creating of empty find flags If we don't return a new instance, we'll have a shared one between all find calls. --- qutebrowser/browser/webengine/webenginetab.py | 9 +++++---- qutebrowser/browser/webkit/webkittab.py | 9 +++++---- 2 files changed, 10 insertions(+), 8 deletions(-) diff --git a/qutebrowser/browser/webengine/webenginetab.py b/qutebrowser/browser/webengine/webenginetab.py index 9f946beb9..2f16b6ae3 100644 --- a/qutebrowser/browser/webengine/webenginetab.py +++ b/qutebrowser/browser/webengine/webenginetab.py @@ -239,15 +239,16 @@ class WebEngineSearch(browsertab.AbstractSearch): back yet. """ - _NO_FLAGS = QWebEnginePage.FindFlags(0) # type: ignore[call-overload] - def __init__(self, tab, parent=None): super().__init__(tab, parent) - self._flags = self._NO_FLAGS + self._flags = self._empty_flags() self._pending_searches = 0 # The API necessary to stop wrapping was added in this version self._wrap_handler = _WebEngineSearchWrapHandler() + def _empty_flags(self): + return QWebEnginePage.FindFlags(0) # type: ignore[call-overload] + def connect_signals(self): self._wrap_handler.connect_signal(self._widget.page()) @@ -298,7 +299,7 @@ class WebEngineSearch(browsertab.AbstractSearch): return self.text = text - self._flags = self._NO_FLAGS + self._flags = self._empty_flags() self._wrap_handler.reset_match_data() self._wrap_handler.flag_wrap = wrap if self._is_case_sensitive(ignore_case): diff --git a/qutebrowser/browser/webkit/webkittab.py b/qutebrowser/browser/webkit/webkittab.py index 361e2a402..213c7f277 100644 --- a/qutebrowser/browser/webkit/webkittab.py +++ b/qutebrowser/browser/webkit/webkittab.py @@ -85,11 +85,12 @@ class WebKitSearch(browsertab.AbstractSearch): """QtWebKit implementations related to searching on the page.""" - _NO_FLAGS = QWebPage.FindFlags(0) # type: ignore[call-overload] - def __init__(self, tab, parent=None): super().__init__(tab, parent) - self._flags = self._NO_FLAGS + self._flags = self._empty_flags() + + def _empty_flags(self): + return QWebPage.FindFlags(0) # type: ignore[call-overload] def _call_cb(self, callback, found, text, flags, caller): """Call the given callback if it's non-None. @@ -141,7 +142,7 @@ class WebKitSearch(browsertab.AbstractSearch): self.text = text self.search_displayed = True - self._flags = self._NO_FLAGS + self._flags = self._empty_flags() if self._is_case_sensitive(ignore_case): self._flags |= QWebPage.FindCaseSensitively if reverse: From c1d67c9ef45e23426837eb1837843aca9b0e1656 Mon Sep 17 00:00:00 2001 From: Florian Bruhin Date: Sun, 10 May 2020 20:49:21 +0200 Subject: [PATCH 34/34] Exclude FontBase from docs --- scripts/dev/src2asciidoc.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/dev/src2asciidoc.py b/scripts/dev/src2asciidoc.py index b82ede3e1..51f4a3633 100755 --- a/scripts/dev/src2asciidoc.py +++ b/scripts/dev/src2asciidoc.py @@ -162,7 +162,7 @@ def _get_configtypes(): inspect.isclass(e) and # pylint: disable=protected-access e not in [configtypes.BaseType, configtypes.MappingType, - configtypes._Numeric] and + configtypes._Numeric, configtypes.FontBase] and # pylint: enable=protected-access issubclass(e, configtypes.BaseType)) yield from inspect.getmembers(configtypes, predicate)