Search code examples
python-3.xpsycopg2argparsegetpass

How to use argparse in the actual script


How does argparse work? I was told to 'hide' the password from the psycopg2 connection I am building so to be able to run the script automatically every week and being able to share it between departments. This is the beginning of the psycopg2 script where the password is asked:

#Connect to database
conn_string = "host='my_aws_postgresql_database.rds.amazonaws.com' dbname='my_database_name' user='my_username' password='my_password'"
# print the connection string we will use to connect
print ("\"Connecting to database\n ->%s\"" % (conn_string))

Now, how would I use argparse (and getpass) to hide my password? I found this script about this subject a couple of times (I would delete the print statement after getting it to work):

import argparse
import getpass

class Password(argparse.Action):
    def __call__(self, parser, namespace, values, option_string):
        if values is None:
            values = getpass.getpass()
        setattr(namespace, self.dest, values)

parser = argparse.ArgumentParser('Test password parser')
parser.add_argument('-p', action=Password, nargs='?', dest='password', 
help='Enter your password')
args = parser.parse_args()
print (args.password)

I tried to add the argparse snippet above the #Connect to database code. And replaced the password part on line 2 with

conn_string =
    "host='my_aws_postgresql_database.rds.amazonaws.com'
    dbname='my_database_name'
    user='my_username'
    password='" + args + "'"

Then I tried to run the whole script with the command python3 my_script_file.py my_password -p I get asked for the password which I entered, but this rendered the following error

usage: Test password parser [-h] [-p [PASSWORD]]
Test password parser: error: unrecognized arguments: my_password

If I use python3 my_script_file.py my_password I get the same error, but I did not have to enter the password (again).

Am I close to the solution? Is this the standard way of doing this?

The problem was that I used python3 my_script_file.py my_password -p instead of the correct order python3 my_script_file.py -p my_password, see accepted answer below by @hpaulj and the comments to that answer.


Solution

  • This parser is gives the user 2 ways of entering the password, on the commandline, or with a separate getpass prompt:

    import argparse
    import getpass    
    class Password(argparse.Action):
        def __call__(self, parser, namespace, values, option_string):
            if values is None:
                values = getpass.getpass()
            setattr(namespace, self.dest, values)
    
    parser = argparse.ArgumentParser('Test password parser')
    parser.add_argument('-p', action=Password, nargs='?', dest='password',
    help='Enter your password')
    args = parser.parse_args()
    print (args)
    

    Sample runs:

    0911:~/mypy$ python3 stack44571340.py 
    Namespace(password=None)
    0912:~/mypy$ python3 stack44571340.py -p test
    Namespace(password='test')
    0912:~/mypy$ python3 stack44571340.py -p
    Password: 
    Namespace(password='testing')
    0912:~/mypy$ python3 stack44571340.py test
    usage: Test password parser [-h] [-p [PASSWORD]]
    Test password parser: error: unrecognized arguments: test
    

    I tested without any arguments (got the default None)`; with '-p test' which uses the 'test' string; with just '-p', which asks; and without '-p', which produces the error.

    I don't know why python3 my_script_file.py -p my_password produced an error; my best guess there's a typo in your parser definition (something wrong with nargs?).

    It's not entirely clear how you merged this parser code into the larger script. Done right it shouldn't have changed the behavior of the parser.

    The password argument would be used as:

    password='" + args.password + "'"
    

    The echo argument, is a positional one, which requires a string. In contrast the -p with nargs='?', is an optional flagged argument, which allows for the three way input I illustrated.

    parser.add_argument("echo")