euri10
euri10

Reputation: 2626

Python click option with a prompt AND default hidden

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()

Upvotes: 5

Views: 6673

Answers (2)

mVChr
mVChr

Reputation: 50187

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

Upvotes: 9

Ilya Luzyanin
Ilya Luzyanin

Reputation: 8110

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)

Upvotes: 3

Related Questions