From d7786c694fb30eb9da79b5842629cc271d0ed09e Mon Sep 17 00:00:00 2001 From: Florian Bruhin Date: Tue, 27 Nov 2018 20:35:58 +0100 Subject: [PATCH] Add type hints for qutebrowser.browser.browsertab --- mypy.ini | 3 + qutebrowser/browser/browsertab.py | 372 ++++++++++-------- qutebrowser/browser/webengine/webenginetab.py | 9 +- qutebrowser/browser/webkit/webkittab.py | 8 +- 4 files changed, 229 insertions(+), 163 deletions(-) diff --git a/mypy.ini b/mypy.ini index 578a8c1fc..94d5dff40 100644 --- a/mypy.ini +++ b/mypy.ini @@ -49,3 +49,6 @@ ignore_missing_imports = True [mypy-qutebrowser.browser.webkit.rfc6266] # subclasses dynamic PyPEG2 classes disallow_subclassing_any = False + +[mypy-qutebrowser.browser.browsertab] +disallow_untyped_defs = True diff --git a/qutebrowser/browser/browsertab.py b/qutebrowser/browser/browsertab.py index 02d9b70dd..f3a613844 100644 --- a/qutebrowser/browser/browsertab.py +++ b/qutebrowser/browser/browsertab.py @@ -21,12 +21,19 @@ import enum import itertools +import typing import attr -from PyQt5.QtCore import pyqtSignal, pyqtSlot, QUrl, QObject, QSizeF, Qt +from PyQt5.QtCore import (pyqtSignal, pyqtSlot, QUrl, QObject, QSizeF, Qt, + QEvent, QPoint) from PyQt5.QtGui import QIcon from PyQt5.QtWidgets import QWidget, QApplication, QDialog -from PyQt5.QtPrintSupport import QPrintDialog +from PyQt5.QtPrintSupport import QPrintDialog, QPrinter +from PyQt5.QtNetwork import QNetworkAccessManager +MYPY = False +if MYPY: + # pylint: disable=unused-import + from PyQt5.QtWebEngineWidgets import QWebEngineView import pygments import pygments.lexers @@ -37,14 +44,19 @@ from qutebrowser.config import config from qutebrowser.utils import (utils, objreg, usertypes, log, qtutils, urlutils, message) from qutebrowser.misc import miscwidgets, objects -from qutebrowser.browser import mouse, hints +from qutebrowser.browser import mouse, hints, webelem from qutebrowser.qt import sip +if MYPY: + # pylint: disable=unused-import + from qutebrowser.browser.inspector import AbstractWebInspector tab_id_gen = itertools.count(0) -def create(win_id, private, parent=None): +def create(win_id: int, + private: bool, + parent: QWidget = None) -> 'AbstractTab': """Get a QtWebKit/QtWebEngine tab object. Args: @@ -65,7 +77,7 @@ def create(win_id, private, parent=None): parent=parent) -def init(): +def init() -> None: """Initialize backend-specific modules.""" if objects.backend == usertypes.Backend.QtWebEngine: from qutebrowser.browser.webengine import webenginetab @@ -112,17 +124,18 @@ class TabData: input_mode: current input mode for the tab. """ - keep_icon = attr.ib(False) - viewing_source = attr.ib(False) - inspector = attr.ib(None) - open_target = attr.ib(usertypes.ClickTarget.normal) - override_target = attr.ib(None) - pinned = attr.ib(False) - fullscreen = attr.ib(False) - netrc_used = attr.ib(False) - input_mode = attr.ib(usertypes.KeyMode.normal) + keep_icon = attr.ib(False) # type: bool + viewing_source = attr.ib(False) # type: bool + inspector = attr.ib(None) # type: typing.Optional[AbstractWebInspector] + open_target = attr.ib( + usertypes.ClickTarget.normal) # type: usertypes.ClickTarget + override_target = attr.ib(None) # type: usertypes.ClickTarget + pinned = attr.ib(False) # type: bool + fullscreen = attr.ib(False) # type: bool + netrc_used = attr.ib(False) # type: bool + input_mode = attr.ib(usertypes.KeyMode.normal) # type: usertypes.KeyMode - def should_show_icon(self): + def should_show_icon(self) -> bool: return (config.val.tabs.favicons.show == 'always' or config.val.tabs.favicons.show == 'pinned' and self.pinned) @@ -139,33 +152,35 @@ class AbstractAction: action_class = None # type: type action_base = None # type: type - def __init__(self, tab): - self._widget = None + def __init__(self, tab: 'AbstractTab') -> None: + self._widget = typing.cast('QWebEngineView', None) self._tab = tab - def exit_fullscreen(self): + def exit_fullscreen(self) -> None: """Exit the fullscreen mode.""" raise NotImplementedError - def save_page(self): + def save_page(self) -> None: """Save the current page.""" raise NotImplementedError - def run_string(self, name): + def run_string(self, name: str) -> None: """Run a webaction based on its name.""" member = getattr(self.action_class, name, None) if not isinstance(member, self.action_base): raise WebTabError("{} is not a valid web action!".format(name)) - self._widget.triggerPageAction(member) + self._widget.triggerPageAction(member) # type: ignore - def show_source(self, - pygments=False): # pylint: disable=redefined-outer-name + def show_source( + self, + pygments: bool = False # pylint: disable=redefined-outer-name + ) -> None: """Show the source of the current page in a new tab.""" raise NotImplementedError - def _show_source_pygments(self): + def _show_source_pygments(self) -> None: - def show_source_cb(source): + def show_source_cb(source: str) -> None: """Show source as soon as it's ready.""" # WORKAROUND for https://github.com/PyCQA/pylint/issues/491 # pylint: disable=no-member @@ -188,23 +203,24 @@ class AbstractPrinting: """Attribute of AbstractTab for printing the page.""" - def __init__(self, tab): + def __init__(self, tab: 'AbstractTab') -> None: self._widget = None self._tab = tab - def check_pdf_support(self): + def check_pdf_support(self) -> bool: raise NotImplementedError - def check_printer_support(self): + def check_printer_support(self) -> bool: raise NotImplementedError - def check_preview_support(self): + def check_preview_support(self) -> bool: raise NotImplementedError - def to_pdf(self, filename): + def to_pdf(self, filename: str) -> bool: raise NotImplementedError - def to_printer(self, printer, callback=None): + def to_printer(self, printer: QPrinter, + callback: typing.Callable[[bool], None] = None) -> None: """Print the tab. Args: @@ -214,17 +230,17 @@ class AbstractPrinting: """ raise NotImplementedError - def show_dialog(self): + def show_dialog(self) -> None: """Print with a QPrintDialog.""" self.check_printer_support() - def print_callback(ok): + def print_callback(ok: bool) -> None: """Called when printing finished.""" if not ok: message.error("Printing failed!") diag.deleteLater() - def do_print(): + def do_print() -> None: """Called when the dialog was closed.""" self.to_printer(diag.printer(), print_callback) @@ -257,15 +273,16 @@ class AbstractSearch(QObject): finished = pyqtSignal(bool) cleared = pyqtSignal() + _Callback = typing.Callable[[bool], None] - def __init__(self, tab, parent=None): + def __init__(self, tab: 'AbstractTab', parent: QWidget = None): super().__init__(parent) self._tab = tab self._widget = None - self.text = None + self.text = None # type: typing.Optional[str] self.search_displayed = False - def _is_case_sensitive(self, ignore_case): + def _is_case_sensitive(self, ignore_case: str) -> bool: """Check if case-sensitivity should be used. This assumes self.text is already set properly. @@ -273,6 +290,7 @@ class AbstractSearch(QObject): Arguments: ignore_case: The ignore_case value from the config. """ + assert self.text is not None mapping = { 'smart': not self.text.islower(), 'never': True, @@ -280,8 +298,10 @@ class AbstractSearch(QObject): } return mapping[ignore_case] - def search(self, text, *, ignore_case='never', reverse=False, - result_cb=None): + def search(self, text: str, *, + ignore_case: str = 'never', + reverse: bool = False, + result_cb: _Callback = None) -> None: """Find the given text on the page. Args: @@ -292,11 +312,11 @@ class AbstractSearch(QObject): """ raise NotImplementedError - def clear(self): + def clear(self) -> None: """Clear the current search.""" raise NotImplementedError - def prev_result(self, *, result_cb=None): + def prev_result(self, *, result_cb: _Callback = None) -> None: """Go to the previous result of the current search. Args: @@ -304,7 +324,7 @@ class AbstractSearch(QObject): """ raise NotImplementedError - def next_result(self, *, result_cb=None): + def next_result(self, *, result_cb: _Callback = None) -> None: """Go to the next result of the current search. Args: @@ -322,7 +342,7 @@ class AbstractZoom(QObject): _default_zoom_changed: Whether the zoom was changed from the default. """ - def __init__(self, tab, parent=None): + def __init__(self, tab: 'AbstractTab', parent: QWidget = None) -> None: super().__init__(parent) self._tab = tab self._widget = None @@ -339,21 +359,21 @@ class AbstractZoom(QObject): # cfg.changed.disconnect, self.init_neighborlist)) @pyqtSlot(str) - def _on_config_changed(self, option): + def _on_config_changed(self, option: str) -> None: if option in ['zoom.levels', 'zoom.default']: if not self._default_zoom_changed: factor = float(config.val.zoom.default) / 100 self.set_factor(factor) self._init_neighborlist() - def _init_neighborlist(self): + def _init_neighborlist(self) -> None: """Initialize self._neighborlist.""" levels = config.val.zoom.levels self._neighborlist = usertypes.NeighborList( levels, mode=usertypes.NeighborList.Modes.edge) self._neighborlist.fuzzyval = config.val.zoom.default - def offset(self, offset): + def offset(self, offset: int) -> None: """Increase/Decrease the zoom level by the given offset. Args: @@ -366,10 +386,10 @@ class AbstractZoom(QObject): self.set_factor(float(level) / 100, fuzzyval=False) return level - def _set_factor_internal(self, factor): + def _set_factor_internal(self, factor: float) -> None: raise NotImplementedError - def set_factor(self, factor, *, fuzzyval=True): + def set_factor(self, factor: float, *, fuzzyval: bool = True) -> None: """Zoom to a given zoom factor. Args: @@ -387,13 +407,13 @@ class AbstractZoom(QObject): self._zoom_factor = factor self._set_factor_internal(factor) - def factor(self): + def factor(self) -> float: return self._zoom_factor - def set_default(self): + def set_default(self) -> None: self._set_factor_internal(float(config.val.zoom.default) / 100) - def set_current(self): + def set_current(self) -> None: self._set_factor_internal(self._zoom_factor) @@ -410,7 +430,10 @@ class AbstractCaret(QObject): selection_toggled = pyqtSignal(bool) follow_selected_done = pyqtSignal() - def __init__(self, tab, mode_manager, parent=None): + def __init__(self, + tab: 'AbstractTab', + mode_manager: modeman.ModeManager, + parent: QWidget = None) -> None: super().__init__(parent) self._tab = tab self._widget = None @@ -418,74 +441,74 @@ class AbstractCaret(QObject): mode_manager.entered.connect(self._on_mode_entered) mode_manager.left.connect(self._on_mode_left) - def _on_mode_entered(self, mode): + def _on_mode_entered(self, mode: usertypes.KeyMode) -> None: raise NotImplementedError - def _on_mode_left(self, mode): + def _on_mode_left(self, mode: usertypes.KeyMode) -> None: raise NotImplementedError - def move_to_next_line(self, count=1): + def move_to_next_line(self, count: int = 1) -> None: raise NotImplementedError - def move_to_prev_line(self, count=1): + def move_to_prev_line(self, count: int = 1) -> None: raise NotImplementedError - def move_to_next_char(self, count=1): + def move_to_next_char(self, count: int = 1) -> None: raise NotImplementedError - def move_to_prev_char(self, count=1): + def move_to_prev_char(self, count: int = 1) -> None: raise NotImplementedError - def move_to_end_of_word(self, count=1): + def move_to_end_of_word(self, count: int = 1) -> None: raise NotImplementedError - def move_to_next_word(self, count=1): + def move_to_next_word(self, count: int = 1) -> None: raise NotImplementedError - def move_to_prev_word(self, count=1): + def move_to_prev_word(self, count: int = 1) -> None: raise NotImplementedError - def move_to_start_of_line(self): + def move_to_start_of_line(self) -> None: raise NotImplementedError - def move_to_end_of_line(self): + def move_to_end_of_line(self) -> None: raise NotImplementedError - def move_to_start_of_next_block(self, count=1): + def move_to_start_of_next_block(self, count: int = 1) -> None: raise NotImplementedError - def move_to_start_of_prev_block(self, count=1): + def move_to_start_of_prev_block(self, count: int = 1) -> None: raise NotImplementedError - def move_to_end_of_next_block(self, count=1): + def move_to_end_of_next_block(self, count: int = 1) -> None: raise NotImplementedError - def move_to_end_of_prev_block(self, count=1): + def move_to_end_of_prev_block(self, count: int = 1) -> None: raise NotImplementedError - def move_to_start_of_document(self): + def move_to_start_of_document(self) -> None: raise NotImplementedError - def move_to_end_of_document(self): + def move_to_end_of_document(self) -> None: raise NotImplementedError - def toggle_selection(self): + def toggle_selection(self) -> None: raise NotImplementedError - def drop_selection(self): + def drop_selection(self) -> None: raise NotImplementedError - def selection(self, callback): + def selection(self, callback: typing.Callable[[str], None]) -> None: raise NotImplementedError - def _follow_enter(self, tab): + def _follow_enter(self, tab: bool) -> None: """Follow a link by faking an enter press.""" if tab: self._tab.key_press(Qt.Key_Enter, modifier=Qt.ControlModifier) else: self._tab.key_press(Qt.Key_Enter) - def follow_selected(self, *, tab=False): + def follow_selected(self, *, tab: bool = False) -> None: raise NotImplementedError @@ -495,69 +518,69 @@ class AbstractScroller(QObject): perc_changed = pyqtSignal(int, int) - def __init__(self, tab, parent=None): + def __init__(self, tab: 'AbstractTab', parent: QWidget = None): super().__init__(parent) self._tab = tab - self._widget = None + self._widget = None # type: typing.Optional[QWebEngineView] self.perc_changed.connect(self._log_scroll_pos_change) @pyqtSlot() - def _log_scroll_pos_change(self): - log.webview.vdebug("Scroll position changed to {}".format( - self.pos_px())) + def _log_scroll_pos_change(self) -> None: + log.webview.vdebug( # type: ignore + "Scroll position changed to {}".format(self.pos_px())) - def _init_widget(self, widget): + def _init_widget(self, widget: 'QWebEngineView') -> None: self._widget = widget - def pos_px(self): + def pos_px(self) -> int: raise NotImplementedError - def pos_perc(self): + def pos_perc(self) -> int: raise NotImplementedError - def to_perc(self, x=None, y=None): + def to_perc(self, x: int = None, y: int = None) -> None: raise NotImplementedError - def to_point(self, point): + def to_point(self, point: QPoint) -> None: raise NotImplementedError - def to_anchor(self, name): + def to_anchor(self, name: str) -> None: raise NotImplementedError - def delta(self, x=0, y=0): + def delta(self, x: int = 0, y: int = 0) -> None: raise NotImplementedError - def delta_page(self, x=0, y=0): + def delta_page(self, x: float = 0, y: float = 0) -> None: raise NotImplementedError - def up(self, count=1): + def up(self, count: int = 1) -> None: raise NotImplementedError - def down(self, count=1): + def down(self, count: int = 1) -> None: raise NotImplementedError - def left(self, count=1): + def left(self, count: int = 1) -> None: raise NotImplementedError - def right(self, count=1): + def right(self, count: int = 1) -> None: raise NotImplementedError - def top(self): + def top(self) -> None: raise NotImplementedError - def bottom(self): + def bottom(self) -> None: raise NotImplementedError - def page_up(self, count=1): + def page_up(self, count: int = 1) -> None: raise NotImplementedError - def page_down(self, count=1): + def page_down(self, count: int = 1) -> None: raise NotImplementedError - def at_top(self): + def at_top(self) -> bool: raise NotImplementedError - def at_bottom(self): + def at_bottom(self) -> bool: raise NotImplementedError @@ -565,20 +588,20 @@ class AbstractHistory: """The history attribute of a AbstractTab.""" - def __init__(self, tab): + def __init__(self, tab: 'AbstractTab') -> None: self._tab = tab self._history = None - def __len__(self): - return len(self._history) - - def __iter__(self): - return iter(self._history.items()) - - def current_idx(self): + def __len__(self) -> int: raise NotImplementedError - def back(self, count=1): + def __iter__(self) -> typing.Iterable: + raise NotImplementedError + + def current_idx(self) -> int: + raise NotImplementedError + + def back(self, count: int = 1) -> None: """Go back in the tab's history.""" idx = self.current_idx() - count if idx >= 0: @@ -587,7 +610,7 @@ class AbstractHistory: self._go_to_item(self._item_at(0)) raise WebTabError("At beginning of history.") - def forward(self, count=1): + def forward(self, count: int = 1) -> None: """Go forward in the tab's history.""" idx = self.current_idx() + count if idx < len(self): @@ -596,27 +619,27 @@ class AbstractHistory: self._go_to_item(self._item_at(len(self) - 1)) raise WebTabError("At end of history.") - def can_go_back(self): + def can_go_back(self) -> bool: raise NotImplementedError - def can_go_forward(self): + def can_go_forward(self) -> bool: raise NotImplementedError - def _item_at(self, i): + def _item_at(self, i: int) -> typing.Any: raise NotImplementedError - def _go_to_item(self, item): + def _go_to_item(self, item: typing.Any) -> None: raise NotImplementedError - def serialize(self): + def serialize(self) -> bytes: """Serialize into an opaque format understood by self.deserialize.""" raise NotImplementedError - def deserialize(self, data): - """Serialize from a format produced by self.serialize.""" + def deserialize(self, data: bytes) -> None: + """Deserialize from a format produced by self.serialize.""" raise NotImplementedError - def load_items(self, items): + def load_items(self, items: typing.Sequence) -> None: """Deserialize from a list of WebHistoryItems.""" raise NotImplementedError @@ -625,11 +648,18 @@ class AbstractElements: """Finding and handling of elements on the page.""" - def __init__(self, tab): + _MultiCallback = typing.Callable[ + [typing.Sequence[webelem.AbstractWebElement]], None] + _SingleCallback = typing.Callable[ + [typing.Optional[webelem.AbstractWebElement]], None] + + def __init__(self, tab: 'AbstractTab') -> None: self._widget = None self._tab = tab - def find_css(self, selector, callback, *, only_visible=False): + def find_css(self, selector: str, + callback: _MultiCallback, *, + only_visible: bool = False) -> None: """Find all HTML elements matching a given selector async. If there's an error, the callback is called with a webelem.Error @@ -642,7 +672,7 @@ class AbstractElements: """ raise NotImplementedError - def find_id(self, elem_id, callback): + def find_id(self, elem_id: str, callback: _SingleCallback) -> None: """Find the HTML element with the given ID async. Args: @@ -651,7 +681,7 @@ class AbstractElements: """ raise NotImplementedError - def find_focused(self, callback): + def find_focused(self, callback: _SingleCallback) -> None: """Find the focused element on the page async. Args: @@ -660,7 +690,7 @@ class AbstractElements: """ raise NotImplementedError - def find_at_pos(self, pos, callback): + def find_at_pos(self, pos: QPoint, callback: _SingleCallback) -> None: """Find the element at the given position async. This is also called "hit test" elsewhere. @@ -680,9 +710,9 @@ class AbstractAudio(QObject): muted_changed = pyqtSignal(bool) recently_audible_changed = pyqtSignal(bool) - def __init__(self, tab, parent=None): + def __init__(self, tab: 'AbstractTab', parent: QWidget = None) -> None: super().__init__(parent) - self._widget = None + self._widget = None # type: typing.Optional[QWebEngineView] self._tab = tab def set_muted(self, muted: bool, override: bool = False) -> None: @@ -695,14 +725,14 @@ class AbstractAudio(QObject): """ raise NotImplementedError - def is_muted(self): + def is_muted(self) -> bool: """Whether this tab is muted.""" raise NotImplementedError def toggle_muted(self, *, override: bool = False) -> None: self.set_muted(not self.is_muted(), override=override) - def is_recently_audible(self): + def is_recently_audible(self) -> bool: """Whether this tab has had audio playing recently.""" raise NotImplementedError @@ -758,7 +788,11 @@ class AbstractTab(QWidget): renderer_process_terminated = pyqtSignal(TerminationStatus, int) predicted_navigation = pyqtSignal(QUrl) - def __init__(self, *, win_id, mode_manager, private, parent=None): + def __init__(self, *, + win_id: int, + mode_manager: modeman.ModeManager, + private: bool, + parent: QWidget = None) -> None: self.private = private self.win_id = win_id self.tab_id = next(tab_id_gen) @@ -772,7 +806,7 @@ class AbstractTab(QWidget): self.data = TabData() self._layout = miscwidgets.WrapperLayout(self) - self._widget = None + self._widget = None # type: typing.Optional[QWebEngineView] self._progress = 0 self._has_ssl_errors = False self._mode_manager = mode_manager @@ -789,7 +823,7 @@ class AbstractTab(QWidget): self.predicted_navigation.connect(self._on_predicted_navigation) - def _set_widget(self, widget): + def _set_widget(self, widget: 'QWebEngineView') -> None: # pylint: disable=protected-access self._widget = widget self._layout.wrap(self, widget) @@ -807,10 +841,10 @@ class AbstractTab(QWidget): self._install_event_filter() self.zoom.set_default() - def _install_event_filter(self): + def _install_event_filter(self) -> None: raise NotImplementedError - def _set_load_status(self, val): + def _set_load_status(self, val: usertypes.LoadStatus) -> None: """Setter for load_status.""" if not isinstance(val, usertypes.LoadStatus): raise TypeError("Type {} is no LoadStatus member!".format(val)) @@ -818,11 +852,11 @@ class AbstractTab(QWidget): self._load_status = val self.load_status_changed.emit(val.name) - def event_target(self): + def event_target(self) -> QWidget: """Return the widget events should be sent to.""" raise NotImplementedError - def send_event(self, evt): + def send_event(self, evt: QEvent) -> None: """Send the given event to the underlying widget. The event will be sent via QApplication.postEvent. @@ -844,7 +878,7 @@ class AbstractTab(QWidget): QApplication.postEvent(recipient, evt) @pyqtSlot(QUrl) - def _on_predicted_navigation(self, url): + def _on_predicted_navigation(self, url: QUrl) -> None: """Adjust the title if we are going to visit a URL soon.""" qtutils.ensure_valid(url) url_string = url.toDisplayString() @@ -852,14 +886,14 @@ class AbstractTab(QWidget): self.title_changed.emit(url_string) @pyqtSlot(QUrl) - def _on_url_changed(self, url): + def _on_url_changed(self, url: QUrl) -> None: """Update title when URL has changed and no title is available.""" if url.isValid() and not self.title(): self.title_changed.emit(url.toDisplayString()) self.url_changed.emit(url) @pyqtSlot() - def _on_load_started(self): + def _on_load_started(self) -> None: self._progress = 0 self._has_ssl_errors = False self.data.viewing_source = False @@ -867,7 +901,10 @@ class AbstractTab(QWidget): self.load_started.emit() @pyqtSlot(usertypes.NavigationRequest) - def _on_navigation_request(self, navigation): + def _on_navigation_request( + self, + navigation: usertypes.NavigationRequest + ) -> None: """Handle common acceptNavigationRequest code.""" url = utils.elide(navigation.url.toDisplayString(), 100) log.webview.debug("navigation request: url {}, type {}, is_main_frame " @@ -891,7 +928,7 @@ class AbstractTab(QWidget): navigation.url.errorString())) navigation.accepted = False - def handle_auto_insert_mode(self, ok): + def handle_auto_insert_mode(self, ok: bool) -> None: """Handle `input.insert_mode.auto_load` after loading finished.""" if not config.val.input.insert_mode.auto_load or not ok: return @@ -900,7 +937,7 @@ class AbstractTab(QWidget): if cur_mode == usertypes.KeyMode.insert: return - def _auto_insert_mode_cb(elem): + def _auto_insert_mode_cb(elem: webelem.AbstractWebElement) -> None: """Called from JS after finding the focused element.""" if elem is None: log.webview.debug("No focused element!") @@ -912,7 +949,8 @@ class AbstractTab(QWidget): self.elements.find_focused(_auto_insert_mode_cb) @pyqtSlot(bool) - def _on_load_finished(self, ok): + def _on_load_finished(self, ok: bool) -> None: + assert self._widget is not None if sip.isdeleted(self._widget): # https://github.com/qutebrowser/qutebrowser/issues/3498 return @@ -943,46 +981,50 @@ class AbstractTab(QWidget): self.zoom.set_current() @pyqtSlot() - def _on_history_trigger(self): + def _on_history_trigger(self) -> None: """Emit add_history_item when triggered by backend-specific signal.""" raise NotImplementedError @pyqtSlot(int) - def _on_load_progress(self, perc): + def _on_load_progress(self, perc: int) -> None: self._progress = perc self.load_progress.emit(perc) - def url(self, requested=False): + def url(self, requested: bool = False) -> QUrl: raise NotImplementedError - def progress(self): + def progress(self) -> int: return self._progress - def load_status(self): + def load_status(self) -> usertypes.LoadStatus: return self._load_status - def _openurl_prepare(self, url, *, predict=True): + def _openurl_prepare(self, url: QUrl, *, predict: bool = True) -> None: qtutils.ensure_valid(url) if predict: self.predicted_navigation.emit(url) - def openurl(self, url, *, predict=True): + def openurl(self, url: QUrl, *, predict: bool = True) -> None: raise NotImplementedError - def reload(self, *, force=False): + def reload(self, *, force: bool = False) -> None: raise NotImplementedError - def stop(self): + def stop(self) -> None: raise NotImplementedError - def clear_ssl_errors(self): + def clear_ssl_errors(self) -> None: raise NotImplementedError - def key_press(self, key, modifier=Qt.NoModifier): + def key_press(self, + key: Qt.Key, + modifier: Qt.KeyboardModifier = Qt.NoModifier) -> None: """Send a fake key event to this tab.""" raise NotImplementedError - def dump_async(self, callback, *, plain=False): + def dump_async(self, + callback: typing.Callable[[str], None], *, + plain: bool = False) -> None: """Dump the current page's html asynchronously. The given callback will be called with the result when dumping is @@ -990,7 +1032,12 @@ class AbstractTab(QWidget): """ raise NotImplementedError - def run_js_async(self, code, callback=None, *, world=None): + def run_js_async( + self, + code: str, + callback: typing.Callable[[typing.Any], None] = None, *, + world: typing.Union[usertypes.JsWorld, int] = None + ) -> None: """Run javascript async. The given callback will be called with the result when running JS is @@ -1004,19 +1051,19 @@ class AbstractTab(QWidget): """ raise NotImplementedError - def shutdown(self): + def shutdown(self) -> None: raise NotImplementedError - def title(self): + def title(self) -> str: raise NotImplementedError - def icon(self): + def icon(self) -> None: raise NotImplementedError - def set_html(self, html, base_url=QUrl()): + def set_html(self, html: str, base_url: QUrl = QUrl()) -> None: raise NotImplementedError - def networkaccessmanager(self): + def networkaccessmanager(self) -> typing.Optional[QNetworkAccessManager]: """Get the QNetworkAccessManager for this tab. This is only implemented for QtWebKit. @@ -1024,7 +1071,7 @@ class AbstractTab(QWidget): """ raise NotImplementedError - def user_agent(self): + def user_agent(self) -> typing.Optional[str]: """Get the user agent for this tab. This is only implemented for QtWebKit. @@ -1032,13 +1079,16 @@ class AbstractTab(QWidget): """ raise NotImplementedError - def __repr__(self): + def __repr__(self) -> str: try: - url = utils.elide(self.url().toDisplayString(QUrl.EncodeUnicode), - 100) + qurl = self.url() + url = qurl.toDisplayString(QUrl.EncodeUnicode) # type: ignore except (AttributeError, RuntimeError) as exc: url = '<{}>'.format(exc.__class__.__name__) + else: + url = utils.elide(url, 100) return utils.get_repr(self, tab_id=self.tab_id, url=url) - def is_deleted(self): + def is_deleted(self) -> bool: + assert self._widget is not None return sip.isdeleted(self._widget) diff --git a/qutebrowser/browser/webengine/webenginetab.py b/qutebrowser/browser/webengine/webenginetab.py index 9945886fa..47bacd60e 100644 --- a/qutebrowser/browser/webengine/webenginetab.py +++ b/qutebrowser/browser/webengine/webenginetab.py @@ -522,6 +522,12 @@ class WebEngineHistory(browsertab.AbstractHistory): """QtWebEngine implementations related to page history.""" + def __len__(self): + return len(self._history) + + def __iter__(self): + return iter(self._history.items()) + def current_idx(self): return self._history.currentItemIndex() @@ -551,7 +557,7 @@ class WebEngineHistory(browsertab.AbstractHistory): return qtutils.serialize(self._history) def deserialize(self, data): - return qtutils.deserialize(data, self._history) + qtutils.deserialize(data, self._history) def load_items(self, items): if items: @@ -672,6 +678,7 @@ class WebEngineAudio(browsertab.AbstractAudio): def set_muted(self, muted: bool, override: bool = False) -> None: self._overridden = override + assert self._widget is not None page = self._widget.page() page.setAudioMuted(muted) diff --git a/qutebrowser/browser/webkit/webkittab.py b/qutebrowser/browser/webkit/webkittab.py index c791326ce..51e3f385e 100644 --- a/qutebrowser/browser/webkit/webkittab.py +++ b/qutebrowser/browser/webkit/webkittab.py @@ -513,6 +513,12 @@ class WebKitHistory(browsertab.AbstractHistory): """QtWebKit implementations related to page history.""" + def __len__(self): + return len(self._history) + + def __iter__(self): + return iter(self._history.items()) + def current_idx(self): return self._history.currentItemIndex() @@ -533,7 +539,7 @@ class WebKitHistory(browsertab.AbstractHistory): return qtutils.serialize(self._history) def deserialize(self, data): - return qtutils.deserialize(data, self._history) + qtutils.deserialize(data, self._history) def load_items(self, items): if items: