Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

RFC single key press to end "show output" #673

Closed
wants to merge 7 commits into from
66 changes: 66 additions & 0 deletions nonbufferedconsole.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
"""
Evaluation script for non-buffered console operation and the portability
of the implementation across Python supported platforms. Can be executed
on any computer that has Python available. Does not depend on 3rd party
libraries, exclusively uses core features.
"""

_nbc_use_input = True
_nbc_use_getch = False
_nbc_use_select = False

import sys
if sys.platform in ("emscripten", "wasi"):
pass
elif sys.platform in ("win32",):
import msvcrt
_nbc_use_input = False
_nbc_use_getch = True
else:
import select
import termios
import tty
_nbc_use_input = False
_nbc_use_select = True

class NonBufferedConsole(object):

def __init__(self):
pass

def __enter__(self):
if _nbc_use_select:
self.prev_settings = termios.tcgetattr(sys.stdin)
tty.setcbreak(sys.stdin.fileno())
return self

def __exit__(self, type, value, traceback):
if _nbc_use_select:
termios.tcsetattr(sys.stdin, termios.TCSADRAIN, self.prev_settings)

def get_data(self):
if _nbc_use_getch:
c = msvcrt.getch()
if c in ('\x00', '\xe0'):
c = msvcrt.getch()
return c

if _nbc_use_select:
rset, _, _ = select.select([sys.stdin], [], [], None)
if sys.stdin in rset:
return sys.stdin.read(1)
return None

# The Python input() call strictly speaking is not a
# terminal in non-buffered mode and without a prompt.
# But supporting this fallback here is most appropriate
# and simplifies call sites.
if _nbc_use_input:
input("Hit Enter to return:")
return None

if __name__ == "__main__":
print("waiting for key press")
with NonBufferedConsole() as nbc:
key = nbc.get_data()
print("key press seen")
11 changes: 8 additions & 3 deletions pudb/debugger.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@

import urwid

from pudb.lowlevel import decode_lines, ui_log
from pudb.lowlevel import decode_lines, NonBufferedConsole, ui_log
from pudb.settings import get_save_config_path, load_config, save_config


Expand Down Expand Up @@ -769,10 +769,15 @@ def __init__(self, screen):

def __enter__(self):
self.screen.stop()
return self

def __exit__(self, exc_type, exc_value, exc_traceback):
self.screen.start()

def press_key_to_return(self):
with NonBufferedConsole() as nbc:
key = nbc.get_data()


class DebuggerUI(FrameVarInfoKeeper):
# {{{ constructor
Expand Down Expand Up @@ -2082,8 +2087,8 @@ def shrink_sidebar(w, size, key):
# {{{ top-level listeners

def show_output(w, size, key):
with StoppedScreen(self.screen):
input("Hit Enter to return:")
with StoppedScreen(self.screen) as s:
s.press_key_to_return()

def reload_breakpoints_and_redisplay():
reload_breakpoints()
Expand Down
69 changes: 69 additions & 0 deletions pudb/lowlevel.py
Original file line number Diff line number Diff line change
Expand Up @@ -283,4 +283,73 @@ def decode_lines(lines):

# }}}


# {{{ non-buffered console

# Local platform dependent helper to get single key presses from a
# terminal in unbuffered mode. Eliminates the necessity to press ENTER
# before other input also becomes available. Also avoids the accumulation
# of prompts on the screen as was the case with Python's input() call.
# Is used in situations where urwid is disabled and curses calls are
# not available.

_nbc_use_input = True
_nbc_use_getch = False
_nbc_use_select = False
if sys.platform in ("emscripten", "wasi"):
pass
elif sys.platform in ("win32",):
import msvcrt
_nbc_use_input = False
_nbc_use_getch = True
else:
import select
import termios
import tty
_nbc_use_input = False
_nbc_use_select = True


class NonBufferedConsole(object):

def __init__(self):
pass

def __enter__(self):
if _nbc_use_select:
self.prev_settings = termios.tcgetattr(sys.stdin)
tty.setcbreak(sys.stdin.fileno())
return self

def __exit__(self, type, value, traceback):
if _nbc_use_select:
termios.tcsetattr(sys.stdin, termios.TCSADRAIN, self.prev_settings)

def get_data(self):
if _nbc_use_getch:
c = msvcrt.getch()
if c in ('\x00', '\xe0'):
c = msvcrt.getch()
return c

if _nbc_use_select:
rset, _, _ = select.select([sys.stdin], [], [], None)
if sys.stdin in rset:
return sys.stdin.read(1)
return None

# Strictly speaking putting the fallback here which requires
# pressing ENTER is not correct, this is the "non buffered"
# console support code. But it simplifies call sites. And is
# easy to tell by users because a prompt is provided. This is
# the most portable approach, and backwards compatible with
# earlier PuDB releases. It's a most appropriate default for
# otherwise unsupported platforms. Or when users choose to
# not accept single key presses, or keys other than ENTER.
if _nbc_use_input:
input("Hit Enter to return:")
return None

# }}}

# vim: foldmethod=marker