On Mon, Aug 16, 2021 at 5:20 PM Niteesh G. S. <niteesh...@gmail.com> wrote:
> > > On Tue, Aug 17, 2021 at 1:14 AM John Snow <js...@redhat.com> wrote: > >> >> >> On Fri, Jul 30, 2021 at 4:19 PM G S Niteesh Babu <niteesh...@gmail.com> >> wrote: >> >>> Add syntax highlighting for the incoming and outgoing QMP messages. >>> This is achieved using the pygments module which was added in a >>> previous commit. >>> >>> The current implementation is a really simple one which doesn't >>> allow for any configuration. In future this has to be improved >>> to allow for easier theme config using an external config of >>> some sort. >>> >>> Signed-off-by: G S Niteesh Babu <niteesh...@gmail.com> >>> --- >>> python/qemu/aqmp/aqmp_tui.py | 52 +++++++++++++++++++++++++++--------- >>> 1 file changed, 40 insertions(+), 12 deletions(-) >>> >>> diff --git a/python/qemu/aqmp/aqmp_tui.py b/python/qemu/aqmp/aqmp_tui.py >>> index ab9ada793a..0d5ec62cb7 100644 >>> --- a/python/qemu/aqmp/aqmp_tui.py >>> +++ b/python/qemu/aqmp/aqmp_tui.py >>> @@ -19,6 +19,8 @@ >>> Union, >>> ) >>> >>> +from pygments import lexers >>> +from pygments import token as Token >>> import urwid >>> import urwid_readline >>> >>> @@ -35,6 +37,22 @@ >>> LOGGER = logging.getLogger() >>> >>> >>> +palette = [ >>> + (Token.Punctuation, '', '', '', 'h15,bold', 'g7'), >>> + (Token.Text, '', '', '', '', 'g7'), >>> + (Token.Name.Tag, '', '', '', 'bold,#f88', 'g7'), >>> + (Token.Literal.Number.Integer, '', '', '', '#fa0', 'g7'), >>> + (Token.Literal.String.Double, '', '', '', '#6f6', 'g7'), >>> + (Token.Keyword.Constant, '', '', '', '#6af', 'g7'), >>> + ('DEBUG', '', '', '', '#ddf', 'g7'), >>> + ('INFO', '', '', '', 'g100', 'g7'), >>> + ('WARNING', '', '', '', '#ff6', 'g7'), >>> + ('ERROR', '', '', '', '#a00', 'g7'), >>> + ('CRITICAL', '', '', '', '#a00', 'g7'), >>> + ('background', '', 'black', '', '', 'g7'), >>> +] >>> + >>> + >>> def format_json(msg: str) -> str: >>> """ >>> Formats given multiline JSON message into a single line message. >>> @@ -57,17 +75,14 @@ def __init__(self, address: Union[str, Tuple[str, >>> int]]) -> None: >>> self.aloop: Optional[Any] = None # FIXME: Use more concrete >>> type. >>> super().__init__() >>> >>> - def add_to_history(self, msg: str) -> None: >>> - urwid.emit_signal(self, UPDATE_MSG, msg) >>> + def add_to_history(self, msg: str, level: Optional[str] = None) -> >>> None: >>> + urwid.emit_signal(self, UPDATE_MSG, msg, level) >>> >>> def _cb_outbound(self, msg: Message) -> Message: >>> # FIXME: I think the ideal way to omit these messages during >>> in-TUI >>> - # logging will be to add a filter to the logger. We can use >>> regex to >>> - # filter out messages starting with 'Request:' or 'Response:' >>> but I >>> - # think a better approach will be encapsulate the message in an >>> object >>> - # and filter based on the object. Encapsulation of the message >>> will >>> - # also be necessary when we want different formatting of >>> messages >>> - # inside TUI. >>> + # logging will be to add a filter to the logger. We can use >>> + # regex/startswith to filter out messages starting with >>> 'Request:' or >>> + # 'Response:'. If possible please suggest other ideas. >>> >> >> We'll want to fix this up earlier in the series -- the finished version >> of your series shouldn't have FIXME comments, and later patches in the >> series shouldn't edit comments added earlier. We chatted on IRC about the >> right strategy for filtering log messages, so I think V4 should have a >> strategy in place to avoid this comment, right? >> > Yes, this has been addressed in the v4. > We avoid logging messages when we log the messages to the TUI screen. This > is done by checking if TUILogHandler is installed on the logger or not. > >> >> >>> handler = LOGGER.handlers[0] >>> if not isinstance(handler, TUILogHandler): >>> LOGGER.debug('Request: %s', str(msg)) >>> @@ -156,6 +171,9 @@ def _get_formatted_address(self) -> str: >>> self._set_status('Server shutdown') >>> >>> def run(self, debug: bool = False) -> None: >>> + screen = urwid.raw_display.Screen() >>> + screen.set_terminal_properties(256) >>> >> >> I'm not sure if this will work everywhere, but ... it's fine for now, I >> assume. >> > I'll add a note here. > >> + >>> self.aloop = asyncio.get_event_loop() >>> self.aloop.set_debug(debug) >>> >>> @@ -167,6 +185,8 @@ def run(self, debug: bool = False) -> None: >>> event_loop = urwid.AsyncioEventLoop(loop=self.aloop) >>> main_loop = urwid.MainLoop(urwid.AttrMap(self.window, >>> 'background'), >>> unhandled_input=self.unhandled_input, >>> + screen=screen, >>> + palette=palette, >>> handle_mouse=True, >>> event_loop=event_loop) >>> >>> @@ -251,7 +271,8 @@ def __init__(self, master: App) -> None: >>> self.history = urwid.SimpleFocusListWalker([]) >>> super().__init__(self.history) >>> >>> - def add_to_history(self, history: str) -> None: >>> + def add_to_history(self, >>> + history: Union[str, List[Tuple[str, str]]]) -> >>> None: >>> self.history.append(urwid.Text(history)) >>> if self.history: >>> self.history.set_focus(len(self.history) - 1) >>> @@ -271,8 +292,15 @@ def __init__(self, master: App) -> None: >>> super().__init__(self.body) >>> urwid.connect_signal(self.master, UPDATE_MSG, >>> self.cb_add_to_history) >>> >>> - def cb_add_to_history(self, msg: str) -> None: >>> - self.history.add_to_history(msg) >>> + def cb_add_to_history(self, msg: str, level: Optional[str] = None) >>> -> None: >>> + formatted = [] >>> + if level: >>> + formatted.append((level, msg)) >>> + else: >>> + lexer = lexers.JsonLexer() # pylint: disable=no-member >>> + for token in lexer.get_tokens(msg): >>> + formatted.append(token) >>> + self.history.add_to_history(formatted) >>> >> >> This looks like it's conflating having a debugging level with receiving a >> message that's already formatted. >> >> i.e., the code here assumes that if it receives a level, it is ALSO >> already formatted. That smells like trouble -- but I think there are some >> changes to the logging and rendering you had planned for V4 anyway, I >> believe? >> > Why do you think it is trouble? > When a level is provided, it is most probably going to be text messages or > QMP messages with text. In either case, there is no formatting to be done. > In the second case, we don't have a JSON formattor that can format > a JSON message along with text without breaking. So I thought it is better > to leave the formatting to the user and just use the level to provide > syntax highlighting. > > Because it's an indirect type inference -- you are *indirectly* measuring whether or not a message has been formatted yet. Using the presence or absence of 'level' is an indirect way of proving the condition; are there not more direct ways? Talking on IRC, I see that you have plans for an abstracted Item view that might make this a null point, though -- so I'll drop this line of interrogation for now. Thanks!