summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorFlorian Bruhin <me@the-compiler.org>2022-04-24 18:06:07 +0200
committerFlorian Bruhin <me@the-compiler.org>2022-04-24 18:10:10 +0200
commite2db790593eee8329fe8b168e82043bcbb19cbae (patch)
tree8d0690d20472bba48e4074c463019e0b6d0b2ee6
parent65345eab9572fe2426eb8292aa4ab734c099576f (diff)
downloadqutebrowser-new-stubs.tar.gz
qutebrowser-new-stubs.zip
-rw-r--r--misc/requirements/requirements-mypy.txt2
-rw-r--r--qutebrowser/app.py3
-rw-r--r--qutebrowser/browser/browsertab.py63
-rw-r--r--qutebrowser/browser/commands.py1
-rw-r--r--qutebrowser/browser/downloadview.py17
-rw-r--r--qutebrowser/browser/hints.py8
-rw-r--r--qutebrowser/browser/inspector.py16
-rw-r--r--qutebrowser/browser/network/pac.py3
-rw-r--r--qutebrowser/browser/qtnetworkdownloads.py3
-rw-r--r--qutebrowser/browser/qutescheme.py4
-rw-r--r--qutebrowser/browser/webengine/notification.py6
-rw-r--r--qutebrowser/browser/webengine/webenginedownloads.py8
-rw-r--r--qutebrowser/browser/webengine/webengineelem.py4
-rw-r--r--qutebrowser/browser/webengine/webengineinspector.py11
-rw-r--r--qutebrowser/browser/webengine/webenginequtescheme.py2
-rw-r--r--qutebrowser/browser/webengine/webenginesettings.py7
-rw-r--r--qutebrowser/browser/webengine/webenginetab.py34
-rw-r--r--qutebrowser/browser/webengine/webview.py4
-rw-r--r--qutebrowser/browser/webkit/network/networkreply.py21
-rw-r--r--qutebrowser/browser/webkit/webkitinspector.py4
-rw-r--r--qutebrowser/browser/webkit/webkittab.py76
-rw-r--r--qutebrowser/browser/webkit/webpage.py10
-rw-r--r--qutebrowser/browser/webkit/webview.py4
-rw-r--r--qutebrowser/commands/userscripts.py3
-rw-r--r--qutebrowser/completion/completer.py12
-rw-r--r--qutebrowser/completion/completiondelegate.py2
-rw-r--r--qutebrowser/completion/completionwidget.py42
-rw-r--r--qutebrowser/completion/models/completionmodel.py4
-rw-r--r--qutebrowser/components/misccommands.py2
-rw-r--r--qutebrowser/components/readlinecommands.py2
-rw-r--r--qutebrowser/components/scrollcommands.py4
-rw-r--r--qutebrowser/config/stylesheet.py3
-rw-r--r--qutebrowser/keyinput/keyutils.py10
-rw-r--r--qutebrowser/keyinput/modeman.py1
-rw-r--r--qutebrowser/mainwindow/mainwindow.py10
-rw-r--r--qutebrowser/mainwindow/prompt.py4
-rw-r--r--qutebrowser/mainwindow/statusbar/bar.py5
-rw-r--r--qutebrowser/mainwindow/statusbar/command.py3
-rw-r--r--qutebrowser/mainwindow/statusbar/url.py3
-rw-r--r--qutebrowser/mainwindow/tabbedbrowser.py79
-rw-r--r--qutebrowser/mainwindow/tabwidget.py73
-rw-r--r--qutebrowser/misc/crashsignal.py3
-rw-r--r--qutebrowser/misc/editor.py3
-rw-r--r--qutebrowser/misc/ipc.py11
-rw-r--r--qutebrowser/misc/objects.py4
45 files changed, 373 insertions, 221 deletions
diff --git a/misc/requirements/requirements-mypy.txt b/misc/requirements/requirements-mypy.txt
index d5a266759..28ea9f06a 100644
--- a/misc/requirements/requirements-mypy.txt
+++ b/misc/requirements/requirements-mypy.txt
@@ -11,7 +11,7 @@ mypy==0.942
mypy-extensions==0.4.3
pluggy==1.0.0
Pygments==2.11.2
-PyQt5-stubs==5.15.2.0
+PyQt5-stubs==5.15.6.0
tomli==2.0.1
types-PyYAML==6.0.6
typing_extensions==4.2.0
diff --git a/qutebrowser/app.py b/qutebrowser/app.py
index c046475b5..f74617133 100644
--- a/qutebrowser/app.py
+++ b/qutebrowser/app.py
@@ -562,8 +562,7 @@ class Application(QApplication):
log.init.debug("Initializing application...")
self.launch_time = datetime.datetime.now()
- self.focusObjectChanged.connect( # type: ignore[attr-defined]
- self.on_focus_object_changed)
+ self.focusObjectChanged.connect(self.on_focus_object_changed)
self.setAttribute(Qt.AA_UseHighDpiPixmaps, True)
self.new_window.connect(self._on_new_window)
diff --git a/qutebrowser/browser/browsertab.py b/qutebrowser/browser/browsertab.py
index 661c5f68b..699fe1b0b 100644
--- a/qutebrowser/browser/browsertab.py
+++ b/qutebrowser/browser/browsertab.py
@@ -24,7 +24,7 @@ import itertools
import functools
import dataclasses
from typing import (cast, TYPE_CHECKING, Any, Callable, Iterable, List, Optional,
- Sequence, Set, Type, Union)
+ Sequence, Set, Type, Union, Tuple)
from PyQt5.QtCore import (pyqtSignal, pyqtSlot, QUrl, QObject, QSizeF, Qt,
QEvent, QPoint, QRect)
@@ -35,12 +35,12 @@ from PyQt5.QtNetwork import QNetworkAccessManager
if TYPE_CHECKING:
from PyQt5.QtWebKit import QWebHistory, QWebHistoryItem
- from PyQt5.QtWebKitWidgets import QWebPage
+ from PyQt5.QtWebKitWidgets import QWebPage, QWebView
from PyQt5.QtWebEngineWidgets import (
- QWebEngineHistory, QWebEngineHistoryItem, QWebEnginePage)
+ QWebEngineHistory, QWebEngineHistoryItem, QWebEnginePage, QWebEngineView)
from qutebrowser.keyinput import modeman
-from qutebrowser.config import config
+from qutebrowser.config import config, websettings
from qutebrowser.utils import (utils, objreg, usertypes, log, qtutils,
urlutils, message, jinja)
from qutebrowser.misc import miscwidgets, objects, sessions
@@ -53,6 +53,7 @@ if TYPE_CHECKING:
tab_id_gen = itertools.count(0)
+_WidgetType = Union["QWebView", "QWebEngineView"]
def create(win_id: int,
@@ -156,7 +157,7 @@ class AbstractAction:
action_base: Type[Union['QWebPage.WebAction', 'QWebEnginePage.WebAction']]
def __init__(self, tab: 'AbstractTab') -> None:
- self._widget = cast(QWidget, None)
+ self._widget = cast(_WidgetType, None)
self._tab = tab
def exit_fullscreen(self) -> None:
@@ -172,6 +173,7 @@ class AbstractAction:
member = getattr(self.action_class, name, None)
if not isinstance(member, self.action_base):
raise WebTabError("{} is not a valid web action!".format(name))
+ assert member is not None # for mypy
self._widget.triggerPageAction(member)
def show_source(self, pygments: bool = False) -> None:
@@ -229,7 +231,7 @@ class AbstractPrinting:
"""Attribute ``printing`` of AbstractTab for printing the page."""
def __init__(self, tab: 'AbstractTab') -> None:
- self._widget = cast(QWidget, None)
+ self._widget = cast(_WidgetType, None)
self._tab = tab
def check_pdf_support(self) -> None:
@@ -308,7 +310,7 @@ class AbstractSearch(QObject):
def __init__(self, tab: 'AbstractTab', parent: QWidget = None):
super().__init__(parent)
self._tab = tab
- self._widget = cast(QWidget, None)
+ self._widget = cast(_WidgetType, None)
self.text: Optional[str] = None
self.search_displayed = False
@@ -372,7 +374,7 @@ class AbstractZoom(QObject):
def __init__(self, tab: 'AbstractTab', parent: QWidget = None) -> None:
super().__init__(parent)
self._tab = tab
- self._widget = cast(QWidget, None)
+ self._widget = cast(_WidgetType, None)
# Whether zoom was changed from the default.
self._default_zoom_changed = False
self._init_neighborlist()
@@ -466,7 +468,7 @@ class AbstractCaret(QObject):
mode_manager: modeman.ModeManager,
parent: QWidget = None) -> None:
super().__init__(parent)
- self._widget = cast(QWidget, None)
+ self._widget = cast(_WidgetType, None)
self._mode_manager = mode_manager
mode_manager.entered.connect(self._on_mode_entered)
mode_manager.left.connect(self._on_mode_left)
@@ -559,7 +561,7 @@ class AbstractScroller(QObject):
def __init__(self, tab: 'AbstractTab', parent: QWidget = None):
super().__init__(parent)
self._tab = tab
- self._widget = cast(QWidget, None)
+ self._widget = cast(_WidgetType, None)
if 'log-scroll-pos' in objects.debug_flags:
self.perc_changed.connect(self._log_scroll_pos_change)
@@ -568,16 +570,16 @@ class AbstractScroller(QObject):
log.webview.vdebug( # type: ignore[attr-defined]
"Scroll position changed to {}".format(self.pos_px()))
- def _init_widget(self, widget: QWidget) -> None:
+ def _init_widget(self, widget: _WidgetType) -> None:
self._widget = widget
- def pos_px(self) -> int:
+ def pos_px(self) -> QPoint:
raise NotImplementedError
- def pos_perc(self) -> int:
+ def pos_perc(self) -> Tuple[int, int]:
raise NotImplementedError
- def to_perc(self, x: int = None, y: int = None) -> None:
+ def to_perc(self, x: float = None, y: float = None) -> None:
raise NotImplementedError
def to_point(self, point: QPoint) -> None:
@@ -627,6 +629,8 @@ class AbstractHistoryPrivate:
"""Private API related to the history."""
+ _history: Union["QWebHistory", "QWebEngineHistory"]
+
def serialize(self) -> bytes:
"""Serialize into an opaque format understood by self.deserialize."""
raise NotImplementedError
@@ -711,7 +715,7 @@ class AbstractElements:
_ErrorCallback = Callable[[Exception], None]
def __init__(self, tab: 'AbstractTab') -> None:
- self._widget = cast(QWidget, None)
+ self._widget = cast(_WidgetType, None)
self._tab = tab
def find_css(self, selector: str,
@@ -772,7 +776,7 @@ class AbstractAudio(QObject):
def __init__(self, tab: 'AbstractTab', parent: QWidget = None) -> None:
super().__init__(parent)
- self._widget = cast(QWidget, None)
+ self._widget = cast(_WidgetType, None)
self._tab = tab
def set_muted(self, muted: bool, override: bool = False) -> None:
@@ -804,11 +808,11 @@ class AbstractTabPrivate:
def __init__(self, mode_manager: modeman.ModeManager,
tab: 'AbstractTab') -> None:
- self._widget = cast(QWidget, None)
+ self._widget = cast(_WidgetType, None)
self._tab = tab
self._mode_manager = mode_manager
- def event_target(self) -> QWidget:
+ def event_target(self) -> Optional[QWidget]:
"""Return the widget events should be sent to."""
raise NotImplementedError
@@ -848,7 +852,7 @@ class AbstractTabPrivate:
def shutdown(self) -> None:
raise NotImplementedError
- def run_js_sync(self, code: str) -> None:
+ def run_js_sync(self, code: str) -> Any:
"""Run javascript sync.
Result will be returned when running JS is complete.
@@ -867,7 +871,7 @@ class AbstractTabPrivate:
self._tab.data.inspector = None
self.toggle_inspector(inspector.Position.window)
- def toggle_inspector(self, position: inspector.Position) -> None:
+ def toggle_inspector(self, position: Optional[inspector.Position]) -> None:
"""Show/hide (and if needed, create) the web inspector for this tab."""
tabdata = self._tab.data
if tabdata.inspector is None:
@@ -944,6 +948,19 @@ class AbstractTab(QWidget):
# for a given hostname anyways.
_insecure_hosts: Set[str] = set()
+ # Sub-APIs initialized by subclasses
+ history: AbstractHistory
+ scroller: AbstractScroller
+ caret: AbstractCaret
+ zoom: AbstractZoom
+ search: AbstractSearch
+ printing: AbstractPrinting
+ action: AbstractAction
+ elements: AbstractElements
+ audio: AbstractAudio
+ private_api: AbstractTabPrivate
+ settings: websettings.AbstractSettings
+
def __init__(self, *, win_id: int,
mode_manager: 'modeman.ModeManager',
private: bool,
@@ -962,7 +979,7 @@ class AbstractTab(QWidget):
self.data = TabData()
self._layout = miscwidgets.WrapperLayout(self)
- self._widget = cast(QWidget, None)
+ self._widget = cast(_WidgetType, None)
self._progress = 0
self._load_status = usertypes.LoadStatus.none
self._tab_event_filter = eventfilter.TabEventFilter(
@@ -976,7 +993,7 @@ class AbstractTab(QWidget):
self.before_load_started.connect(self._on_before_load_started)
- def _set_widget(self, widget: QWidget) -> None:
+ def _set_widget(self, widget: Union["QWebView", "QWebEngineView"]) -> None:
# pylint: disable=protected-access
self._widget = widget
self.data.splitter = miscwidgets.InspectorSplitter(
@@ -1195,7 +1212,7 @@ class AbstractTab(QWidget):
def title(self) -> str:
raise NotImplementedError
- def icon(self) -> None:
+ def icon(self) -> QIcon:
raise NotImplementedError
def set_html(self, html: str, base_url: QUrl = QUrl()) -> None:
diff --git a/qutebrowser/browser/commands.py b/qutebrowser/browser/commands.py
index 00d5e521f..7f9c4810d 100644
--- a/qutebrowser/browser/commands.py
+++ b/qutebrowser/browser/commands.py
@@ -913,6 +913,7 @@ class CommandDispatcher:
# Not sure how you enter a command without an active window...
raise cmdutils.CommandError(
"No window specified and couldn't find active window!")
+ assert isinstance(active_win, mainwindow.MainWindow)
win_id = active_win.win_id
if win_id not in objreg.window_registry:
diff --git a/qutebrowser/browser/downloadview.py b/qutebrowser/browser/downloadview.py
index 69c58741a..8187a1002 100644
--- a/qutebrowser/browser/downloadview.py
+++ b/qutebrowser/browser/downloadview.py
@@ -86,6 +86,15 @@ class DownloadView(QListView):
count = model.rowCount()
return utils.get_repr(self, count=count)
+ def _model(self) -> downloads.DownloadModel:
+ """Get the current download model.
+
+ Ensures the model is not None.
+ """
+ model = self.model()
+ assert isinstance(model, downloads.DownloadModel), model
+ return model
+
@pyqtSlot()
def _update_geometry(self):
"""Wrapper to call updateGeometry.
@@ -112,7 +121,7 @@ class DownloadView(QListView):
"""
if not index.isValid():
return
- item = self.model().data(index, downloads.ModelRole.item)
+ item = self._model().data(index, downloads.ModelRole.item)
if item.done and item.successful:
item.open_file()
item.remove()
@@ -126,7 +135,7 @@ class DownloadView(QListView):
Args:
item: The DownloadItem to get the actions for, or None.
"""
- model = self.model()
+ model = self._model()
actions: _ActionListType = []
if item is None:
pass
@@ -154,7 +163,7 @@ class DownloadView(QListView):
"""Show the context menu."""
index = self.indexAt(point)
if index.isValid():
- item = self.model().data(index, downloads.ModelRole.item)
+ item = self._model().data(index, downloads.ModelRole.item)
else:
item = None
self._menu = QMenu(self)
@@ -176,7 +185,7 @@ class DownloadView(QListView):
def sizeHint(self):
"""Return sizeHint based on the view contents."""
- idx = self.model().last_index()
+ idx = self._model().last_index()
bottom = self.visualRect(idx).bottom()
if bottom != -1:
margins = self.contentsMargins()
diff --git a/qutebrowser/browser/hints.py b/qutebrowser/browser/hints.py
index 2e4e8e4b4..335508b3d 100644
--- a/qutebrowser/browser/hints.py
+++ b/qutebrowser/browser/hints.py
@@ -255,7 +255,7 @@ class HintActions:
flags = QUrl.FullyEncoded | QUrl.RemovePassword
if url.scheme() == 'mailto':
flags |= QUrl.RemoveScheme
- urlstr = url.toString(flags) # type: ignore[arg-type]
+ urlstr = url.toString(flags)
new_content = urlstr
@@ -356,8 +356,7 @@ class HintActions:
url: The URL to open as a QUrl.
context: The HintContext to use.
"""
- urlstr = url.toString(
- QUrl.FullyEncoded | QUrl.RemovePassword) # type: ignore[arg-type]
+ urlstr = url.toString(QUrl.FullyEncoded | QUrl.RemovePassword)
args = context.get_args(urlstr)
commandrunner = runners.CommandRunner(self._win_id)
commandrunner.run_safely('spawn ' + ' '.join(args))
@@ -657,6 +656,7 @@ class HintManager(QObject):
self._context.labels[string] = label
keyparser = self._get_keyparser(usertypes.KeyMode.hint)
+ assert isinstance(keyparser, modeparsers.HintKeyParser)
keyparser.update_bindings(strings)
modeman.enter(self._win_id, usertypes.KeyMode.hint,
@@ -852,6 +852,7 @@ class HintManager(QObject):
# apply auto_follow_timeout
timeout = config.val.hints.auto_follow_timeout
normal_parser = self._get_keyparser(usertypes.KeyMode.normal)
+ assert isinstance(normal_parser, modeparsers.NormalKeyParser)
normal_parser.set_inhibited_timeout(timeout)
# unpacking gets us the first (and only) key in the dict.
self._fire(*visible)
@@ -927,6 +928,7 @@ class HintManager(QObject):
self._context.labels[string] = label
keyparser = self._get_keyparser(usertypes.KeyMode.hint)
+ assert isinstance(keyparser, modeparsers.HintKeyParser)
keyparser.update_bindings(strings, preserve_filter=True)
# Note: filter_hints can be called with non-None filterstr only
diff --git a/qutebrowser/browser/inspector.py b/qutebrowser/browser/inspector.py
index 2b40e97e4..51cad3709 100644
--- a/qutebrowser/browser/inspector.py
+++ b/qutebrowser/browser/inspector.py
@@ -22,7 +22,7 @@
import base64
import binascii
import enum
-from typing import cast, Optional
+from typing import cast, Optional, Union, TYPE_CHECKING
from PyQt5.QtWidgets import QWidget
from PyQt5.QtCore import pyqtSignal, pyqtSlot, QObject, QEvent
@@ -34,6 +34,14 @@ from qutebrowser.utils import log, usertypes
from qutebrowser.keyinput import modeman
from qutebrowser.misc import miscwidgets
+if TYPE_CHECKING:
+ from PyQt5.QtWebKitWidgets import QWebInspector, QWebPage
+ from PyQt5.QtWebEngineWidgets import QWebEnginePage
+ from qutebrowser.browser.webengine import webengineinspector
+
+
+_WidgetType = Union["QWebInspector", "webengineinspector.WebEngineInspectorView"]
+
class Position(enum.Enum):
@@ -93,7 +101,7 @@ class AbstractWebInspector(QWidget):
win_id: int,
parent: QWidget = None) -> None:
super().__init__(parent)
- self._widget = cast(QWidget, None)
+ self._widget = cast(_WidgetType, None)
self._layout = miscwidgets.WrapperLayout(self)
self._splitter = splitter
self._position: Optional[Position] = None
@@ -105,7 +113,7 @@ class AbstractWebInspector(QWidget):
eventfilter=self._event_filter,
parent=self)
- def _set_widget(self, widget: QWidget) -> None:
+ def _set_widget(self, widget: _WidgetType) -> None:
self._widget = widget
self._widget.setWindowTitle("Web Inspector")
self._widget.installEventFilter(self._child_event_filter)
@@ -198,7 +206,7 @@ class AbstractWebInspector(QWidget):
geom = base64.b64encode(data).decode('ASCII')
configfiles.state['inspector']['window'] = geom
- def inspect(self, page: QWidget) -> None:
+ def inspect(self, page: Union["QWebPage", "QWebEnginePage"]) -> None:
"""Inspect the given QWeb(Engine)Page."""
raise NotImplementedError
diff --git a/qutebrowser/browser/network/pac.py b/qutebrowser/browser/network/pac.py
index 3a544c78f..99078ab20 100644
--- a/qutebrowser/browser/network/pac.py
+++ b/qutebrowser/browser/network/pac.py
@@ -270,8 +270,7 @@ 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( # type: ignore[attr-defined]
- self._finish)
+ self._reply.finished.connect(self._finish)
@pyqtSlot()
def _finish(self):
diff --git a/qutebrowser/browser/qtnetworkdownloads.py b/qutebrowser/browser/qtnetworkdownloads.py
index 8adb7ea20..a77289efb 100644
--- a/qutebrowser/browser/qtnetworkdownloads.py
+++ b/qutebrowser/browser/qtnetworkdownloads.py
@@ -24,7 +24,7 @@ import os.path
import shutil
import functools
import dataclasses
-from typing import Dict, IO, Optional
+from typing import Dict, IO, Optional, List
from PyQt5.QtCore import pyqtSlot, pyqtSignal, QTimer, QUrl
from PyQt5.QtWidgets import QApplication
@@ -601,6 +601,7 @@ class DownloadManager(downloads.AbstractDownloadManager):
"""
assert nam.adopted_downloads == 0
for download in self.downloads:
+ assert isinstance(download, DownloadItem)
if download._uses_nam(nam): # pylint: disable=protected-access
nam.adopt_download(download)
return nam.adopted_downloads
diff --git a/qutebrowser/browser/qutescheme.py b/qutebrowser/browser/qutescheme.py
index 68e36d249..c0da8ac94 100644
--- a/qutebrowser/browser/qutescheme.py
+++ b/qutebrowser/browser/qutescheme.py
@@ -128,9 +128,7 @@ def data_for_url(url: QUrl) -> Tuple[str, bytes]:
Return:
A (mimetype, data) tuple.
"""
- norm_url = url.adjusted(
- QUrl.NormalizePathSegments | # type: ignore[arg-type]
- QUrl.StripTrailingSlash)
+ norm_url = url.adjusted(QUrl.NormalizePathSegments | QUrl.StripTrailingSlash)
if norm_url != url:
raise Redirect(norm_url)
diff --git a/qutebrowser/browser/webengine/notification.py b/qutebrowser/browser/webengine/notification.py
index 2b77a5ac4..57b593ce1 100644
--- a/qutebrowser/browser/webengine/notification.py
+++ b/qutebrowser/browser/webengine/notification.py
@@ -288,7 +288,7 @@ class NotificationBridgePresenter(QObject):
qt_notification.show()
self._active_notifications[notification_id] = qt_notification
- qt_notification.closed.connect( # type: ignore[attr-defined]
+ qt_notification.closed.connect(
functools.partial(self._adapter.on_web_closed, notification_id))
def _find_replaces_id(
@@ -632,6 +632,7 @@ class HerbeNotificationAdapter(AbstractNotificationAdapter):
self.close_id.emit(pid)
else:
proc = self.sender()
+ assert isinstance(proc, QProcess), proc
stderr = proc.readAllStandardError()
raise Error(f'herbe exited with status {code}: {stderr}')
@@ -757,8 +758,7 @@ class DBusNotificationAdapter(AbstractNotificationAdapter):
QDBusServiceWatcher.WatchForUnregistration,
self,
)
- self._watcher.serviceUnregistered.connect( # type: ignore[attr-defined]
- self._on_service_unregistered)
+ self._watcher.serviceUnregistered.connect(self._on_service_unregistered)
test_service = 'test-notification-service' in objects.debug_flags
service = self.TEST_SERVICE if test_service else self.SERVICE
diff --git a/qutebrowser/browser/webengine/webenginedownloads.py b/qutebrowser/browser/webengine/webenginedownloads.py
index a6a2a1b93..a96f49d6b 100644
--- a/qutebrowser/browser/webengine/webenginedownloads.py
+++ b/qutebrowser/browser/webengine/webenginedownloads.py
@@ -44,10 +44,8 @@ class DownloadItem(downloads.AbstractDownloadItem):
parent: QObject = None) -> None:
super().__init__(manager=manager, parent=manager)
self._qt_item = qt_item
- qt_item.downloadProgress.connect( # type: ignore[attr-defined]
- self.stats.on_download_progress)
- qt_item.stateChanged.connect( # type: ignore[attr-defined]
- self._on_state_changed)
+ qt_item.downloadProgress.connect(self.stats.on_download_progress)
+ qt_item.stateChanged.connect(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
@@ -92,7 +90,7 @@ class DownloadItem(downloads.AbstractDownloadItem):
def _do_die(self):
progress_signal = self._qt_item.downloadProgress
- progress_signal.disconnect() # type: ignore[attr-defined]
+ progress_signal.disconnect()
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 5d4c6ad9a..75b7a51ba 100644
--- a/qutebrowser/browser/webengine/webengineelem.py
+++ b/qutebrowser/browser/webengine/webengineelem.py
@@ -37,6 +37,8 @@ class WebEngineElement(webelem.AbstractWebElement):
"""A web element for QtWebEngine, using JS under the hood."""
+ _tab: "webenginetab.WebEngineTab"
+
def __init__(self, js_dict: Dict[str, Any],
tab: 'webenginetab.WebEngineTab') -> None:
super().__init__(tab)
@@ -248,7 +250,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[call-overload]
+ QApplication.processEvents(
QEventLoop.ExcludeSocketNotifiers |
QEventLoop.ExcludeUserInputEvents)
diff --git a/qutebrowser/browser/webengine/webengineinspector.py b/qutebrowser/browser/webengine/webengineinspector.py
index ae31c0bee..d30a716de 100644
--- a/qutebrowser/browser/webengine/webengineinspector.py
+++ b/qutebrowser/browser/webengine/webengineinspector.py
@@ -49,13 +49,17 @@ class WebEngineInspectorView(QWebEngineView):
See WebEngineView.createWindow for details.
"""
- return self.page().inspectedPage().view().createWindow(wintype)
+ view = self.page().inspectedPage().view()
+ assert isinstance(view, QWebEngineView)
+ return view.createWindow(wintype)
class WebEngineInspector(inspector.AbstractWebInspector):
"""A web inspector for QtWebEngine with Qt API support."""
+ _widget: WebEngineInspectorView
+
def __init__(self, splitter: miscwidgets.InspectorSplitter,
win_id: int,
parent: QWidget = None) -> None:
@@ -66,8 +70,7 @@ class WebEngineInspector(inspector.AbstractWebInspector):
self._settings = webenginesettings.WebEngineSettings(view.settings())
self._set_widget(view)
page = view.page()
- page.windowCloseRequested.connect( # type: ignore[attr-defined]
- self._on_window_close_requested)
+ page.windowCloseRequested.connect(self._on_window_close_requested)
def _on_window_close_requested(self) -> None:
"""Called when the 'x' was clicked in the devtools."""
@@ -96,7 +99,7 @@ class WebEngineInspector(inspector.AbstractWebInspector):
"please install the qt5-qtwebengine-devtools "
"Fedora package.")
- def inspect(self, page: QWebEnginePage) -> None: # type: ignore[override]
+ def inspect(self, page: QWebEnginePage) -> None:
inspector_page = self._widget.page()
inspector_page.setInspectedPage(page)
self._settings.update_for_url(inspector_page.requestedUrl())
diff --git a/qutebrowser/browser/webengine/webenginequtescheme.py b/qutebrowser/browser/webengine/webenginequtescheme.py
index 64361f7c4..9e073951a 100644
--- a/qutebrowser/browser/webengine/webenginequtescheme.py
+++ b/qutebrowser/browser/webengine/webenginequtescheme.py
@@ -138,6 +138,6 @@ def init():
assert not QWebEngineUrlScheme.schemeByName(b'qute').name()
scheme = QWebEngineUrlScheme(b'qute')
scheme.setFlags(
- QWebEngineUrlScheme.LocalScheme | # type: ignore[arg-type]
+ QWebEngineUrlScheme.LocalScheme |
QWebEngineUrlScheme.LocalAccessAllowed)
QWebEngineUrlScheme.registerScheme(scheme)
diff --git a/qutebrowser/browser/webengine/webenginesettings.py b/qutebrowser/browser/webengine/webenginesettings.py
index 5430cec77..0b25726c0 100644
--- a/qutebrowser/browser/webengine/webenginesettings.py
+++ b/qutebrowser/browser/webengine/webenginesettings.py
@@ -332,9 +332,9 @@ class ProfileSetter:
def _update_settings(option):
"""Update global settings when qwebsettings changed."""
_global_settings.update_setting(option)
- default_profile.setter.update_setting(option)
+ default_profile.setter.update_setting(option) # type: ignore[attr-defined]
if private_profile:
- private_profile.setter.update_setting(option)
+ private_profile.setter.update_setting(option) # type: ignore[attr-defined]
def _init_user_agent_str(ua):
@@ -352,8 +352,9 @@ def _init_profile(profile: QWebEngineProfile) -> None:
This currently only contains the steps which are shared between a private and a
non-private profile (at the moment, only the default profile).
"""
+ # FIXME:mypy subclass QWebEngineProfile instead?
profile.setter = ProfileSetter(profile) # type: ignore[attr-defined]
- profile.setter.init_profile()
+ profile.setter.init_profile() # type: ignore[attr-defined]
_qute_scheme_handler.install(profile)
_req_interceptor.install(profile)
diff --git a/qutebrowser/browser/webengine/webenginetab.py b/qutebrowser/browser/webengine/webenginetab.py
index 7d355d10e..28182d1b6 100644
--- a/qutebrowser/browser/webengine/webenginetab.py
+++ b/qutebrowser/browser/webengine/webenginetab.py
@@ -30,7 +30,7 @@ from PyQt5.QtCore import (pyqtSignal, pyqtSlot, Qt, QPoint, QPointF, QTimer, QUr
QObject)
from PyQt5.QtNetwork import QAuthenticator
from PyQt5.QtWidgets import QWidget
-from PyQt5.QtWebEngineWidgets import QWebEnginePage, QWebEngineScript, QWebEngineHistory
+from PyQt5.QtWebEngineWidgets import QWebEnginePage, QWebEngineView, QWebEngineScript, QWebEngineHistory
from qutebrowser.config import config
from qutebrowser.browser import browsertab, eventfilter, shared, webelem, greasemonkey
@@ -57,6 +57,8 @@ class WebEngineAction(browsertab.AbstractAction):
"""QtWebEngine implementations related to web actions."""
+ _widget: webview.WebEngineView
+
action_class = QWebEnginePage
action_base = QWebEnginePage.WebAction
@@ -79,6 +81,8 @@ class WebEnginePrinting(browsertab.AbstractPrinting):
"""QtWebEngine implementations related to printing."""
+ _widget: webview.WebEngineView
+
def check_pdf_support(self):
pass
@@ -190,6 +194,8 @@ class WebEngineSearch(browsertab.AbstractSearch):
_pending_searches: How many searches have been started but not called
back yet.
"""
+
+ _widget: webview.WebEngineView
def __init__(self, tab, parent=None):
super().__init__(tab, parent)
@@ -199,7 +205,7 @@ class WebEngineSearch(browsertab.AbstractSearch):
self._wrap_handler = _WebEngineSearchWrapHandler()
def _empty_flags(self):
- return QWebEnginePage.FindFlags(0) # type: ignore[call-overload]
+ return QWebEnginePage.FindFlags(0)
def _args_to_flags(self, reverse, ignore_case):
flags = self._empty_flags()
@@ -275,8 +281,7 @@ 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[call-overload]
+ flags = QWebEnginePage.FindFlags(int(self._flags))
if flags & QWebEnginePage.FindBackward:
if self._wrap_handler.prevent_wrapping(going_up=False):
return
@@ -493,6 +498,8 @@ class WebEngineScroller(browsertab.AbstractScroller):
"""QtWebEngine implementations related to scrolling."""
+ _widget: webview.WebEngineView
+
def __init__(self, tab, parent=None):
super().__init__(tab, parent)
self._pos_perc = (0, 0)
@@ -713,6 +720,8 @@ class WebEngineZoom(browsertab.AbstractZoom):
"""QtWebEngine implementations related to zooming."""
+ _widget: webview.WebEngineView
+
def _set_factor_internal(self, factor):
self._widget.setZoomFactor(factor)
@@ -799,6 +808,8 @@ class WebEngineAudio(browsertab.AbstractAudio):
If that's the case, we leave it alone.
"""
+ _widget: webview.WebEngineView
+
def __init__(self, tab, parent=None):
super().__init__(tab, parent)
self._overridden = False
@@ -870,6 +881,8 @@ class _WebEnginePermissions(QObject):
"""Handling of various permission-related signals."""
+ _widget: webview.WebEngineView
+
# Using 0 as WORKAROUND for:
# https://www.riverbankcomputing.com/pipermail/pyqt/2019-July/041903.html
@@ -898,7 +911,7 @@ class _WebEnginePermissions(QObject):
def __init__(self, tab, parent=None):
super().__init__(parent)
self._tab = tab
- self._widget = cast(QWidget, None)
+ self._widget = cast(webview.WebEngineView, None)
assert self._options.keys() == self._messages.keys()
def connect_signals(self):
@@ -1033,10 +1046,12 @@ class _Quirk:
class _WebEngineScripts(QObject):
+ _widget: webview.WebEngineView
+
def __init__(self, tab, parent=None):
super().__init__(parent)
self._tab = tab
- self._widget = cast(QWidget, None)
+ self._widget = cast(webview.WebEngineView, None)
self._greasemonkey = greasemonkey.gm_manager
def connect_signals(self):
@@ -1240,6 +1255,8 @@ class WebEngineTabPrivate(browsertab.AbstractTabPrivate):
"""QtWebEngine-related methods which aren't part of the public API."""
+ _widget: webview.WebEngineView
+
def networkaccessmanager(self):
return None
@@ -1275,6 +1292,10 @@ class WebEngineTab(browsertab.AbstractTab):
abort_questions = pyqtSignal()
+ _widget: QWebEngineView
+ search: WebEngineSearch
+ audio: WebEngineAudio
+
def __init__(self, *, win_id, mode_manager, private, parent=None):
super().__init__(win_id=win_id,
mode_manager=mode_manager,
@@ -1685,6 +1706,7 @@ class WebEngineTab(browsertab.AbstractTab):
def _connect_signals(self):
view = self._widget
page = view.page()
+ assert isinstance(page, webview.WebEnginePage)
page.windowCloseRequested.connect(self.window_close_requested)
page.linkHovered.connect(self.link_hovered)
diff --git a/qutebrowser/browser/webengine/webview.py b/qutebrowser/browser/webengine/webview.py
index 76ce1a42e..6e72df014 100644
--- a/qutebrowser/browser/webengine/webview.py
+++ b/qutebrowser/browser/webengine/webview.py
@@ -70,7 +70,9 @@ class WebEngineView(QWebEngineView):
return self.focusProxy()
def shutdown(self):
- self.page().shutdown()
+ page = self.page()
+ assert isinstance(page, WebEnginePage)
+ page.shutdown()
def createWindow(self, wintype):
"""Called by Qt when a page wants to create a new window.
diff --git a/qutebrowser/browser/webkit/network/networkreply.py b/qutebrowser/browser/webkit/network/networkreply.py
index c1ead3209..d5c724d63 100644
--- a/qutebrowser/browser/webkit/network/networkreply.py
+++ b/qutebrowser/browser/webkit/network/networkreply.py
@@ -59,15 +59,9 @@ 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[attr-defined]
- QTimer.singleShot(
- 0,
- lambda: self.readyRead.emit()) # type: ignore[attr-defined]
- QTimer.singleShot(
- 0,
- lambda: self.finished.emit()) # type: ignore[attr-defined]
+ QTimer.singleShot(0, lambda: self.metaDataChanged.emit())
+ QTimer.singleShot(0, lambda: self.readyRead.emit())
+ QTimer.singleShot(0, lambda: self.finished.emit())
@pyqtSlot()
def abort(self):
@@ -122,10 +116,8 @@ class ErrorNetworkReply(QNetworkReply):
# the device to avoid getting a warning.
self.setOpenMode(QIODevice.ReadOnly)
self.setError(error, errorstring)
- QTimer.singleShot(0, lambda:
- self.error.emit(error)) # type: ignore[attr-defined]
- QTimer.singleShot(0, lambda:
- self.finished.emit()) # type: ignore[attr-defined]
+ QTimer.singleShot(0, lambda: self.error.emit(error))
+ QTimer.singleShot(0, lambda: self.finished.emit())
def abort(self):
"""Do nothing since it's a fake reply."""
@@ -152,8 +144,7 @@ class RedirectNetworkReply(QNetworkReply):
def __init__(self, new_url, parent=None):
super().__init__(parent)
self.setAttribute(QNetworkRequest.RedirectionTargetAttribute, new_url)
- QTimer.singleShot(0, lambda:
- self.finished.emit()) # type: ignore[attr-defined]
+ QTimer.singleShot(0, lambda: self.finished.emit())
def abort(self):
"""Called when there's e.g. a redirection limit."""
diff --git a/qutebrowser/browser/webkit/webkitinspector.py b/qutebrowser/browser/webkit/webkitinspector.py
index 57bcd40ea..2d4b95caf 100644
--- a/qutebrowser/browser/webkit/webkitinspector.py
+++ b/qutebrowser/browser/webkit/webkitinspector.py
@@ -31,6 +31,8 @@ class WebKitInspector(inspector.AbstractWebInspector):
"""A web inspector for QtWebKit."""
+ _widget = QWebInspector
+
def __init__(self, splitter: miscwidgets.InspectorSplitter,
win_id: int,
parent: QWidget = None) -> None:
@@ -38,7 +40,7 @@ class WebKitInspector(inspector.AbstractWebInspector):
qwebinspector = QWebInspector()
self._set_widget(qwebinspector)
- def inspect(self, page: QWebPage) -> None: # type: ignore[override]
+ def inspect(self, page: QWebPage) -> None:
settings = QWebSettings.globalSettings()
settings.setAttribute(QWebSettings.DeveloperExtrasEnabled, True)
self._widget.setPage(page)
diff --git a/qutebrowser/browser/webkit/webkittab.py b/qutebrowser/browser/webkit/webkittab.py
index 7a41b995c..6be096acf 100644
--- a/qutebrowser/browser/webkit/webkittab.py
+++ b/qutebrowser/browser/webkit/webkittab.py
@@ -32,8 +32,9 @@ from PyQt5.QtWebKit import QWebSettings, QWebHistory, QWebElement
from PyQt5.QtPrintSupport import QPrinter
from qutebrowser.browser import browsertab, shared
-from qutebrowser.browser.webkit import (webview, tabhistory, webkitelem,
+from qutebrowser.browser.webkit import (webview, webpage, tabhistory, webkitelem,
webkitsettings, webkitinspector)
+from qutebrowser.browser.webkit.network import networkmanager
from qutebrowser.utils import qtutils, usertypes, utils, log, debug, resources
from qutebrowser.keyinput import modeman
from qutebrowser.qt import sip
@@ -46,6 +47,8 @@ class WebKitAction(browsertab.AbstractAction):
action_class = QWebPage
action_base = QWebPage.WebAction
+ _widget: webview.WebView
+
def exit_fullscreen(self):
raise browsertab.UnsupportedOperationError
@@ -69,7 +72,7 @@ class WebKitAction(browsertab.AbstractAction):
'Unselect': QWebPage.ToggleVideoFullscreen + 2,
}
if name in new_actions:
- self._widget.triggerPageAction(new_actions[name])
+ self._widget.triggerPageAction(new_actions[name]) # type: ignore[arg-type]
return
super().run_string(name)
@@ -79,6 +82,8 @@ class WebKitPrinting(browsertab.AbstractPrinting):
"""QtWebKit implementations related to printing."""
+ _widget: webview.WebView
+
def check_pdf_support(self):
pass
@@ -101,6 +106,8 @@ class WebKitSearch(browsertab.AbstractSearch):
"""QtWebKit implementations related to searching on the page."""
+ _widget: webview.WebView
+
def __init__(self, tab, parent=None):
super().__init__(tab, parent)
self._flags = self._empty_flags()
@@ -153,7 +160,8 @@ class WebKitSearch(browsertab.AbstractSearch):
self.search_displayed = False
# We first clear the marked text, then the highlights
self._widget.findText('')
- self._widget.findText('', QWebPage.HighlightAllOccurrences)
+ self._widget.findText(
+ '', QWebPage.HighlightAllOccurrences) # type: ignore[arg-type]
def search(self, text, *, ignore_case=usertypes.IgnoreCase.never,
reverse=False, wrap=True, result_cb=None):
@@ -179,7 +187,7 @@ class WebKitSearch(browsertab.AbstractSearch):
def next_result(self, *, result_cb=None):
self.search_displayed = True
- found = self._widget.findText(self.text, self._flags)
+ found = self._widget.findText(self.text, self._flags) # type: ignore[arg-type]
self._call_cb(result_cb, found, self.text, self._flags, 'next_result')
def prev_result(self, *, result_cb=None):
@@ -191,7 +199,7 @@ class WebKitSearch(browsertab.AbstractSearch):
flags &= ~QWebPage.FindBackward
else:
flags |= QWebPage.FindBackward
- found = self._widget.findText(self.text, flags)
+ found = self._widget.findText(self.text, flags) # type: ignore[arg-type]
self._call_cb(result_cb, found, self.text, flags, 'prev_result')
@@ -199,6 +207,8 @@ class WebKitCaret(browsertab.AbstractCaret):
"""QtWebKit implementations related to moving the cursor/selection."""
+ _widget: webview.WebView
+
def __init__(self,
tab: 'WebKitTab',
mode_manager: modeman.ModeManager,
@@ -515,6 +525,8 @@ class WebKitZoom(browsertab.AbstractZoom):
"""QtWebKit implementations related to zooming."""
+ _widget: webview.WebView
+
def _set_factor_internal(self, factor):
self._widget.setZoomFactor(factor)
@@ -525,6 +537,8 @@ class WebKitScroller(browsertab.AbstractScroller):
# FIXME:qtwebengine When to use the main frame, when the current one?
+ _widget: webview.WebView
+
def pos_px(self):
return self._widget.page().mainFrame().scrollPosition()
@@ -624,6 +638,8 @@ class WebKitHistoryPrivate(browsertab.AbstractHistoryPrivate):
"""History-related methods which are not part of the extension API."""
+ _history: QWebHistory
+
def __init__(self, tab: 'WebKitTab') -> None:
self._tab = tab
self._history = cast(QWebHistory, None)
@@ -695,6 +711,7 @@ class WebKitElements(browsertab.AbstractElements):
"""QtWebKit implementations related to elements on the page."""
_tab: 'WebKitTab'
+ _widget: webview.WebView
def find_css(self, selector, callback, error_cb, *, only_visible=False):
utils.unused(error_cb)
@@ -730,7 +747,7 @@ class WebKitElements(browsertab.AbstractElements):
self.find_css('#' + elem_id, find_id_cb, error_cb=lambda exc: None)
def find_focused(self, callback):
- frame = self._widget.page().currentFrame()
+ frame = cast(Optional[QWebFrame], self._widget.page().currentFrame())
if frame is None:
callback(None)
return
@@ -744,7 +761,7 @@ class WebKitElements(browsertab.AbstractElements):
def find_at_pos(self, pos, callback):
assert pos.x() >= 0
assert pos.y() >= 0
- frame = self._widget.page().frameAt(pos)
+ frame = cast(Optional[QWebFrame], self._widget.page().frameAt(pos))
if frame is None:
# This happens when we click inside the webview, but not actually
# on the QWebPage - for example when clicking the scrollbar
@@ -796,6 +813,8 @@ class WebKitTabPrivate(browsertab.AbstractTabPrivate):
"""QtWebKit-related methods which aren't part of the public API."""
+ _widget: webview.WebView
+
def networkaccessmanager(self):
return self._widget.page().networkAccessManager()
@@ -821,6 +840,8 @@ class WebKitTab(browsertab.AbstractTab):
"""A QtWebKit tab in the browser."""
+ _widget: webview.WebView
+
def __init__(self, *, win_id, mode_manager, private, parent=None):
super().__init__(win_id=win_id,
mode_manager=mode_manager,
@@ -912,6 +933,7 @@ class WebKitTab(browsertab.AbstractTab):
def _on_load_started(self):
super()._on_load_started()
nam = self._widget.page().networkAccessManager()
+ assert isinstance(nam, networkmanager.NetworkManager)
nam.netrc_used = False
# Make sure the icon is cleared when navigating to a page without one.
self.icon_changed.emit(QIcon())
@@ -929,7 +951,9 @@ class WebKitTab(browsertab.AbstractTab):
when using error pages... See
https://github.com/qutebrowser/qutebrowser/issues/84
"""
- self._on_load_finished(not self._widget.page().error_occurred)
+ page = self._widget.page()
+ assert isinstance(page, webpage.BrowserPage)
+ self._on_load_finished(not page.error_occurred)
@pyqtSlot()
def _on_webkit_icon_changed(self):
@@ -984,18 +1008,30 @@ class WebKitTab(browsertab.AbstractTab):
view = self._widget
page = view.page()
frame = page.mainFrame()
- page.windowCloseRequested.connect(self.window_close_requested)
- page.linkHovered.connect(self.link_hovered)
- page.loadProgress.connect(self._on_load_progress)
- frame.loadStarted.connect(self._on_load_started)
+ page.windowCloseRequested.connect( # type: ignore[attr-defined]
+ self.window_close_requested)
+ page.linkHovered.connect( # type: ignore[attr-defined]
+ self.link_hovered)
+ page.loadProgress.connect( # type: ignore[attr-defined]
+ self._on_load_progress)
+ frame.loadStarted.connect( # type: ignore[attr-defined]
+ self._on_load_started)
view.scroll_pos_changed.connect(self.scroller.perc_changed)
- view.titleChanged.connect(self.title_changed)
- view.urlChanged.connect(self._on_url_changed)
+ view.titleChanged.connect( # type: ignore[attr-defined]
+ self.title_changed)
+ view.urlChanged.connect( # type: ignore[attr-defined]
+ self._on_url_changed)
view.shutting_down.connect(self.shutting_down)
page.networkAccessManager().sslErrors.connect(self._on_ssl_errors)
- frame.loadFinished.connect(self._on_frame_load_finished)
- view.iconChanged.connect(self._on_webkit_icon_changed)
- page.frameCreated.connect(self._on_frame_created)
- frame.contentsSizeChanged.connect(self._on_contents_size_changed)
- frame.initialLayoutCompleted.connect(self._on_history_trigger)
- page.navigation_request.connect(self._on_navigation_request)
+ frame.loadFinished.connect( # type: ignore[attr-defined]
+ self._on_frame_load_finished)
+ view.iconChanged.connect( # type: ignore[attr-defined]
+ self._on_webkit_icon_changed)
+ page.frameCreated.connect( # type: ignore[attr-defined]
+ self._on_frame_created)
+ frame.contentsSizeChanged.connect( # type: ignore[attr-defined]
+ self._on_contents_size_changed)
+ frame.initialLayoutCompleted.connect( # type: ignore[attr-defined]
+ self._on_history_trigger)
+ page.navigation_request.connect( # type: ignore[attr-defined]
+ self._on_navigation_request)
diff --git a/qutebrowser/browser/webkit/webpage.py b/qutebrowser/browser/webkit/webpage.py
index ddbd78de2..0adc003d1 100644
--- a/qutebrowser/browser/webkit/webpage.py
+++ b/qutebrowser/browser/webkit/webpage.py
@@ -241,6 +241,7 @@ class BrowserPage(QWebPage):
if download_manager.has_downloads_with_nam(nam):
nam.setParent(download_manager)
else:
+ assert isinstance(nam, networkmanager.NetworkManager)
nam.shutdown()
def display_content(self, reply, mimetype):
@@ -371,11 +372,10 @@ class BrowserPage(QWebPage):
self.setFeaturePermission, frame, feature,
QWebPage.PermissionDeniedByUser)
- url = frame.url().adjusted(cast(QUrl.FormattingOptions,
- QUrl.RemoveUserInfo |
- QUrl.RemovePath |
- QUrl.RemoveQuery |
- QUrl.RemoveFragment))
+ url = frame.url().adjusted(QUrl.RemoveUserInfo | # type: ignore[operator]
+ QUrl.RemovePath |
+ QUrl.RemoveQuery |
+ QUrl.RemoveFragment)
question = shared.feature_permission(
url=url,
option=options[feature], msg=messages[feature],
diff --git a/qutebrowser/browser/webkit/webview.py b/qutebrowser/browser/webkit/webview.py
index 289e29920..e54c4af50 100644
--- a/qutebrowser/browser/webkit/webview.py
+++ b/qutebrowser/browser/webkit/webview.py
@@ -109,7 +109,9 @@ class WebView(QWebView):
settings = self.settings()
settings.setAttribute(QWebSettings.JavascriptEnabled, False)
self.stop()
- self.page().shutdown()
+ page = self.page()
+ assert isinstance(page, webpage.BrowserPage)
+ page.shutdown()
def createWindow(self, wintype):
"""Called by Qt when a page wants to create a new window.
diff --git a/qutebrowser/commands/userscripts.py b/qutebrowser/commands/userscripts.py
index 8282aa7c7..c776324ac 100644
--- a/qutebrowser/commands/userscripts.py
+++ b/qutebrowser/commands/userscripts.py
@@ -63,8 +63,7 @@ class _QtFIFOReader(QObject):
self._fifo = os.fdopen(fd, 'r')
self._notifier = QSocketNotifier(cast(sip.voidptr, fd),
QSocketNotifier.Read, self)
- self._notifier.activated.connect( # type: ignore[attr-defined]
- self.read_line)
+ self._notifier.activated.connect(self.read_line)
@pyqtSlot()
def read_line(self):
diff --git a/qutebrowser/completion/completer.py b/qutebrowser/completion/completer.py
index 8b0de9d8a..b7d49a274 100644
--- a/qutebrowser/completion/completer.py
+++ b/qutebrowser/completion/completer.py
@@ -29,6 +29,7 @@ from qutebrowser.commands import parser, cmdexc
from qutebrowser.misc import objects, split
from qutebrowser.utils import log, utils, debug, objreg
from qutebrowser.completion.models import miscmodels
+from qutebrowser.completion import completionwidget
if TYPE_CHECKING:
from qutebrowser.browser import browsertab
@@ -74,10 +75,15 @@ class Completer(QObject):
def __repr__(self):
return utils.get_repr(self)
+ def _completion(self) -> completionwidget.CompletionView:
+ """Convenience method to get the current completion."""
+ completion = self.parent()
+ assert isinstance(completion, completionwidget.CompletionView)
+ return completion
+
def _model(self):
"""Convenience method to get the current completion model."""
- completion = self.parent()
- return completion.model()
+ return self._completion().model()
def _get_new_completion(self, before_cursor, under_cursor):
"""Get the completion function based on the current command text.
@@ -230,7 +236,7 @@ class Completer(QObject):
@pyqtSlot()
def _update_completion(self):
"""Check if completions are available and activate them."""
- completion = self.parent()
+ completion = self._completion()
if self._cmd.prefix() != ':':
# This is a search or gibberish, so we don't need to complete
diff --git a/qutebrowser/completion/completiondelegate.py b/qutebrowser/completion/completiondelegate.py
index 9ea82e876..e233a31bc 100644
--- a/qutebrowser/completion/completiondelegate.py
+++ b/qutebrowser/completion/completiondelegate.py
@@ -299,7 +299,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[operator]
+ return size + QSize(10, 3)
def paint(self, painter, option, index):
"""Override the QStyledItemDelegate paint function.
diff --git a/qutebrowser/completion/completionwidget.py b/qutebrowser/completion/completionwidget.py
index dd8e267fe..de9100a2e 100644
--- a/qutebrowser/completion/completionwidget.py
+++ b/qutebrowser/completion/completionwidget.py
@@ -30,6 +30,7 @@ from PyQt5.QtCore import pyqtSlot, pyqtSignal, Qt, QItemSelectionModel, QSize
from qutebrowser.config import config, stylesheet
from qutebrowser.completion import completiondelegate
+from qutebrowser.completion.models import completionmodel
from qutebrowser.utils import utils, usertypes, debug, log, qtutils
from qutebrowser.api import cmdutils
if TYPE_CHECKING:
@@ -148,6 +149,15 @@ class CompletionView(QTreeView):
def __repr__(self):
return utils.get_repr(self)
+ def _model(self) -> completionmodel.CompletionModel:
+ """Get the current completion model.
+
+ Ensures the model is not None.
+ """
+ model = self.model()
+ assert isinstance(model, completionmodel.CompletionModel), model
+ return model
+
@pyqtSlot(str)
def _on_config_changed(self, option):
if option in ['completion.height', 'completion.shrink']:
@@ -158,7 +168,7 @@ class CompletionView(QTreeView):
if self.model() is None:
return
width = self.size().width()
- column_widths = self.model().column_widths
+ column_widths = self._model().column_widths
pixel_widths = [(width * perc // 100) for perc in column_widths]
delta = self.verticalScrollBar().sizeHint().width()
@@ -182,21 +192,22 @@ class CompletionView(QTreeView):
Return:
A QModelIndex.
"""
+ model = self._model()
idx = self.selectionModel().currentIndex()
if not idx.isValid():
# No item selected yet
if upwards:
- return self.model().last_item()
+ return model.last_item()
else:
- return self.model().first_item()
+ return model.first_item()
while True:
idx = self.indexAbove(idx) if upwards else self.indexBelow(idx)
# wrap around if we arrived at beginning/end
if not idx.isValid() and upwards:
- return self.model().last_item()
+ return model.last_item()
elif not idx.isValid() and not upwards:
- idx = self.model().first_item()
+ idx = model.first_item()
self.scrollTo(idx.parent())
return idx
elif idx.parent().isValid():
@@ -216,7 +227,7 @@ class CompletionView(QTreeView):
"""
old_idx = self.selectionModel().currentIndex()
idx = old_idx
- model = self.model()
+ model = self._model()
if not idx.isValid():
# No item selected yet
@@ -259,6 +270,7 @@ class CompletionView(QTreeView):
A QModelIndex.
"""
idx = self.selectionModel().currentIndex()
+ model = self._model()
if not idx.isValid():
return self._next_idx(upwards).sibling(0, 0)
idx = idx.parent()
@@ -267,10 +279,10 @@ class CompletionView(QTreeView):
idx = idx.sibling(idx.row() + direction, 0)
if not idx.isValid() and upwards:
# wrap around to the first item of the last category
- return self.model().last_item().sibling(0, 0)
+ return model.last_item().sibling(0, 0)
elif not idx.isValid() and not upwards:
# wrap around to the first item of the first category
- idx = self.model().first_item()
+ idx = model.first_item()
self.scrollTo(idx.parent())
return idx
elif idx.isValid() and idx.child(0, 0).isValid():
@@ -327,7 +339,7 @@ class CompletionView(QTreeView):
selmodel.setCurrentIndex(
idx,
- QItemSelectionModel.ClearAndSelect | # type: ignore[arg-type]
+ QItemSelectionModel.ClearAndSelect |
QItemSelectionModel.Rows)
# if the last item is focused, try to fetch more
@@ -335,7 +347,7 @@ class CompletionView(QTreeView):
if not self.visualRect(next_idx).isValid():
self.expandAll()
- count = self.model().count()
+ count = self._model().count()
if count == 0:
self.hide()
elif count == 1 and config.val.completion.quick:
@@ -382,14 +394,14 @@ class CompletionView(QTreeView):
return
self.pattern = pattern
with debug.log_time(log.completion, 'Set pattern {}'.format(pattern)):
- self.model().set_pattern(pattern)
+ self._model().set_pattern(pattern)
self.selectionModel().clear()
self._maybe_update_geometry()
self._maybe_show()
def _maybe_show(self):
if (config.val.completion.show == 'always' and
- self.model().count() > 0):
+ self._model().count() > 0):
self.show()
else:
self.hide()
@@ -435,7 +447,7 @@ class CompletionView(QTreeView):
indexes = selected.indexes()
if not indexes:
return
- data = str(self.model().data(indexes[0]))
+ data = str(self._model().data(indexes[0]))
self.selection_changed.emit(data)
def resizeEvent(self, e):
@@ -458,7 +470,7 @@ class CompletionView(QTreeView):
index = self.currentIndex()
if not index.isValid():
raise cmdutils.CommandError("No item selected!")
- self.model().delete_cur_item(index)
+ self._model().delete_cur_item(index)
@cmdutils.register(instance='completion',
modes=[usertypes.KeyMode.command], scope='window')
@@ -473,7 +485,7 @@ class CompletionView(QTreeView):
index = self.currentIndex()
if not index.isValid():
raise cmdutils.CommandError("No item selected!")
- text = self.model().data(index)
+ text = self._model().data(index)
if not utils.supports_selection():
sel = False
diff --git a/qutebrowser/completion/models/completionmodel.py b/qutebrowser/completion/models/completionmodel.py
index 236b25533..4236c7777 100644
--- a/qutebrowser/completion/models/completionmodel.py
+++ b/qutebrowser/completion/models/completionmodel.py
@@ -180,10 +180,10 @@ class CompletionModel(QAbstractItemModel):
pattern: The filter pattern to set.
"""
log.completion.debug("Setting completion pattern '{}'".format(pattern))
- self.layoutAboutToBeChanged.emit() # type: ignore[attr-defined]
+ self.layoutAboutToBeChanged.emit()
for cat in self._categories:
cat.set_pattern(pattern)
- self.layoutChanged.emit() # type: ignore[attr-defined]
+ self.layoutChanged.emit()
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 fe908b7d2..c20e8e290 100644
--- a/qutebrowser/components/misccommands.py
+++ b/qutebrowser/components/misccommands.py
@@ -83,7 +83,7 @@ def _print_preview(tab: apitypes.Tab) -> None:
diag = QPrintPreviewDialog(tab)
diag.setAttribute(Qt.WA_DeleteOnClose)
diag.setWindowFlags(
- diag.windowFlags() | # type: ignore[operator, arg-type]
+ diag.windowFlags() |
Qt.WindowMaximizeButtonHint |
Qt.WindowMinimizeButtonHint)
diag.paintRequested.connect(functools.partial(
diff --git a/qutebrowser/components/readlinecommands.py b/qutebrowser/components/readlinecommands.py
index 4ac03041c..a0631f3d1 100644
--- a/qutebrowser/components/readlinecommands.py
+++ b/qutebrowser/components/readlinecommands.py
@@ -42,7 +42,7 @@ class _ReadlineBridge:
"""Get the currently active QLineEdit."""
# FIXME add this to api.utils or so
qapp = QApplication.instance()
- assert qapp is not None
+ assert isinstance(qapp, QApplication)
w = qapp.focusWidget()
if isinstance(w, QLineEdit):
diff --git a/qutebrowser/components/scrollcommands.py b/qutebrowser/components/scrollcommands.py
index 757c23b2b..faa31a931 100644
--- a/qutebrowser/components/scrollcommands.py
+++ b/qutebrowser/components/scrollcommands.py
@@ -19,6 +19,7 @@
"""Scrolling-related commands."""
+from typing import Dict, Callable, Any
from qutebrowser.api import cmdutils, apitypes
@@ -54,7 +55,8 @@ def scroll(tab: apitypes.Tab, direction: str, count: int = 1) -> None:
(up/down/left/right/top/bottom).
count: multiplier
"""
- funcs = {
+ # FIXME:mypy Use a callback protocol to enforce having 'count'?
+ funcs: Dict[str, Callable[..., None]] = {
'up': tab.scroller.up,
'down': tab.scroller.down,
'left': tab.scroller.left,
diff --git a/qutebrowser/config/stylesheet.py b/qutebrowser/config/stylesheet.py
index 2927aec08..2c0c5fb07 100644
--- a/qutebrowser/config/stylesheet.py
+++ b/qutebrowser/config/stylesheet.py
@@ -23,6 +23,7 @@ import functools
from typing import Optional, FrozenSet
from PyQt5.QtCore import pyqtSlot, QObject
+from PyQt5.QtWidgets import QWidget
from qutebrowser.config import config
from qutebrowser.misc import debugcachestats
@@ -71,7 +72,7 @@ class _StyleSheetObserver(QObject):
None.
"""
- def __init__(self, obj: QObject,
+ def __init__(self, obj: QWidget,
stylesheet: Optional[str], update: bool) -> None:
super().__init__()
self._obj = obj
diff --git a/qutebrowser/keyinput/keyutils.py b/qutebrowser/keyinput/keyutils.py
index f74c59aa7..465145386 100644
--- a/qutebrowser/keyinput/keyutils.py
+++ b/qutebrowser/keyinput/keyutils.py
@@ -156,7 +156,7 @@ 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."""
mask = Qt.KeyboardModifierMask
- assert not key & ~mask, hex(key) # type: ignore[operator]
+ assert not key & ~mask, hex(key)
def _is_printable(key: Qt.Key) -> bool:
@@ -255,8 +255,8 @@ def _modifiers_to_string(modifiers: _ModifierType) -> str:
"""
_assert_plain_modifier(modifiers)
altgr = Qt.GroupSwitchModifier
- if modifiers & altgr: # type: ignore[operator]
- modifiers &= ~altgr # type: ignore[operator, assignment]
+ if modifiers & altgr:
+ modifiers &= ~altgr
result = 'AltGr+'
else:
result = ''
@@ -416,7 +416,7 @@ class KeyInfo:
return ''
text = QKeySequence(self.key).toString()
- if not self.modifiers & Qt.ShiftModifier: # type: ignore[operator]
+ if not self.modifiers & Qt.ShiftModifier:
text = text.lower()
return text
@@ -473,7 +473,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[call-overload]
+ modifiers = Qt.KeyboardModifiers(
int(key_and_modifiers) & Qt.KeyboardModifierMask)
yield KeyInfo(key=key, modifiers=modifiers)
diff --git a/qutebrowser/keyinput/modeman.py b/qutebrowser/keyinput/modeman.py
index 3c47fafe3..24c701401 100644
--- a/qutebrowser/keyinput/modeman.py
+++ b/qutebrowser/keyinput/modeman.py
@@ -37,6 +37,7 @@ from qutebrowser.misc import objects
INPUT_MODES = [usertypes.KeyMode.insert, usertypes.KeyMode.passthrough]
PROMPT_MODES = [usertypes.KeyMode.prompt, usertypes.KeyMode.yesno]
+# FIXME:mypy TypedDict?
ParserDictType = MutableMapping[usertypes.KeyMode, basekeyparser.BaseKeyParser]
diff --git a/qutebrowser/mainwindow/mainwindow.py b/qutebrowser/mainwindow/mainwindow.py
index 7f62c2dc4..74522a091 100644
--- a/qutebrowser/mainwindow/mainwindow.py
+++ b/qutebrowser/mainwindow/mainwindow.py
@@ -92,7 +92,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[call-overload]
+ QCoreApplication.processEvents(
QEventLoop.ExcludeUserInputEvents | QEventLoop.ExcludeSocketNotifiers)
if not sip.isdeleted(window):
@@ -561,11 +561,11 @@ class MainWindow(QWidget):
def _set_decoration(self, hidden):
"""Set the visibility of the window decoration via Qt."""
- window_flags: int = Qt.Window
+ window_flags = cast(Qt.WindowFlags, Qt.Window)
refresh_window = self.isVisible()
if hidden:
window_flags |= Qt.CustomizeWindowHint | Qt.NoDropShadowWindowHint
- self.setWindowFlags(cast(Qt.WindowFlags, window_flags))
+ self.setWindowFlags(window_flags)
if refresh_window:
self.show()
@@ -574,9 +574,7 @@ class MainWindow(QWidget):
if not config.val.content.fullscreen.window:
if on:
self.state_before_fullscreen = self.windowState()
- self.setWindowState(
- Qt.WindowFullScreen | # type: ignore[arg-type]
- self.state_before_fullscreen) # type: ignore[operator]
+ self.setWindowState(Qt.WindowFullScreen | self.state_before_fullscreen)
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 6fea16093..f7a04bee0 100644
--- a/qutebrowser/mainwindow/prompt.py
+++ b/qutebrowser/mainwindow/prompt.py
@@ -784,7 +784,7 @@ class FilenamePrompt(_BasePrompt):
selmodel.setCurrentIndex(
idx,
- QItemSelectionModel.ClearAndSelect | # type: ignore[arg-type]
+ QItemSelectionModel.ClearAndSelect |
QItemSelectionModel.Rows)
self._insert_path(idx, clicked=False)
@@ -808,7 +808,7 @@ class DownloadFilenamePrompt(FilenamePrompt):
def __init__(self, question, parent=None):
super().__init__(question, parent)
self._file_model.setFilter(
- QDir.AllDirs | QDir.Drives | QDir.NoDotAndDotDot) # type: ignore[arg-type]
+ QDir.AllDirs | QDir.Drives | QDir.NoDotAndDotDot)
def accept(self, value=None, save=False):
done = super().accept(value, save)
diff --git a/qutebrowser/mainwindow/statusbar/bar.py b/qutebrowser/mainwindow/statusbar/bar.py
index 8bad290be..46cf083bd 100644
--- a/qutebrowser/mainwindow/statusbar/bar.py
+++ b/qutebrowser/mainwindow/statusbar/bar.py
@@ -22,8 +22,7 @@
import enum
import dataclasses
-from PyQt5.QtCore import (pyqtSignal, pyqtSlot, # type: ignore[attr-defined]
- pyqtProperty, Qt, QSize, QTimer)
+from PyQt5.QtCore import pyqtSignal, pyqtSlot, pyqtProperty, Qt, QSize, QTimer
from PyQt5.QtWidgets import QWidget, QHBoxLayout, QStackedLayout, QSizePolicy
from qutebrowser.browser import browsertab
@@ -300,7 +299,7 @@ class StatusBar(QWidget):
padding = config.val.statusbar.padding
self._hbox.setContentsMargins(padding.left, 0, padding.right, 0)
- @pyqtProperty('QStringList')
+ @pyqtProperty('QStringList') # type: ignore[type-var]
def color_flags(self):
"""Getter for self.color_flags, so it can be used as Qt property."""
return self._color_flags.to_stringlist()
diff --git a/qutebrowser/mainwindow/statusbar/command.py b/qutebrowser/mainwindow/statusbar/command.py
index 199f0a103..95076380a 100644
--- a/qutebrowser/mainwindow/statusbar/command.py
+++ b/qutebrowser/mainwindow/statusbar/command.py
@@ -19,6 +19,7 @@
"""The commandline in the statusbar."""
+from typing import Optional
from PyQt5.QtCore import pyqtSignal, pyqtSlot, Qt, QSize
from PyQt5.QtGui import QKeyEvent
@@ -240,7 +241,7 @@ class Command(misc.MinimalLineEditMixin, misc.CommandLineEdit):
self.clear_completion_selection.emit()
self.hide_completion.emit()
- def setText(self, text: str) -> None:
+ def setText(self, text: Optional[str]) -> None:
"""Extend setText to set prefix and make sure the prompt is ok."""
if not text:
pass
diff --git a/qutebrowser/mainwindow/statusbar/url.py b/qutebrowser/mainwindow/statusbar/url.py
index 99818e284..ca9b15779 100644
--- a/qutebrowser/mainwindow/statusbar/url.py
+++ b/qutebrowser/mainwindow/statusbar/url.py
@@ -21,8 +21,7 @@
import enum
-from PyQt5.QtCore import (pyqtSlot, pyqtProperty, # type: ignore[attr-defined]
- QUrl)
+from PyQt5.QtCore import pyqtSlot, pyqtProperty, 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 a96f6d583..840e69cd0 100644
--- a/qutebrowser/mainwindow/tabbedbrowser.py
+++ b/qutebrowser/mainwindow/tabbedbrowser.py
@@ -28,7 +28,7 @@ from typing import (
Any, Deque, List, Mapping, MutableMapping, MutableSequence, Optional, Tuple)
from PyQt5.QtWidgets import QSizePolicy, QWidget, QApplication
-from PyQt5.QtCore import pyqtSignal, pyqtSlot, QTimer, QUrl
+from PyQt5.QtCore import pyqtSignal, pyqtSlot, QTimer, QUrl, QPoint
from qutebrowser.config import config
from qutebrowser.keyinput import modeman
@@ -73,14 +73,14 @@ class TabDeque:
size = config.val.tabs.focus_stack_size
if size < 0:
size = None
- self._stack: Deque[weakref.ReferenceType[QWidget]] = collections.deque(
- maxlen=size)
+ self._stack: Deque[weakref.ReferenceType[browsertab.AbstractTab]] = (
+ collections.deque(maxlen=size))
# Items that have been removed from the primary stack.
- self._stack_deleted: List[weakref.ReferenceType[QWidget]] = []
+ self._stack_deleted: List[weakref.ReferenceType[browsertab.AbstractTab]] = []
self._ignore_next = False
self._keep_deleted_next = False
- def on_switch(self, old_tab: QWidget) -> None:
+ def on_switch(self, old_tab: browsertab.AbstractTab) -> None:
"""Record tab switch events."""
if self._ignore_next:
self._ignore_next = False
@@ -92,24 +92,29 @@ class TabDeque:
self._keep_deleted_next = False
self._stack.append(tab)
- def prev(self, cur_tab: QWidget) -> QWidget:
+ def prev(self, cur_tab: browsertab.AbstractTab) -> browsertab.AbstractTab:
"""Get the 'previous' tab in the stack.
Throws IndexError on failure.
"""
- tab: Optional[QWidget] = None
+ tab: Optional[browsertab.AbstractTab] = None
while tab is None or tab.pending_removal or tab is cur_tab:
tab = self._stack.pop()()
self._stack_deleted.append(weakref.ref(cur_tab))
self._ignore_next = True
return tab
- def next(self, cur_tab: QWidget, *, keep_overflow: bool = True) -> QWidget:
+ def next(
+ self,
+ cur_tab: browsertab.AbstractTab,
+ *,
+ keep_overflow: bool = True,
+ ) -> browsertab.AbstractTab:
"""Get the 'next' tab in the stack.
Throws IndexError on failure.
"""
- tab: Optional[QWidget] = None
+ tab: Optional[browsertab.AbstractTab] = None
while tab is None or tab.pending_removal or tab is cur_tab:
tab = self._stack_deleted.pop()()
# On next tab-switch, current tab will be added to stack as normal.
@@ -118,7 +123,7 @@ class TabDeque:
self._keep_deleted_next = True
return tab
- def last(self, cur_tab: QWidget) -> QWidget:
+ def last(self, cur_tab: browsertab.AbstractTab) -> browsertab.AbstractTab:
"""Get the last tab.
Throws IndexError on failure.
@@ -216,7 +221,11 @@ class TabbedBrowser(QWidget):
self.widget.tabCloseRequested.connect(self.on_tab_close_requested)
self.widget.new_tab_requested.connect(self.tabopen)
self.widget.currentChanged.connect(self._on_current_changed)
- self.cur_fullscreen_requested.connect(self.widget.tabBar().maybe_hide)
+
+ tabbar = self.widget.tabBar()
+ assert isinstance(tabbar, tabwidget.TabBar)
+ self.cur_fullscreen_requested.connect(tabbar.maybe_hide)
+
self.widget.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)
# load_finished instead of load_started as WORKAROUND for
@@ -235,8 +244,8 @@ class TabbedBrowser(QWidget):
self._now_focused = None
self.search_text = None
self.search_options: Mapping[str, Any] = {}
- self._local_marks: MutableMapping[QUrl, MutableMapping[str, int]] = {}
- self._global_marks: MutableMapping[str, Tuple[int, QUrl]] = {}
+ self._local_marks: MutableMapping[QUrl, MutableMapping[str, QPoint]] = {}
+ self._global_marks: MutableMapping[str, Tuple[QPoint, QUrl]] = {}
self.default_window_icon = self.widget.window().windowIcon()
self.is_private = private
self.tab_deque = TabDeque()
@@ -373,6 +382,25 @@ class TabbedBrowser(QWidget):
tab.history_item_triggered.connect(
history.web_history.add_from_tab)
+ def _current_tab(self) -> browsertab.AbstractTab:
+ """Get the current browser tab.
+
+ Note: The assert ensures the current tab is never None.
+ """
+ tab = self.widget.currentWidget()
+ assert isinstance(tab, browsertab.AbstractTab), tab
+ return tab
+
+ def _tab_by_idx(self, idx: int) -> Optional[browsertab.AbstractTab]:
+ """Get a browser tab by index.
+
+ If no tab was found at the given index, None is returned.
+ """
+ tab = self.widget.widget(idx)
+ if tab is not None:
+ assert isinstance(tab, browsertab.AbstractTab), tab
+ return tab
+
def current_url(self):
"""Get the URL of the current tab.
@@ -503,13 +531,15 @@ class TabbedBrowser(QWidget):
]
only_one_tab_open = self.widget.count() == 1
if only_one_tab_open and last_close_replaces:
- no_history = len(self.widget.widget(0).history) == 1
+ tab = self._tab_by_idx(0)
+ assert tab is not None
+ no_history = len(tab.history) == 1
urls = {
'blank': QUrl('about:blank'),
'startpage': config.val.url.start_pages[0],
'default-page': config.val.url.default_page,
}
- first_tab_url = self.widget.widget(0).url()
+ first_tab_url = tab.url()
last_close_urlstr = urls[last_close].toString().rstrip('/')
first_tab_urlstr = first_tab_url.toString().rstrip('/')
last_close_url_used = first_tab_urlstr == last_close_urlstr
@@ -520,7 +550,8 @@ class TabbedBrowser(QWidget):
for entry in reversed(entries):
if use_current_tab:
- newtab = self.widget.widget(0)
+ newtab = self._tab_by_idx(0)
+ assert newtab is not None
use_current_tab = False
else:
newtab = self.tabopen(background=False, idx=entry.index)
@@ -540,14 +571,14 @@ class TabbedBrowser(QWidget):
if newtab or self.widget.currentWidget() is None:
self.tabopen(url, background=False)
else:
- self.widget.currentWidget().load_url(url)
+ self._current_tab().load_url(url)
@pyqtSlot(int)
def on_tab_close_requested(self, idx):
"""Close a tab via an index."""
- tab = self.widget.widget(idx)
+ tab = self._tab_by_idx(idx)
if tab is None:
- log.webview.debug( # type: ignore[unreachable]
+ log.webview.debug(
"Got invalid tab {} for index {}!".format(tab, idx))
return
self.tab_close_prompt_if_pinned(
@@ -820,6 +851,7 @@ class TabbedBrowser(QWidget):
mode in modeman.INPUT_MODES):
tab = self.widget.currentWidget()
if tab is not None:
+ assert isinstance(tab, browsertab.AbstractTab)
tab.data.input_mode = mode
@pyqtSlot(usertypes.KeyMode)
@@ -833,6 +865,7 @@ class TabbedBrowser(QWidget):
widget))
widget.setFocus()
if config.val.tabs.mode_on_change == 'restore':
+ assert isinstance(widget, browsertab.AbstractTab)
widget.data.input_mode = usertypes.KeyMode.normal
@pyqtSlot(int)
@@ -842,9 +875,9 @@ class TabbedBrowser(QWidget):
if idx == -1 or self.is_shutting_down:
# closing the last tab (before quitting) or shutting down
return
- tab = self.widget.widget(idx)
+ tab = self._tab_by_idx(idx)
if tab is None:
- log.webview.debug( # type: ignore[unreachable]
+ log.webview.debug(
"on_current_changed got called with invalid index {}"
.format(idx))
return
@@ -1022,7 +1055,7 @@ class TabbedBrowser(QWidget):
if key != "'":
message.error("Failed to set mark: url invalid")
return
- point = self.widget.currentWidget().scroller.pos_px()
+ point = self._current_tab().scroller.pos_px()
if key.isupper():
self._global_marks[key] = point, url
@@ -1043,7 +1076,7 @@ class TabbedBrowser(QWidget):
except qtutils.QtValueError:
urlkey = None
- tab = self.widget.currentWidget()
+ tab = self._current_tab()
if key.isupper():
if key in self._global_marks:
diff --git a/qutebrowser/mainwindow/tabwidget.py b/qutebrowser/mainwindow/tabwidget.py
index 511c2c309..2239b5ad3 100644
--- a/qutebrowser/mainwindow/tabwidget.py
+++ b/qutebrowser/mainwindow/tabwidget.py
@@ -22,7 +22,7 @@
import functools
import contextlib
import dataclasses
-from typing import Optional, cast
+from typing import Optional, Dict, Any
from PyQt5.QtCore import (pyqtSignal, pyqtSlot, Qt, QSize, QRect, QPoint,
QTimer, QUrl)
@@ -76,17 +76,30 @@ class TabWidget(QTabWidget):
@config.change_filter('tabs')
def _init_config(self):
"""Initialize attributes based on the config."""
- tabbar = self.tabBar()
self.setMovable(True)
self.setTabsClosable(False)
position = config.val.tabs.position
selection_behavior = config.val.tabs.select_on_remove
self.setTabPosition(position)
- tabbar.vertical = position in [ # type: ignore[attr-defined]
- QTabWidget.West, QTabWidget.East]
+
+ tabbar = self._tab_bar()
+ tabbar.vertical = position in [QTabWidget.West, QTabWidget.East]
tabbar.setSelectionBehaviorOnRemove(selection_behavior)
tabbar.refresh()
+ def _tab_bar(self) -> "TabBar":
+ """Get the TabBar for this TabWidget."""
+ bar = self.tabBar()
+ assert isinstance(bar, TabBar)
+ return bar
+
+ def _tab_by_idx(self, idx: int) -> Optional[browsertab.AbstractTab]:
+ """Get the tab at the given index."""
+ tab = self.widget(idx)
+ if tab is not None:
+ assert isinstance(tab, browsertab.AbstractTab)
+ return tab
+
def set_tab_indicator_color(self, idx, color):
"""Set the tab indicator color.
@@ -94,17 +107,17 @@ class TabWidget(QTabWidget):
idx: The tab index.
color: A QColor.
"""
- bar = self.tabBar()
+ bar = self._tab_bar()
bar.set_tab_data(idx, 'indicator-color', color)
bar.update(bar.tabRect(idx))
def tab_indicator_color(self, idx):
"""Get the tab indicator color for the given index."""
- return self.tabBar().tab_indicator_color(idx)
+ return self._tab_bar().tab_indicator_color(idx)
def set_page_title(self, idx, title):
"""Set the tab title user data."""
- tabbar = self.tabBar()
+ tabbar = self._tab_bar()
if config.cache['tabs.tooltips']:
# always show only plain title in tooltips
@@ -115,7 +128,7 @@ class TabWidget(QTabWidget):
def page_title(self, idx):
"""Get the tab title user data."""
- return self.tabBar().page_title(idx)
+ return self._tab_bar().page_title(idx)
def update_tab_title(self, idx, field=None):
"""Update the tab text for the given tab.
@@ -126,7 +139,8 @@ class TabWidget(QTabWidget):
is only set if the given field is in the template.
"""
assert idx != -1
- tab = self.widget(idx)
+ tab = self._tab_by_idx(idx)
+ assert tab is not None
if tab.data.pinned:
fmt = config.cache['tabs.title.format_pinned']
else:
@@ -142,7 +156,7 @@ class TabWidget(QTabWidget):
def left_align(num):
return str(num).ljust(len(str(self.count())))
- bar = self.tabBar()
+ bar = self._tab_bar()
cur_idx = bar.currentIndex()
if idx == cur_idx:
rel_idx = left_align(idx + 1) + " "
@@ -164,14 +178,12 @@ class TabWidget(QTabWidget):
def get_tab_fields(self, idx):
"""Get the tab field data."""
- tab = self.widget(idx)
- if tab is None:
- log.misc.debug( # type: ignore[unreachable]
- "Got None-tab in get_tab_fields!")
+ tab = self._tab_by_idx(idx)
+ assert tab is not None
page_title = self.page_title(idx)
- fields = {}
+ fields: Dict[str, Any] = {}
fields['id'] = tab.tab_id
fields['current_title'] = page_title
fields['title_sep'] = ' - ' if page_title else ''
@@ -206,9 +218,7 @@ class TabWidget(QTabWidget):
fields['protocol'] = url.scheme()
y = tab.scroller.pos_perc()[1]
- if y is None:
- scroll_pos = '???'
- elif y <= 0:
+ if y <= 0:
scroll_pos = 'top'
elif y >= 100:
scroll_pos = 'bot'
@@ -228,7 +238,7 @@ class TabWidget(QTabWidget):
non-visible. To avoid flickering, disable repaint updates while we
work.
"""
- bar = self.tabBar()
+ bar = self._tab_bar()
toggle = (self.count() > 10 and
not bar.drag_in_progress and
bar.isVisible())
@@ -317,7 +327,7 @@ class TabWidget(QTabWidget):
@pyqtSlot(int)
def _on_current_changed(self, index):
"""Emit the tab_index_changed signal if the current tab changed."""
- self.tabBar().on_current_changed()
+ self._tab_bar().on_current_changed()
self.update_tab_titles()
self.tab_index_changed.emit(index, self.count())
@@ -332,16 +342,13 @@ class TabWidget(QTabWidget):
Return:
The tab URL as QUrl.
"""
- tab = self.widget(idx)
- if tab is None:
- url = QUrl() # type: ignore[unreachable]
- else:
- url = tab.url()
+ tab = self._tab_by_idx(idx)
+ url = QUrl() if tab is None else tab.url()
# It's possible for url to be invalid, but the caller will handle that.
qtutils.ensure_valid(url)
return url
- def update_tab_favicon(self, tab: QWidget) -> None:
+ def update_tab_favicon(self, tab: browsertab.AbstractTab) -> None:
"""Update favicon of the given tab."""
idx = self.indexOf(tab)
@@ -353,11 +360,11 @@ class TabWidget(QTabWidget):
def setTabIcon(self, idx: int, icon: QIcon) -> None:
"""Always show tab icons for pinned tabs in some circumstances."""
- tab = cast(Optional[browsertab.AbstractTab], self.widget(idx))
+ tab = self._tab_by_idx(idx)
if (icon.isNull() and
config.cache['tabs.favicons.show'] != 'never' and
config.cache['tabs.pinned.shrink'] and
- not self.tabBar().vertical and
+ not self._tab_bar().vertical and
tab is not None and tab.data.pinned):
icon = self.style().standardIcon(QStyle.SP_FileIcon)
super().setTabIcon(idx, icon)
@@ -423,9 +430,15 @@ class TabBar(QTabBar):
def __repr__(self):
return utils.get_repr(self, count=self.count())
+ def _tab_widget(self):
+ """Get the TabWidget we're in."""
+ parent = self.parent()
+ assert isinstance(parent, TabWidget)
+ return parent
+
def _current_tab(self):
"""Get the current tab object."""
- return self.parent().currentWidget()
+ return self._tab_widget().currentWidget()
@pyqtSlot(str)
def _on_config_changed(self, option: str) -> None:
@@ -627,7 +640,7 @@ class TabBar(QTabBar):
raise IndexError("Tab index ({}) out of range ({})!".format(
index, self.count()))
- widget = self.parent().widget(index)
+ widget = self._tab_widget().widget(index)
if widget is None:
# This could happen when Qt calls tabSizeHint while initializing
# tabs.
diff --git a/qutebrowser/misc/crashsignal.py b/qutebrowser/misc/crashsignal.py
index d94d3ec54..a79118044 100644
--- a/qutebrowser/misc/crashsignal.py
+++ b/qutebrowser/misc/crashsignal.py
@@ -361,8 +361,7 @@ class SignalHandler(QObject):
self._notifier = QSocketNotifier(cast(sip.voidptr, read_fd),
QSocketNotifier.Read,
self)
- self._notifier.activated.connect( # type: ignore[attr-defined]
- self.handle_signal_wakeup)
+ self._notifier.activated.connect(self.handle_signal_wakeup)
self._orig_wakeup_fd = signal.set_wakeup_fd(write_fd)
# pylint: enable=import-error,no-member,useless-suppression
else:
diff --git a/qutebrowser/misc/editor.py b/qutebrowser/misc/editor.py
index 3ef84284d..a4bc1d8ab 100644
--- a/qutebrowser/misc/editor.py
+++ b/qutebrowser/misc/editor.py
@@ -207,8 +207,7 @@ class ExternalEditor(QObject):
if not ok:
log.procs.error("Failed to watch path: {}"
.format(self._filename))
- self._watcher.fileChanged.connect( # type: ignore[attr-defined]
- self._on_file_changed)
+ self._watcher.fileChanged.connect(self._on_file_changed)
args = [self._sub_placeholder(arg, line, column) for arg in editor[1:]]
log.procs.debug("Calling \"{}\" with args {}".format(executable, args))
diff --git a/qutebrowser/misc/ipc.py b/qutebrowser/misc/ipc.py
index 77b9e8f6c..f5955b0e3 100644
--- a/qutebrowser/misc/ipc.py
+++ b/qutebrowser/misc/ipc.py
@@ -191,8 +191,7 @@ class IPCServer(QObject):
self._atime_timer.setTimerType(Qt.VeryCoarseTimer)
self._server = QLocalServer(self)
- self._server.newConnection.connect( # type: ignore[attr-defined]
- self.handle_connection)
+ self._server.newConnection.connect(self.handle_connection)
self._socket = None
self._old_socket = None
@@ -270,18 +269,16 @@ class IPCServer(QObject):
log.ipc.debug("Client connected (socket 0x{:x}).".format(id(socket)))
self._socket = socket
self._timer.start()
- socket.readyRead.connect( # type: ignore[attr-defined]
- self.on_ready_read)
+ socket.readyRead.connect(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[attr-defined]
+ socket.error.connect(self.on_error)
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( # type: ignore[attr-defined]
- self.on_disconnected)
+ socket.disconnected.connect(self.on_disconnected)
if socket.state() == QLocalSocket.UnconnectedState:
log.ipc.debug("Socket was disconnected immediately.")
self.on_disconnected()
diff --git a/qutebrowser/misc/objects.py b/qutebrowser/misc/objects.py
index 00a1ef35d..63a9cb2dd 100644
--- a/qutebrowser/misc/objects.py
+++ b/qutebrowser/misc/objects.py
@@ -26,7 +26,7 @@ import argparse
from typing import TYPE_CHECKING, Any, Dict, Set, Union, cast
if TYPE_CHECKING:
- from PyQt5.QtWidgets import QApplication
+ from qutebrowser import app
from qutebrowser.utils import usertypes
from qutebrowser.commands import command
@@ -47,4 +47,4 @@ backend: Union['usertypes.Backend', NoBackend] = NoBackend()
commands: Dict[str, 'command.Command'] = {}
debug_flags: Set[str] = set()
args = cast(argparse.Namespace, None)
-qapp = cast('QApplication', None)
+qapp = cast('app.Application', None)