diff --git a/Lib/getpass.py b/Lib/getpass.py --- a/Lib/getpass.py +++ b/Lib/getpass.py @@ -38,27 +38,26 @@ Always restores terminal settings before returning. """ - fd = None - tty = None - try: - # Always try reading and writing directly on the tty first. - fd = os.open('/dev/tty', os.O_RDWR|os.O_NOCTTY) - tty = os.fdopen(fd, 'w+', 1) - input = tty - if not stream: - stream = tty - except EnvironmentError as e: - # If that fails, see if stdin can be controlled. + tty, exinst, passwd = None, None, None + # Something to break off if an error happens + while 1: try: - fd = sys.stdin.fileno() - except (AttributeError, ValueError): - passwd = fallback_getpass(prompt, stream) - input = sys.stdin - if not stream: - stream = sys.stderr + # Always try reading and writing directly on the tty first. + fd = os.open('/dev/tty', os.O_RDWR|os.O_NOCTTY) + input = tty = os.fdopen(fd, 'w+', 1) + if not stream: + stream = tty + except EnvironmentError: + # If that fails, see if stdin can be controlled; + # use generic fallback implementation as last resort + try: + fd = sys.stdin.fileno() + except: + break + input = sys.stdin + if not stream: + stream = sys.stderr - if fd is not None: - passwd = None try: old = termios.tcgetattr(fd) # a copy to save new = old[:] @@ -68,21 +67,29 @@ tcsetattr_flags |= termios.TCSASOFT try: termios.tcsetattr(fd, tcsetattr_flags, new) - passwd = _raw_input(prompt, stream, input=input) + passwd = _user_input(prompt, stream, input, echooff=True) + except Exception as e: + exinst = e finally: termios.tcsetattr(fd, tcsetattr_flags, old) - stream.flush() # issue7208 - except termios.error as e: + stream.flush() # issue7208 (7246) + except Exception as e: if passwd is not None: - # _raw_input succeeded. The final tcsetattr failed. Reraise - # instead of leaving the terminal in an unknown state. - raise - # We can't control the tty or stdin. Give up and use normal IO. - # fallback_getpass() raises an appropriate warning. - del input, tty # clean up unused file objects before blocking - passwd = fallback_getpass(prompt, stream) + # _user_input succeeded, but the final tcsetattr failed. + # Reraise the termios.error instead of leaving the terminal + # in an unknown state. + exinst = e + break - stream.write('\n') + if not exinst and passwd is None: + # We can't control the tty or stdin. Give up and use normal IO. + # fallback_getpass() raises an appropriate warning. + passwd = fallback_getpass(prompt, stream) + + if tty: + tty.close() + if exinst: + raise exinst return passwd @@ -115,21 +122,19 @@ if not stream: stream = sys.stderr print("Warning: Password input may be echoed.", file=stream) - return _raw_input(prompt, stream) + return _user_input(prompt, stream, sys.stdin, echooff=False) -def _raw_input(prompt="", stream=None, input=None): +def _user_input(prompt, stream, input, echooff=False): # This doesn't save the string in the GNU readline history. - if not stream: - stream = sys.stderr - if not input: - input = sys.stdin prompt = str(prompt) if prompt: stream.write(prompt) stream.flush() # NOTE: The Python C API calls flockfile() (and unlock) during readline. line = input.readline() + if echooff: + stream.write('\n') if not line: raise EOFError if line[-1] == '\n':