summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorFlorian Bruhin <me@the-compiler.org>2022-06-14 15:26:36 +0200
committerFlorian Bruhin <me@the-compiler.org>2022-06-14 17:05:23 +0200
commit5616a99eff34f7074641d1391ed77d6b4b743529 (patch)
treea1f9c881981cb3a45b42f195e0cb694dcff6da54
parent733585a8cddde6cfd00b7f8afa28da61f7b2a091 (diff)
downloadqutebrowser-5616a99eff34f7074641d1391ed77d6b4b743529.tar.gz
qutebrowser-5616a99eff34f7074641d1391ed77d6b4b743529.zip
Add a MessageInfo data class
Preparation for #7246
-rw-r--r--qutebrowser/mainwindow/messageview.py34
-rw-r--r--qutebrowser/utils/message.py51
-rw-r--r--tests/helpers/messagemock.py24
-rw-r--r--tests/unit/mainwindow/test_messageview.py61
4 files changed, 100 insertions, 70 deletions
diff --git a/qutebrowser/mainwindow/messageview.py b/qutebrowser/mainwindow/messageview.py
index 58fcb3683..482d9f5e7 100644
--- a/qutebrowser/mainwindow/messageview.py
+++ b/qutebrowser/mainwindow/messageview.py
@@ -25,7 +25,7 @@ from PyQt5.QtCore import pyqtSlot, pyqtSignal, QTimer, Qt
from PyQt5.QtWidgets import QWidget, QVBoxLayout, QLabel, QSizePolicy
from qutebrowser.config import config, stylesheet
-from qutebrowser.utils import usertypes
+from qutebrowser.utils import usertypes, message
class Message(QLabel):
@@ -74,6 +74,15 @@ class Message(QLabel):
raise ValueError("Invalid level {!r}".format(level))
stylesheet.set_register(self, qss, update=False)
+ @classmethod
+ def from_info(cls, info: message.MessageInfo, parent: QWidget = None) -> "Message":
+ return cls(
+ level=info.level,
+ text=info.text,
+ replace=info.replace,
+ parent=parent,
+ )
+
class MessageView(QWidget):
@@ -119,31 +128,26 @@ class MessageView(QWidget):
self.hide()
self._clear_timer.stop()
- @pyqtSlot(usertypes.MessageLevel, str, str)
- def show_message(
- self,
- level: usertypes.MessageLevel,
- text: str,
- replace: str = None,
- ) -> None:
+ @pyqtSlot(message.MessageInfo)
+ def show_message(self, info: message.MessageInfo) -> None:
"""Show the given message with the given MessageLevel."""
- if text == self._last_text:
+ if info.text == self._last_text:
return
- if replace: # None -> QString() -> ''
- existing = [msg for msg in self._messages if msg.replace == replace]
+ if info.replace is not None:
+ existing = [msg for msg in self._messages if msg.replace == info.replace]
if existing:
assert len(existing) == 1, existing
- assert existing[0].level == level, (existing, level)
- existing[0].setText(text)
+ assert existing[0].level == info.level, (existing, info)
+ existing[0].setText(info.text)
self.update_geometry.emit()
return
- widget = Message(level, text, replace=replace, parent=self)
+ widget = Message.from_info(info)
self._vbox.addWidget(widget)
widget.show()
self._messages.append(widget)
- self._last_text = text
+ self._last_text = info.text
self.show()
self.update_geometry.emit()
if config.val.messages.timeout != 0:
diff --git a/qutebrowser/utils/message.py b/qutebrowser/utils/message.py
index c490aa4e8..48dcc9945 100644
--- a/qutebrowser/utils/message.py
+++ b/qutebrowser/utils/message.py
@@ -23,14 +23,23 @@
"""Message singleton so we don't have to define unneeded signals."""
+import dataclasses
import traceback
-from typing import Any, Callable, Iterable, List, Tuple, Union, Optional
+from typing import Any, Callable, Iterable, List, Union, Optional
from PyQt5.QtCore import pyqtSignal, pyqtBoundSignal, QObject
from qutebrowser.utils import usertypes, log
+@dataclasses.dataclass
+class MessageInfo:
+
+ level: usertypes.MessageLevel
+ text: str
+ replace: Optional[str] = None
+
+
def _log_stack(typ: str, stack: str) -> None:
"""Log the given message stacktrace.
@@ -58,7 +67,11 @@ def error(message: str, *, stack: str = None, replace: str = None) -> None:
typ = 'error (from exception)'
_log_stack(typ, stack)
log.message.error(message)
- global_bridge.show(usertypes.MessageLevel.error, message, replace)
+ global_bridge.show(
+ level=usertypes.MessageLevel.error,
+ text=message,
+ replace=replace,
+ )
def warning(message: str, *, replace: str = None) -> None:
@@ -70,7 +83,11 @@ def warning(message: str, *, replace: str = None) -> None:
"""
_log_stack('warning', ''.join(traceback.format_stack()))
log.message.warning(message)
- global_bridge.show(usertypes.MessageLevel.warning, message, replace)
+ global_bridge.show(
+ level=usertypes.MessageLevel.warning,
+ text=message,
+ replace=replace,
+ )
def info(message: str, *, replace: str = None) -> None:
@@ -81,7 +98,11 @@ def info(message: str, *, replace: str = None) -> None:
replace: Replace existing messages which are still being shown.
"""
log.message.info(message)
- global_bridge.show(usertypes.MessageLevel.info, message, replace)
+ global_bridge.show(
+ level=usertypes.MessageLevel.info,
+ text=message,
+ replace=replace,
+ )
def _build_question(title: str,
@@ -210,7 +231,7 @@ class GlobalMessageBridge(QObject):
mode_left: Emitted when a keymode was left in any window.
"""
- show_message = pyqtSignal(usertypes.MessageLevel, str, str)
+ show_message = pyqtSignal(MessageInfo)
prompt_done = pyqtSignal(usertypes.KeyMode)
ask_question = pyqtSignal(usertypes.Question, bool)
mode_left = pyqtSignal(usertypes.KeyMode)
@@ -219,7 +240,7 @@ class GlobalMessageBridge(QObject):
def __init__(self, parent: QObject = None) -> None:
super().__init__(parent)
self._connected = False
- self._cache: List[Tuple[usertypes.MessageLevel, str, Optional[str]]] = []
+ self._cache: List[MessageInfo] = []
def ask(self, question: usertypes.Question,
blocking: bool, *,
@@ -237,14 +258,18 @@ class GlobalMessageBridge(QObject):
"""
self.ask_question.emit(question, blocking)
- def show(self, level: usertypes.MessageLevel,
- text: str,
- replace: str = None) -> None:
+ def show(
+ self,
+ level: usertypes.MessageLevel,
+ text: str,
+ replace: str = None,
+ ) -> None:
"""Show the given message."""
+ info = MessageInfo(level=level, text=text, replace=replace)
if self._connected:
- self.show_message.emit(level, text, replace)
+ self.show_message.emit(info)
else:
- self._cache.append((level, text, replace))
+ self._cache.append(info)
def flush(self) -> None:
"""Flush messages which accumulated while no handler was connected.
@@ -253,8 +278,8 @@ class GlobalMessageBridge(QObject):
It needs to be called once the show_message signal is connected.
"""
self._connected = True
- for args in self._cache:
- self.show(*args)
+ for info in self._cache:
+ self.show(**dataclasses.asdict(info))
self._cache = []
diff --git a/tests/helpers/messagemock.py b/tests/helpers/messagemock.py
index 41f0bac64..6f020618b 100644
--- a/tests/helpers/messagemock.py
+++ b/tests/helpers/messagemock.py
@@ -20,7 +20,6 @@
"""pytest helper to monkeypatch the message module."""
import logging
-import dataclasses
import pytest
from PyQt5.QtCore import pyqtSlot, pyqtSignal, QObject
@@ -28,15 +27,6 @@ from PyQt5.QtCore import pyqtSlot, pyqtSignal, QObject
from qutebrowser.utils import usertypes, message
-@dataclasses.dataclass
-class Message:
-
- """Information about a shown message."""
-
- level: usertypes.MessageLevel
- text: str
-
-
class MessageMock(QObject):
"""Helper object for message_mock.
@@ -47,7 +37,7 @@ class MessageMock(QObject):
_logger: The logger to use for messages/questions.
"""
- got_message = pyqtSignal(usertypes.MessageLevel, str)
+ got_message = pyqtSignal(message.MessageInfo)
got_question = pyqtSignal(usertypes.Question)
def __init__(self, parent=None):
@@ -56,18 +46,18 @@ class MessageMock(QObject):
self.questions = []
self._logger = logging.getLogger('messagemock')
- @pyqtSlot(usertypes.MessageLevel, str)
- def _record_message(self, level, text):
- self.got_message.emit(level, text)
+ @pyqtSlot(message.MessageInfo)
+ def _record_message(self, info):
+ self.got_message.emit(info)
log_levels = {
usertypes.MessageLevel.error: logging.ERROR,
usertypes.MessageLevel.info: logging.INFO,
usertypes.MessageLevel.warning: logging.WARNING,
}
- log_level = log_levels[level]
+ log_level = log_levels[info.level]
- self._logger.log(log_level, text)
- self.messages.append(Message(level, text))
+ self._logger.log(log_level, info.text)
+ self.messages.append(info)
@pyqtSlot(usertypes.Question)
def _record_question(self, question):
diff --git a/tests/unit/mainwindow/test_messageview.py b/tests/unit/mainwindow/test_messageview.py
index 9e7627635..0f9dc9ea9 100644
--- a/tests/unit/mainwindow/test_messageview.py
+++ b/tests/unit/mainwindow/test_messageview.py
@@ -21,7 +21,7 @@ import pytest
from PyQt5.QtCore import Qt
from qutebrowser.mainwindow import messageview
-from qutebrowser.utils import usertypes
+from qutebrowser.utils import usertypes, message
@pytest.fixture
@@ -38,23 +38,23 @@ def view(qtbot, config_stub):
@pytest.mark.flaky # on macOS
def test_single_message(qtbot, view, level):
with qtbot.wait_exposed(view, timeout=5000):
- view.show_message(level, 'test')
+ view.show_message(message.MessageInfo(level, 'test'))
assert view._messages[0].isVisible()
def test_message_hiding(qtbot, view):
"""Messages should be hidden after the timer times out."""
with qtbot.wait_signal(view._clear_timer.timeout):
- view.show_message(usertypes.MessageLevel.info, 'test')
+ view.show_message(message.MessageInfo(usertypes.MessageLevel.info, 'test'))
assert not view._messages
def test_size_hint(view):
"""The message height should increase with more messages."""
- view.show_message(usertypes.MessageLevel.info, 'test1')
+ view.show_message(message.MessageInfo(usertypes.MessageLevel.info, 'test1'))
height1 = view.sizeHint().height()
assert height1 > 0
- view.show_message(usertypes.MessageLevel.info, 'test2')
+ view.show_message(message.MessageInfo(usertypes.MessageLevel.info, 'test2'))
height2 = view.sizeHint().height()
assert height2 == height1 * 2
@@ -62,7 +62,7 @@ def test_size_hint(view):
def test_word_wrap(view, qtbot):
"""A long message should be wrapped."""
with qtbot.wait_signal(view._clear_timer.timeout):
- view.show_message(usertypes.MessageLevel.info, 'short')
+ view.show_message(message.MessageInfo(usertypes.MessageLevel.info, 'short'))
height1 = view.sizeHint().height()
assert height1 > 0
@@ -72,7 +72,7 @@ def test_word_wrap(view, qtbot):
"for Odysseus, wise but ill fated, who suffers far from his friends on an "
"island deep in the sea.")
- view.show_message(usertypes.MessageLevel.info, text)
+ view.show_message(message.MessageInfo(usertypes.MessageLevel.info, text))
height2 = view.sizeHint().height()
assert height2 > height1
@@ -81,26 +81,26 @@ def test_word_wrap(view, qtbot):
def test_show_message_twice(view):
"""Show the same message twice -> only one should be shown."""
- view.show_message(usertypes.MessageLevel.info, 'test')
- view.show_message(usertypes.MessageLevel.info, 'test')
+ view.show_message(message.MessageInfo(usertypes.MessageLevel.info, 'test'))
+ view.show_message(message.MessageInfo(usertypes.MessageLevel.info, 'test'))
assert len(view._messages) == 1
def test_show_message_twice_after_first_disappears(qtbot, view):
"""Show the same message twice after the first is gone."""
with qtbot.wait_signal(view._clear_timer.timeout):
- view.show_message(usertypes.MessageLevel.info, 'test')
+ view.show_message(message.MessageInfo(usertypes.MessageLevel.info, 'test'))
# Just a sanity check
assert not view._messages
- view.show_message(usertypes.MessageLevel.info, 'test')
+ view.show_message(message.MessageInfo(usertypes.MessageLevel.info, 'test'))
assert len(view._messages) == 1
def test_changing_timer_with_messages_shown(qtbot, view, config_stub):
"""When we change messages.timeout, the timer should be restarted."""
config_stub.val.messages.timeout = 900000 # 15s
- view.show_message(usertypes.MessageLevel.info, 'test')
+ view.show_message(message.MessageInfo(usertypes.MessageLevel.info, 'test'))
with qtbot.wait_signal(view._clear_timer.timeout):
config_stub.val.messages.timeout = 100
@@ -113,8 +113,8 @@ def test_show_multiple_messages_longer(view, count, expected):
There is an upper maximum to avoid messages never disappearing.
"""
for message_number in range(1, count+1):
- view.show_message(usertypes.MessageLevel.info,
- 'test ' + str(message_number))
+ view.show_message(message.MessageInfo(
+ usertypes.MessageLevel.info, f"test {message_number}"))
assert view._clear_timer.interval() == expected
@@ -127,31 +127,40 @@ def test_show_multiple_messages_longer(view, count, expected):
])
def test_replaced_messages(view, replace1, replace2, length):
"""Show two stack=False messages which should replace each other."""
- view.show_message(usertypes.MessageLevel.info, 'test', replace=replace1)
- view.show_message(usertypes.MessageLevel.info, 'test 2', replace=replace2)
+ view.show_message(message.MessageInfo(
+ usertypes.MessageLevel.info, 'test', replace=replace1))
+ view.show_message(message.MessageInfo(
+ usertypes.MessageLevel.info, 'test 2', replace=replace2))
assert len(view._messages) == length
def test_replacing_different_severity(view):
- view.show_message(usertypes.MessageLevel.info, 'test', replace='testid')
+ view.show_message(message.MessageInfo(
+ usertypes.MessageLevel.info, 'test', replace='testid'))
with pytest.raises(AssertionError):
- view.show_message(usertypes.MessageLevel.error, 'test 2', replace='testid')
+ view.show_message(message.MessageInfo(
+ usertypes.MessageLevel.error, 'test 2', replace='testid'))
def test_replacing_changed_text(view):
- view.show_message(usertypes.MessageLevel.info, 'test', replace='testid')
- view.show_message(usertypes.MessageLevel.info, 'test 2')
- view.show_message(usertypes.MessageLevel.info, 'test 3', replace='testid')
+ view.show_message(message.MessageInfo(
+ usertypes.MessageLevel.info, 'test', replace='testid'))
+ view.show_message(message.MessageInfo(
+ usertypes.MessageLevel.info, 'test 2'))
+ view.show_message(message.MessageInfo(
+ usertypes.MessageLevel.info, 'test 3', replace='testid'))
assert len(view._messages) == 2
assert view._messages[0].text() == 'test 3'
assert view._messages[1].text() == 'test 2'
def test_replacing_geometry(qtbot, view):
- view.show_message(usertypes.MessageLevel.info, 'test', replace='testid')
+ view.show_message(message.MessageInfo(
+ usertypes.MessageLevel.info, 'test', replace='testid'))
with qtbot.wait_signal(view.update_geometry):
- view.show_message(usertypes.MessageLevel.info, 'test 2', replace='testid')
+ view.show_message(message.MessageInfo(
+ usertypes.MessageLevel.info, 'test 2', replace='testid'))
@pytest.mark.parametrize('button, count', [
@@ -162,7 +171,9 @@ def test_replacing_geometry(qtbot, view):
])
def test_click_messages(qtbot, view, button, count):
"""Messages should disappear when we click on them."""
- view.show_message(usertypes.MessageLevel.info, 'test mouse click')
- view.show_message(usertypes.MessageLevel.info, 'test mouse click 2')
+ view.show_message(message.MessageInfo(
+ usertypes.MessageLevel.info, 'test mouse click'))
+ view.show_message(message.MessageInfo(
+ usertypes.MessageLevel.info, 'test mouse click 2'))
qtbot.mousePress(view, button)
assert len(view._messages) == count