diff options
Diffstat (limited to 'Lib/getpass.py')
-rw-r--r-- | Lib/getpass.py | 105 |
1 files changed, 75 insertions, 30 deletions
diff --git a/Lib/getpass.py b/Lib/getpass.py index 3331c97..857188f 100644 --- a/Lib/getpass.py +++ b/Lib/getpass.py @@ -1,7 +1,10 @@ """Utilities to get a password and/or the current user name. -getpass(prompt) - prompt for a password, with echo turned off -getuser() - get the user name from the environment or password database +getpass(prompt[, stream]) - Prompt for a password, with echo turned off. +getuser() - Get the user name from the environment or password database. + +GetPassWarning - This UserWarning is issued when getpass() cannot prevent + echoing of the password contents while reading. On Windows, the msvcrt module will be used. On the Mac EasyDialogs.AskPassword is used, if available. @@ -10,34 +13,70 @@ On the Mac EasyDialogs.AskPassword is used, if available. # Authors: Piers Lauder (original) # Guido van Rossum (Windows support and cleanup) +# Gregory P. Smith (tty support & GetPassWarning) -import sys +import os, sys, warnings -__all__ = ["getpass","getuser"] +__all__ = ["getpass","getuser","GetPassWarning"] -def unix_getpass(prompt='Password: ', stream=None): - """Prompt for a password, with echo turned off. - The prompt is written on stream, by default stdout. - Restore terminal settings at end. - """ - if stream is None: - stream = sys.stdout +class GetPassWarning(UserWarning): pass - try: - fd = sys.stdin.fileno() - except: - return default_getpass(prompt) - old = termios.tcgetattr(fd) # a copy to save - new = old[:] +def unix_getpass(prompt='Password: ', stream=None): + """Prompt for a password, with echo turned off. - new[3] = new[3] & ~termios.ECHO # 3 == 'lflags' + Args: + prompt: Written on stream to ask for the input. Default: 'Password: ' + stream: A writable file object to display the prompt. Defaults to + the tty. If no tty is available defaults to sys.stderr. + Returns: + The seKr3t input. + Raises: + EOFError: If our input tty or stdin was closed. + GetPassWarning: When we were unable to turn echo off on the input. + + Always restores terminal settings before returning. + """ + fd = None + tty = None try: - termios.tcsetattr(fd, termios.TCSADRAIN, new) - passwd = _raw_input(prompt, stream) - finally: - termios.tcsetattr(fd, termios.TCSADRAIN, old) + # 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. + try: + fd = sys.stdin.fileno() + except: + passwd = fallback_getpass(prompt, stream) + 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[:] + new[3] &= ~termios.ECHO # 3 == 'lflags' + try: + termios.tcsetattr(fd, termios.TCSADRAIN, new) + passwd = _raw_input(prompt, stream, input=input) + finally: + termios.tcsetattr(fd, termios.TCSADRAIN, old) + except termios.error 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) stream.write('\n') return passwd @@ -46,7 +85,7 @@ def unix_getpass(prompt='Password: ', stream=None): def win_getpass(prompt='Password: ', stream=None): """Prompt for password with echo off, using Windows getch().""" if sys.stdin is not sys.__stdin__: - return default_getpass(prompt, stream) + return fallback_getpass(prompt, stream) import msvcrt for c in prompt: msvcrt.putwch(c) @@ -66,20 +105,26 @@ def win_getpass(prompt='Password: ', stream=None): return pw -def default_getpass(prompt='Password: ', stream=None): - print("Warning: Problem with getpass. Passwords may be echoed.", file=sys.stderr) +def fallback_getpass(prompt='Password: ', stream=None): + warnings.warn("Can not control echo on the terminal.", GetPassWarning, + stacklevel=2) + if not stream: + stream = sys.stderr + print("Warning: Password input may be echoed.", file=stream) return _raw_input(prompt, stream) -def _raw_input(prompt="", stream=None): +def _raw_input(prompt="", stream=None, input=None): # This doesn't save the string in the GNU readline history. - if stream is None: - stream = sys.stdout + if not stream: + stream = sys.stderr + if not input: + input = sys.stdin prompt = str(prompt) if prompt: stream.write(prompt) stream.flush() - line = sys.stdin.readline() + line = input.readline() if not line: raise EOFError if line[-1] == '\n': @@ -119,7 +164,7 @@ except (ImportError, AttributeError): try: from EasyDialogs import AskPassword except ImportError: - getpass = default_getpass + getpass = fallback_getpass else: getpass = AskPassword else: |