Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Importing a python script/module that uses argparse into another python script

This question may be regarded to be redundant but, to my defense, I've considered similar questions and given solutions but they don't work for me (or I just didn't understand them) as I will show.

What I want to do: I wrote a couple of python scripts that are each command-line tools (accepting several arguments using argparse) when built with pyinstaller. They all work as expected both from within pycharm's terminal and from the Ubuntu terminal. Now, I would like each of these scripts to be a module that could be called from another python script passing required arguments as I would do in the terminal.

This is one of the original scripts (I've shortened the script a bit so that it serves as a minimal example):

import sys
import argparse
import getpass


if len(sys.argv) < 2:
    print "You haven't specified any arguments. Use -h to get more details on how to use this command."
    sys.exit(1)


parser = argparse.ArgumentParser()
parser.add_argument('--username', '-u', type=str, default=None, help='Username for the login to the WebCTRL server')
parser.add_argument('--password', '-p', type=str, default=None, help='Password for the login to the WebCTRL server')
parser.add_argument('--node', '-n', type=str, default=None,
    help='Path to the point or node whose children you want to retrieve. Start querying at the lowest level with "-n /trees/geographic"')
parser.add_argument('-url', type=str, default='https://my.server.de',
    help="URL of the WebCTRL server as e.g. http://google.de")
args = parser.parse_args()


if args.username is None:
    print 'No user name specified. Login to WebCTRL needs a user name and password. Check all options for this command via -h'
    sys.exit(1)
else:
    username = args.username
if args.password is None:
    password = getpass.getpass('No password specified via -p. Please enter your WebCTRL login password: ')
else:
    password = args.password
if args.node is None:
    print 'No path to a node specified. Check all options for this command via -h'
    sys.exit(1)
if args.url is None:
    print 'No URL given. Specify the URL to the WebCTRL server analogous to http://google.de'
    sys.exit(1)
else:
    wsdlFile = args.url + '/_common/webservices/Eval?wsdl'


# This doesn't belong to my original code. It's rather for demonstration:
# Print the arguments and leave the script
print 'Username: ' + args.username
print 'Node: ' + args.node
print 'URL: ' + args.url

sys.exit(0)

As I said: From within my IDE (Pycharm) ...

$ python wc_query_test.py -u wsdl -n /trees/geographic
No password specified via -p. Please enter your WebCTRL login password: 
Username: wsdl
Node: /trees/geographic
URL: https://my.server.de

... and from the Ubuntu terminal it works just fine:

$ pyinstaller --distpath dist/. wc_query_test.py
$ ./dist/wc_query_test/wc_query_test -u wsdl -n /trees/geographic
No password specified via -p. Please enter your WebCTRL login password: 
Username: wsdl
Node: /trees/geographic
URL: https://my.server.de

Coming to the actual problem: I want the script wc_query_test.py to be a module that can be imported into another python script and executed there passing along the arguments as I do in the command line. To achieve this I followed @Waylan's instructions in this stackoverflow question.

This is the code I came up with (wc_query_test.py):

import sys
import argparse
import getpass

def main(**kwargs):
    if kwargs.username is None:
        print 'No user name specified. Login to WebCTRL needs a user name and password. Check all options for this command via -h'
        sys.exit(1)
    else:
        username = kwargs.username
    if kwargs.password is None:
        password = getpass.getpass('No password specified via -p. Please enter your WebCTRL login password: ')
    else:
        password = kwargs.password
    if kwargs.node is None:
        print 'No path to a node specified. Check all options for this command via -h'
        sys.exit(1)
    if kwargs.url is None:
        print 'No URL given. Specify the URL to the WebCTRL server analogous to http://google.de'
        sys.exit(1)
    else:
        wsdlFile = kwargs.url + '/_common/webservices/Eval?wsdl'

    # This doesn't belong to my original code. It's rather for demonstration:
    # Print the arguments and leave the script
    print 'Username: ' + username
    print 'Node: ' + kwargs.node
    print 'URL: ' + kwargs.url

    sys.exit(0)

def run():
    parser = argparse.ArgumentParser()
    parser.add_argument('--username', '-u', type=str, default=None, help='Username for the login to the WebCTRL server')
    parser.add_argument('--password', '-p', type=str, default=None, help='Password for the login to the WebCTRL server')
    parser.add_argument('--node', '-n', type=str, default=None,
        help='Path to the point or node whose children you want to retrieve. Start querying at the lowest level with "-n /trees/geographic"')
    parser.add_argument('-url', type=str, default='https://my.server.de',
        help="URL of the WebCTRL server as e.g. http://google.de")
    args = parser.parse_args()

    main(**args)

... and the script that imports the module and calls it (test.py):

import wc_query_test
wc_query_test.main(username='wsdl', password='aaaaaa', node='/trees/geographic')

When I run it in the python terminal I get:

~/PycharmProjects/webctrl$ python wc_query_test.py
~/PycharmProjects/webctrl$ python test.py 
Traceback (most recent call last):
  File "test.py", line 3, in <module>
    wc_query_test.main(username='wsdl', password='aaaaaa', node='/trees/geographic/#geb_g', url='https://webctrl.rz-berlin.mpg.de')
  File "/home/stefan/PycharmProjects/webctrl/wc_query_test.py", line 23, in main
    if kwargs.username is None:
AttributeError: 'dict' object has no attribute 'username'

Running wc_query_test.py yields no output and I see why this is. That was just a test. But running test.py yields an error as well. I get an idea why this can't work either but I cannot put it into words. What about this run() method? Does it make sense to have it? How would I need to modify my code in order to obtain this dual functionality as described in "What I want to do:"? Thank you in advance for your help!

UPDATE: I got rid of the error message. I changed e.g. kwargs.username to kwargs.get('username') since kwargs is a dictionary. The code now looks like this:

import sys
import argparse
import getpass

def main(**kwargs):
    if kwargs.get('username') is None:
        print 'No user name specified. Login to WebCTRL needs a user name and password. Check all options for this command via -h'
        sys.exit(1)
    else:
        username = kwargs.get('username')
    if kwargs.get('password') is None:
        password = getpass.getpass('No password specified via -p. Please enter your WebCTRL login password: ')
    else:
        password = kwargs.get('password')
    if kwargs.get('node') is None:
        print 'No path to a node specified. Check all options for this command via -h'
        sys.exit(1)
    if kwargs.get('url') is None:
        print 'No URL given. Specify the URL to the WebCTRL server analogous to http://google.de'
        sys.exit(1)
    else:
        wsdlFile = kwargs.get('url') + '/_common/webservices/Eval?wsdl'

    # This doesn't belong to my original code. It's rather for demonstration:
    # Print the arguments and leave the script
    print 'Username: ' + username
    print 'Node: ' + kwargs.get('node')
    print 'URL: ' + kwargs.get('url')

    sys.exit(0)

def run():
    parser = argparse.ArgumentParser()
    parser.add_argument('--username', '-u', type=str, default=None, help='Username for the login to the WebCTRL server')
    parser.add_argument('--password', '-p', type=str, default=None, help='Password for the login to the WebCTRL server')
    parser.add_argument('--node', '-n', type=str, default=None,
        help='Path to the point or node whose children you want to retrieve. Start querying at the lowest level with "-n /trees/geographic"')
    parser.add_argument('-url', type=str, default='https://webctrl.rz-berlin.mpg.de',
        help="URL of the WebCTRL server as e.g. http://google.de")
    args = parser.parse_args()

    main(**args)

Running it in the python terminal yields as expected:

$ python test.py 
Username: wsdl
Node: /trees/geographic
URL: https://my.server.de

But building it via pyinstaller and running it as command line tool there is no output:

~/PycharmProjects/webctrl$ ./dist/wc_query_test/wc_query_test -h
~/PycharmProjects/webctrl$

How can I modify wc_query_test.py so that it takes arguments and serves as command line tool?

like image 649
732E772E Avatar asked May 31 '17 11:05

732E772E


1 Answers

Thanks to everyone who responded. With the help of a colleague I got the answer to my question. This is the functioning code:

wc_query_test.py:

import sys
import argparse
import getpass

def main(args):
    if args['username'] is None:
        print 'No user name specified. Login to WebCTRL needs a user name and password. Check all options for this command via -h'
        sys.exit(1)
    else:
        username = args['username']
    if args['password'] is None:
        password = getpass.getpass('No password specified via -p. Please enter your WebCTRL login password: ')
    else:
        password = args['password']
    if args['node'] is None:
        print 'No path to a node specified. Check all options for this command via -h'
        sys.exit(1)
    if args['url'] is None:
        print 'No URL given. Specify the URL to the WebCTRL server analogous to http://google.de'
        sys.exit(1)
    else:
        wsdlFile = args['url'] + '/_common/webservices/Eval?wsdl'

    # This doesn't belong to my original code. It's rather for demonstration:
    # Print the arguments and leave the script
    print 'Username: ' + args['username']
    print 'Node: ' + args['node']
    print 'URL: ' + args['url']


# The parser is only called if this script is called as a script/executable (via command line) but not when imported by another script
if __name__=='__main__':
    if len(sys.argv) < 2:
        print "You haven't specified any arguments. Use -h to get more details on how to use this command."
        sys.exit(1)
    parser = argparse.ArgumentParser()
    parser.add_argument('--username', '-u', type=str, default=None, help='Username for the login to the WebCTRL server')
    parser.add_argument('--password', '-p', type=str, default=None, help='Password for the login to the WebCTRL server')
    parser.add_argument('--node', '-n', type=str, default=None,
        help='Path to the point or node whose children you want to retrieve. Start querying at the lowest level with "-n /trees/geographic"')
    parser.add_argument('-url', type=str, default='https://webctrl.rz-berlin.mpg.de',
        help="URL of the WebCTRL server as e.g. http://google.de")
    args = parser.parse_args()

    # Convert the argparse.Namespace to a dictionary: vars(args)
    main(vars(args))
    sys.exit(0)

Now, there are three ways of executing wc_query_test which is what I wanted to achive:

1) Calling wc_query_test.py from command line:

~/PycharmProjects/webctrl$ python wc_query_test.py -u aawrg -p wgAWER -n YWERGAEWR

2) Compiling and calling wc_query_test from command line:

~/PycharmProjects/webctrl$ pyinstaller --distpath dist/. wc_query_test.py
~/PycharmProjects/webctrl$ ./dist/wc_query_test/wc_query_test -u aawrg -p wgAWER -n YWERGAEWR

3) Calling wc_query_test from another python script which goes into the direction of a module-type usage:

import wc_query_test
myDictonary = {'username':'wsdl', 'password':'aaaaaa', 'node':'/trees/geographic', 'url':'https://my.server.de'}
wc_query_test.main(myDictonary)

All three versions yield the same output as expected, for example:

~/PycharmProjects/webctrl$ ./dist/wc_query_test/wc_query_test -u aawrg -p wgAWER -n YWERGAEWR
Username: aawrg
Node: YWERGAEWR
URL: https://webctrl.rz-berlin.mpg.de
like image 164
732E772E Avatar answered Oct 21 '22 07:10

732E772E