public inbox for gdb-prs@sourceware.org
help / color / mirror / Atom feed
* [Bug tui/26743] New: Unable to accept user `input()` in Python in TUI mode
@ 2020-10-16 17:48 mhov at undo dot io
  0 siblings, 0 replies; only message in thread
From: mhov at undo dot io @ 2020-10-16 17:48 UTC (permalink / raw)
  To: gdb-prs

https://sourceware.org/bugzilla/show_bug.cgi?id=26743

            Bug ID: 26743
           Summary: Unable to accept user `input()` in Python in TUI mode
           Product: gdb
           Version: 9.2
            Status: UNCONFIRMED
          Severity: normal
          Priority: P2
         Component: tui
          Assignee: unassigned at sourceware dot org
          Reporter: mhov at undo dot io
  Target Milestone: ---

Reading `input()' from Python 2.6/3.6 while in TUI mode does not work
well (tested in version 8.1, 9.2 and 11.0.50.20201015-git).

This is preventing me from implementing a user command in Python, but it
also causes issues for existing commands like `explore'.

Soucing and running the following command, which does an `input()' call
from a user defined python command, illustrate the problem on my
machine:

┌────
│ import gdb
│ 
│ class MyCommand(gdb.Command):
│     """Example command taking input."""
│ 
│     def __init__(self):
│         super(MyCommand, self).__init__("my-command", gdb.COMMAND_USER)
│ 
│     def invoke(self, _args, _from_tty):
│         some_input = input("What's your input? ")
│         print("Your input was: " + some_input)
│ 
│ MyCommand()
└────

The command runs as expected in normal CLI mode.

When the command runs in TUI mode, the command does not appear to accept
any input. Occasionally I can get GDB to quit the command by repeatedly
pressing C-c, C-d and Return, but mostly GDB appears to hand at this
point. GDB seems to be stuck in the `input' Python builtin, looping in
`_textiowrapper_readline' on every keypress:

┌────
│ 0  in _textiowrapper_readline of ./Python-3.6.10/Modules/_io/textio.c:1852
│ 1  in _io_TextIOWrapper_readline_impl of
./Python-3.6.10/Modules/_io/textio.c:1954
│ 2  in _io_TextIOWrapper_readline of
./Python-3.6.10/Modules/_io/clinic/textio.c.h:270
│ 3  in PyCFunction_Call of ./Python-3.6.10/Objects/methodobject.c:126
│ 4  in PyObject_Call of ./Python-3.6.10/Objects/abstract.c:2261
│ 5  in PyFile_GetLine of ./Python-3.6.10/Objects/fileobject.c:76
│ 6  in builtin_input_impl of ./Python-3.6.10/Python/bltinmodule.c:2045
│ 7  in builtin_input of ./Python-3.6.10/Python/clinic/bltinmodule.c.h:559
└────

When running in normal CLI mode, `_textiowrapper_readline' consistently
terminates when reading a `"\n"' character following a Return keypress.
The function relies on `_PyIO_find_line_ending' to break out of the loop
by deducing that it has found the end of the line.

In TUI mode I always see `"\r"' characters being read and this does not
seem to ever satisfy `_PyIO_find_line_ending', causing an infinite loop.
I have confirmed that `ncurses' clears the `ICRNL' termios input mode
attribute when GDB switches to TUI. So Python's `input()' doesn't seem
to cope with this. Maybe it could if Python was appropriately notified?

I am able to work around this by temporarily resetting the terminal
attributes so that a Return is automatically translated to a `"\n"':

┌────
│ import sys
│ import termios
│ import contextlib
│ 
│ import gdb
│ 
│ @contextlib.contextmanager
│ def terminal_for_input():
│     """
│     Context manager for running code with terminal attributes that are
│     suitable for accepting input from the user.
│     """
│     attr = termios.tcgetattr(sys.stdin.fileno())
│     try:
│         input_attr = attr.copy()
│ 
│         # Python's ``input`` expects "\n" line delimiter.
│         input_attr[0] |= termios.ICRNL
│         # Convenience: CR on pressing RETURN.
│         input_attr[1] |= termios.ONLCR
│         # Convenience: Input echo.
│         input_attr[3] |= termios.ECHO
│         termios.tcsetattr(sys.stdin.fileno(), termios.TCSANOW, input_attr)
│         yield None
│     finally:
│         termios.tcsetattr(sys.stdin.fileno(), termios.TCSANOW, attr)
│ 
│ class MyCommand(gdb.Command):
│     """
│     Example command taking input.
│     """
│ 
│     def __init__(self):
│         super(MyCommand, self).__init__("my-command", gdb.COMMAND_USER)
│ 
│     def invoke(self, _args, _from_tty):
│         with terminal_for_input():
│             some_input = input("What's your input? ")
│         print("Your input was: " + some_input)
│ 
│ MyCommand()
└────

It might also be possible to work around this by temporarily
reconfiguring `sys.stdin''s `newline' parameter to match the terminal
settings of TUI instead of messing with the terminal, but I can't use
Python 3.7 yet
(<https://docs.python.org/3/library/io.html#io.TextIOWrapper.reconfigure>).
That would also not echo the input.

I am not too familiar with CPython or how it's joined with GDB and
ncurses, so

-- 
You are receiving this mail because:
You are on the CC list for the bug.

^ permalink raw reply	[flat|nested] only message in thread

only message in thread, other threads:[~2020-10-16 17:48 UTC | newest]

Thread overview: (only message) (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2020-10-16 17:48 [Bug tui/26743] New: Unable to accept user `input()` in Python in TUI mode mhov at undo dot io

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).