From 5cfab728b29610dd1ab43591b4ba66919621b8a2 Mon Sep 17 00:00:00 2001 From: Florian Bruhin Date: Wed, 19 Jul 2023 17:27:55 +0200 Subject: Improve handling of Optional with new PyQt stubs --- qutebrowser/browser/downloadview.py | 8 ++++---- qutebrowser/browser/inspector.py | 2 +- qutebrowser/browser/network/proxy.py | 2 +- qutebrowser/mainwindow/tabbedbrowser.py | 12 ++++++------ qutebrowser/misc/guiprocess.py | 2 +- qutebrowser/misc/ipc.py | 7 +++---- qutebrowser/misc/miscwidgets.py | 4 ++-- qutebrowser/utils/log.py | 2 +- qutebrowser/utils/qtutils.py | 31 ++++++++++++++++++++----------- qutebrowser/utils/standarddir.py | 2 +- qutebrowser/utils/urlutils.py | 6 +++--- 11 files changed, 43 insertions(+), 35 deletions(-) diff --git a/qutebrowser/browser/downloadview.py b/qutebrowser/browser/downloadview.py index 8f04f8d9d..02bba7a41 100644 --- a/qutebrowser/browser/downloadview.py +++ b/qutebrowser/browser/downloadview.py @@ -77,12 +77,12 @@ class DownloadView(QListView): self.clicked.connect(self.on_clicked) def __repr__(self): - model = self.model() + model = qtutils.add_optional(self.model()) count: Union[int, str] - if qtutils.is_not_none(model): - count = model.rowCount() - else: + if model is None: count = 'None' + else: + count = model.rowCount() return utils.get_repr(self, count=count) def _model(self) -> downloads.DownloadModel: diff --git a/qutebrowser/browser/inspector.py b/qutebrowser/browser/inspector.py index 15ff6c48a..ed0cae56f 100644 --- a/qutebrowser/browser/inspector.py +++ b/qutebrowser/browser/inspector.py @@ -163,7 +163,7 @@ class AbstractWebInspector(QWidget): self.shutdown() return elif position == Position.window: - self.setParent(qtutils.allow_none(None)) + self.setParent(qtutils.QT_NONE) self._load_state_geometry() else: self._splitter.set_inspector(self, position) diff --git a/qutebrowser/browser/network/proxy.py b/qutebrowser/browser/network/proxy.py index 0351adf0e..53aaac38c 100644 --- a/qutebrowser/browser/network/proxy.py +++ b/qutebrowser/browser/network/proxy.py @@ -51,7 +51,7 @@ def _warn_for_pac(): @pyqtSlot() def shutdown(): QNetworkProxyFactory.setApplicationProxyFactory( - qtutils.allow_none(None)) + qtutils.QT_NONE) class ProxyFactory(QNetworkProxyFactory): diff --git a/qutebrowser/mainwindow/tabbedbrowser.py b/qutebrowser/mainwindow/tabbedbrowser.py index 918434eae..e597c9efe 100644 --- a/qutebrowser/mainwindow/tabbedbrowser.py +++ b/qutebrowser/mainwindow/tabbedbrowser.py @@ -301,11 +301,11 @@ class TabbedBrowser(QWidget): """ widgets = [] for i in range(self.widget.count()): - widget = self.widget.widget(i) - if qtutils.is_not_none(widget): - widgets.append(widget) - else: + widget = qtutils.add_optional(self.widget.widget(i)) + if widget is None: log.webview.debug("Got None-widget in tabbedbrowser!") + else: + widgets.append(widget) return widgets def _update_window_title(self, field=None): @@ -872,8 +872,8 @@ class TabbedBrowser(QWidget): @pyqtSlot(usertypes.KeyMode) def on_mode_left(self, mode): """Give focus to current tab if command mode was left.""" - widget = self.widget.currentWidget() - if not qtutils.is_not_none(widget): + widget = qtutils.add_optional(self.widget.currentWidget()) + if widget is None: return if mode in [usertypes.KeyMode.command] + modeman.PROMPT_MODES: diff --git a/qutebrowser/misc/guiprocess.py b/qutebrowser/misc/guiprocess.py index 9f3b272f6..ac7290ef4 100644 --- a/qutebrowser/misc/guiprocess.py +++ b/qutebrowser/misc/guiprocess.py @@ -394,7 +394,7 @@ class GUIProcess(QObject): log.procs.debug("Starting process.") self._pre_start(cmd, args) self._proc.start( - qtutils.allow_none(self.resolved_cmd), + qtutils.remove_optional(self.resolved_cmd), args, ) self._post_start() diff --git a/qutebrowser/misc/ipc.py b/qutebrowser/misc/ipc.py index 05d9a6a33..b809394f1 100644 --- a/qutebrowser/misc/ipc.py +++ b/qutebrowser/misc/ipc.py @@ -259,10 +259,9 @@ class IPCServer(QObject): "still handling another one (0x{:x}).".format( id(self._socket))) return - socket = self._server.nextPendingConnection() - if not qtutils.is_not_none(socket): - log.ipc.debug( - "No new connection to handle.") + socket = qtutils.add_optional(self._server.nextPendingConnection()) + if socket is None: + log.ipc.debug("No new connection to handle.") return log.ipc.debug("Client connected (socket 0x{:x}).".format(id(socket))) self._socket = socket diff --git a/qutebrowser/misc/miscwidgets.py b/qutebrowser/misc/miscwidgets.py index f41cb9df4..1e90ac75a 100644 --- a/qutebrowser/misc/miscwidgets.py +++ b/qutebrowser/misc/miscwidgets.py @@ -244,10 +244,10 @@ class WrapperLayout(QLayout): if self._widget is None: return assert self._container is not None - self._widget.setParent(qtutils.allow_none(None)) + self._widget.setParent(qtutils.QT_NONE) self._widget.deleteLater() self._widget = None - self._container.setFocusProxy(qtutils.allow_none(None)) + self._container.setFocusProxy(qtutils.QT_NONE) class FullscreenNotification(QLabel): diff --git a/qutebrowser/utils/log.py b/qutebrowser/utils/log.py index 5ac150702..174a599ed 100644 --- a/qutebrowser/utils/log.py +++ b/qutebrowser/utils/log.py @@ -255,7 +255,7 @@ def disable_qt_msghandler() -> Iterator[None]: @contextlib.contextmanager -def py_warning_filter( +def py_warning_filter( action: Literal['default', 'error', 'ignore', 'always', 'module', 'once'] = 'ignore', **kwargs: Any, diff --git a/qutebrowser/utils/qtutils.py b/qutebrowser/utils/qtutils.py index 781e43f08..975503f85 100644 --- a/qutebrowser/utils/qtutils.py +++ b/qutebrowser/utils/qtutils.py @@ -651,20 +651,29 @@ def extract_enum_val(val: Union[sip.simplewrapper, int, enum.Enum]) -> int: return int(val) # type: ignore[call-overload] return val -T = TypeVar("T") +_T = TypeVar("_T") -def is_not_none(obj: Optional[T]) -> "TypeGuard[T]": - """Check if a Qt object is None. - PyQt6 marks things as Optional[...], but PyQt5 doesn't. - By using this function, we can use the same type hints for both. - """ - return obj is not None +if machinery.IS_QT5: + # On Qt 5, add/remove Optional where type annotations don't have it. + # Also we have a special QT_NONE, which (being Any) we can pass to functions + # where PyQt type hints claim that it's not allowed. + def remove_optional(obj: Optional[_T]) -> _T: + return cast(_T, obj) -if machinery.IS_QT5: - def allow_none(obj: Optional[T]) -> T: - return cast(T, obj) + def add_optional(obj: _T) -> Optional[_T]: + return cast(Optional[_T], obj) + + QT_NONE: Any = None else: - def allow_none(obj: Optional[T]) -> Optional[T]: + # On Qt 6, all those things are handled correctly by type annotations, so we + # have a no-op below. + + def remove_optional(obj: Optional[_T]) -> Optional[_T]: + return obj + + def add_optional(obj: Optional[_T]) -> Optional[_T]: return obj + + QT_NONE = None diff --git a/qutebrowser/utils/standarddir.py b/qutebrowser/utils/standarddir.py index 762125c11..a1fa414f7 100644 --- a/qutebrowser/utils/standarddir.py +++ b/qutebrowser/utils/standarddir.py @@ -65,7 +65,7 @@ def _unset_organization() -> Iterator[None]: qapp = QApplication.instance() if qapp is not None: orgname = qapp.organizationName() - qapp.setOrganizationName(qtutils.allow_none(None)) + qapp.setOrganizationName(qtutils.QT_NONE) try: yield finally: diff --git a/qutebrowser/utils/urlutils.py b/qutebrowser/utils/urlutils.py index b3f03b256..1bb035939 100644 --- a/qutebrowser/utils/urlutils.py +++ b/qutebrowser/utils/urlutils.py @@ -179,9 +179,9 @@ def _get_search_url(txt: str) -> QUrl: url = QUrl.fromUserInput(evaluated) else: url = QUrl.fromUserInput(config.val.url.searchengines[engine]) - url.setPath(qtutils.allow_none(None)) - url.setFragment(qtutils.allow_none(None)) - url.setQuery(qtutils.allow_none(None)) + url.setPath(qtutils.QT_NONE) + url.setFragment(qtutils.QT_NONE) + url.setQuery(qtutils.QT_NONE) qtutils.ensure_valid(url) return url -- cgit v1.2.3-54-g00ecf