Major improvements:

* Default to using /dev/tty for the password prompt and input before
  falling back to sys.stdin and sys.stderr.
* Use sys.stderr instead of sys.stdout.
* print the 'password may be echoed' warning to stream used to display
  the prompt rather than always sys.stderr.
* warn() with GetPassWarning when input may be echoed.
This commit is contained in:
Gregory P. Smith 2008-04-22 08:08:41 +00:00
parent 8e0319d82a
commit 19b4411181
2 changed files with 93 additions and 35 deletions

View File

@ -14,13 +14,29 @@ The :mod:`getpass` module provides two functions:
Prompt the user for a password without echoing. The user is prompted using the
string *prompt*, which defaults to ``'Password: '``. On Unix, the prompt is
written to the file-like object *stream*, which defaults to ``sys.stdout`` (this
argument is ignored on Windows).
written to the file-like object *stream*. *stream* defaults to the
controlling terminal (/dev/tty) or if that is unavailable to ``sys.stderr``
(this argument is ignored on Windows).
If echo free input is unavailable getpass() falls back to printing
a warning message to *stream* and reading from ``sys.stdin`` and
issuing a :exc:`GetPassWarning`.
Availability: Macintosh, Unix, Windows.
.. versionchanged:: 2.5
The *stream* parameter was added.
.. versionchanged:: 2.6
On Unix it defaults to using /dev/tty before falling back
to ``sys.stdin`` and ``sys.stderr``.
.. note::
If you call getpass from within idle, the input may be done in the
terminal you launched idle from rather than the idle window itself.
.. exception:: GetPassWarning
A :exc:`UserWarning` subclass issued when password input may be echoed.
.. function:: getuser()

View File

@ -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,38 +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","GetPassWarning"]
class GetPassWarning(UserWarning): pass
__all__ = ["getpass","getuser"]
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.
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.
"""
if stream is None:
stream = sys.stdout
if not sys.stdin.isatty():
print >>sys.stderr, "Warning: sys.stdin is not a tty."
return default_getpass(prompt)
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, e:
# If that fails, see if stdin can be controlled.
try:
fd = sys.stdin.fileno()
except:
return default_getpass(prompt)
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] = new[3] & ~termios.ECHO # 3 == 'lflags'
new[3] &= ~termios.ECHO # 3 == 'lflags'
try:
termios.tcsetattr(fd, termios.TCSADRAIN, new)
passwd = _raw_input(prompt, stream)
passwd = _raw_input(prompt, stream, input=input)
finally:
termios.tcsetattr(fd, termios.TCSADRAIN, old)
except termios.error, 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
@ -50,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.putch(c)
@ -70,20 +105,27 @@ def win_getpass(prompt='Password: ', stream=None):
return pw
def default_getpass(prompt='Password: ', stream=None):
print >>sys.stderr, "Warning: Problem with getpass. Passwords may be echoed."
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 >>stream, "Warning: Password input may be echoed."
return _raw_input(prompt, stream)
def _raw_input(prompt="", stream=None):
def _raw_input(prompt="", stream=None, input=None):
# A raw_input() replacement that 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)
line = sys.stdin.readline()
stream.flush()
line = input.readline()
if not line:
raise EOFError
if line[-1] == '\n':
@ -123,7 +165,7 @@ except (ImportError, AttributeError):
try:
from EasyDialogs import AskPassword
except ImportError:
getpass = default_getpass
getpass = fallback_getpass
else:
getpass = AskPassword
else: