1

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.

ccasimiro9444
  • 425
  • 1
  • 6
  • 22
  • password normaly comes from file %AppData%\Roaming\Postgresql\pgpass.conf on windows – Drako Jun 15 '17 at 15:27
  • 1
    You seem to be on your way, you're just 'hiding' the password by forcing the user to enter it each time they use the program, instead of having it hard-coded. When it comes to your error, you need to specify which arg you want, instead of using the whole list.
    `password='" + args + "'"` should be `password='" + args.password + "'"`
    – Carl Shiles Jun 15 '17 at 15:33
  • I don't understand what you're trying to do. add_argument("echo") just adds an additional argument named echo. – Carl Shiles Jun 15 '17 at 16:06
  • The error is produced by the parser itself. Without the `-p` it doesn't call the `Password` Action, and is left with a string it doesn't know how to handle (no `positional` argument like your `echo`. With the `-p` is calls the Action, but I'm puzzled as to why it didn't pass the nest string to the Action as well. – hpaulj Jun 15 '17 at 16:08
  • @Drako I am using a Mac. But you would say that I should store the password in a *.conf file and then have the getpass library accessing it? Or what are you exactly saying? – ccasimiro9444 Jun 15 '17 at 16:10
  • read documentation, there should be path for config file on MAC written as well – Drako Jun 16 '17 at 06:41

2 Answers2

1

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")
hpaulj
  • 221,503
  • 14
  • 230
  • 353
  • Thank you very much for this clarification. I re-run the sole argparse script again as well with your commands and get the same results. I was puzzled so tried the command to run my script again `python3 my_script_file.py -p my_password` and then it worked with the `args.password`. So was puzzled again, and checked my previous command and saw that I did `python3 my_script_file.py my_password -p`. That must have been the my mistake. – ccasimiro9444 Jun 15 '17 at 16:36
  • I am new to this. Should I change my question to reflect my actual and wrong command I used? – ccasimiro9444 Jun 15 '17 at 16:38
  • Go ahead and edit your question. Yes, the reversed order would explain the error. `-p` is required to handle the next string properly. – hpaulj Jun 15 '17 at 16:44
0

Thank you @CarlShiles, your answer didn't work with that long argparse/getpass snippet from above, but it made me realise that I could just echo the password in there. So I did a simple

import argparse
parser = argparse.ArgumentParser()
parser.add_argument("echo")
args = parser.parse_args()

and then used your suggestion, password='" + args.echo + "'". And then ran the following command python3 my_script_file.py my_password. This worked just fine.

ccasimiro9444
  • 425
  • 1
  • 6
  • 22