A Better "getpass()" Function for Unix
It looks like my version of
getpass (using the tty) is actually the default in the standard library version of
getpass as of python 2.6. If you are using python 2.6 or greater, use the standard library version as it also falls back to stdin if using the tty fails.
What is This About?
I've had this problem in the past and it cropped up again today and pissed me off once again so I decided to tackle the issue. The issue is the Python standard library version of
getpass() from the
getpass module. It works fine in terms of really basic usage, but the big problem comes when you want to write a script that reads from
STDIN and calls
getpass(). Since the built in
getpass() reads from
STDIN, it ends up borking your script. Now, for anyone who has ever used
ssh before (and I assume if you are reading this, you have), you will have noticed that the
ssh client gets around this very issue since you can pipe info to
ssh and enter your password when prompted. This is due to the fact that
ssh client reads and writes directly to your terminal, rather than reading and writing from
STDOUT. This is exactly what I've done with my version of
getpass(). I used most of the code used in the standard library
getpass(), but made it write the prompt and read directly from a connection to the terminal itself. This has been tested on linux (Gentoo) and FreeBSD 6.2 and it works fine on both.
Here is my simple version of the standard library
getpass(). Of course, this will only work on a *nix platform.
import termios , os , sys def getPass(prompt='Password: '): """ Writes the password prompt and reads from terminal directly, just like the openssh client """ if not os.isatty(sys.stderr.fileno()): raise IOError , 'You can only call getPass() from a tty' ttyname = os.ttyname(sys.stderr.fileno()) ttyh = open(ttyname , 'w+b') ttyfd = ttyh.fileno() old = termios.tcgetattr(ttyfd) # a copy to save new = old[:] new = new & ~termios.ECHO # 3 == 'lflags' try: termios.tcsetattr(ttyfd , termios.TCSADRAIN, new) ttyh.write(prompt) ttyh.flush() passwd = ttyh.readline().strip() finally: termios.tcsetattr(ttyfd , termios.TCSADRAIN, old) ttyh.close() sys.stdout.write('\n') return passwd