Python click option with a prompt AND default hidden

6.1k Views Asked by At

The snippet below prompts for the user and password, and defaults to env variables.

Now while the password input is well hidden while typing, I'd like also to have the default between brackets hidden as well, so far if I enter this, the password default is in clear 1234:

➜  export PASSWORD=1234
➜  python test.py
➜  User [myuser]: you can see here
➜  Password [1234]: 
user you can see here
password you cant see


import os
import click

@click.command()
@click.option('--user', prompt=True, default=lambda: os.environ.get('USER', ''))
@click.option('--password', prompt=True, default=lambda: os.environ.get('PASSWORD', ''), hide_input=True)
def test(user, password):
    print('user {}'.format(user))
    print('password {}'.format(password))
    print(password)


if __name__ == '__main__':
    test()
2

There are 2 best solutions below

4
On BEST ANSWER

You could make a class whose string representation hides the password:

class HiddenPassword(object):
    def __init__(self, password=''):
        self.password = password
    def __str__(self):
        return '*' * len(self.password)

Then in your code you'd just have to check whether this class was used and update the password:

@click.command()
@click.option('--user',
              prompt=True,
              default=lambda: os.environ.get('USER', ''))
@click.option('--password',
              prompt=True,
              default=lambda: HiddenPassword(os.environ.get('PASSWORD', '')),
              hide_input=True)
def test(user, password):
    print('user: {}'.format(user))
    if isinstance(password, HiddenPassword):
        password = password.password
    print('password: {}'.format(password))

In action:

$ PASSWORD=foobar python test.py
User [mVChr]:
Password [******]:
user: mVChr
password: foobar
$ PASSWORD=foobar python test.py
User [mVChr]: dan
Password [******]:
user: dan
password: user-entered-pw
3
On

Could not find a better solution, but maybe this will do. You can use callback for validation to check the input value and replace it to environment value if no input was provided.

def callback(ctx, param, value):
  if not value:
    return os.environ.get('PASSWORD', '')
  return value
...
@click.option('--password', prompt=True, callback=callback, default='', hide_input=True)