3

I'm currently working on a Python program which involves logging in with a username and password. I'm using the getpass module to accomplish this. My problem is that, in the case that getpass is unable to control echo, it spews the following into the terminal before continuing with the program.

Warning (from warnings module):
  File "C:\Python27\lib\getpass.py", line 92
    return fallback_getpass(prompt, stream)
GetPassWarning: Can not control echo on the terminal.
Warning: Password input may be echoed.

What I'd like to do is catch the warning and print my own custom message instead. The only code I can think of is the following which prevents the traceback from being shown but does not print the custom message at all.

import getpass

try:
    getpass.getpass("Password: ")
except getpass.GetPassWarning:
    print "Oh no!"

Output:

Warning: Password input may be echoed.
Password: 

I'd like to replace the text, Warning: Password input may be echoed., with my own message ideally.

GarethPW
  • 399
  • 1
  • 3
  • 11
  • This question: [How to disable python warnings](http://stackoverflow.com/q/14463277/4014959) discusses how to suppress warnings, and it contains a link to the docs for the `warnings` module. FWIW, here's the Python source code for [`getpass`](https://github.com/python-git/python/blob/master/Lib/getpass.py). – PM 2Ring Feb 15 '16 at 12:18
  • @PM2Ring Does that allow me to print a custom message prior to the prompt, though? – GarethPW Feb 15 '16 at 12:25
  • 1
    I don't think so (but I've never used the `warnings` module). I guess you could replace the imported `getpass.fallback_getpass` with your own function, although that sort of patching of standard module functions should really only be done as a last resort, IMHO. – PM 2Ring Feb 15 '16 at 12:33

2 Answers2

0

getpass uses the python builtin module warnings to show this warning message. You can filter/ignore them using various methods (Python Docs / PyMOTW).

You could catch the warning like so:

import getpass
import warnings

# the context manager resets the original
# filterwarnings after it has exited
with warnings.catch_warnings():
    # this will raise warnings of type (or inherited of type)
    # 'getpass.GetPassWarning' in the module 'getpass'
    warnings.filterwarnings(
        'error',
        category=getpass.GetPassWarning,
        module='getpass'
    )
    try:
        password = getpass.getpass('Password: ')
    except getpass.GetPassWarning:
        print 'Cannot get password on insecure platform'
Chive
  • 1
  • 1
  • 2
  • Unfortunately, it seems that both of these solutions have the same effect as my attempt. The traceback is blocked but the custom message is not displayed. :/ – GarethPW Feb 15 '16 at 12:34
  • I wasn't actually able to reproduce the `GetPassWarning` in the first place - on which platform are you getting it? – Chive Feb 15 '16 at 16:10
  • I'm using Python 2.7.11 on Windows 10. It's easiest to reproduce in IDLE. – GarethPW Feb 15 '16 at 18:53
  • I've updated the code - this works for me on both Windows 10 / py 2.7 / IDLE and OS X with py 2.7 – Chive Feb 15 '16 at 21:07
  • Well, the text now prints successfully, but the traceback and the `Warning: ...` text still remain. :/ – GarethPW Feb 16 '16 at 12:11
0

The method suggested by @PM2Ring appears to be the best solution I've found so far. For the sake of others passing by and for the purpose of answering the question, I'll wrap everything up in this post.

The following method overwrites the fallback_getpass function in the getpass module, allowing one to control exactly what happens in the event of the fallback being required. It's a little very hacky but it gets the job done.

import getpass

def custom_fallback(prompt="Password: ",stream=None): #Clone of parameters from getpass.fallback_getpass
    print "Custom message." #Custom message goes here
    return getpass._raw_input(prompt) #Use getpass' custom raw_input function for security

getpass.fallback_getpass = custom_fallback #Replace the getpass.fallback_getpass function with our equivalent

password = getpass.getpass("Password: ") #Prompt for password

Output:

Custom message.
Password: 
GarethPW
  • 399
  • 1
  • 3
  • 11