18

This thread is an extension from the previous that can be found here. Say, I have a code that serve two purpose:

  1. print a max number from a list of integer,
  2. make a new dir.
import argparse
import os

parser = argparse.ArgumentParser()
parser.add_argument('integers', metavar='N', type=int, nargs='+',
                    help='an integer for the accumulator')

parser.add_argument('--sum', dest='accumulate', action='store_const',
                    const=sum, default=sum,
                    help='sum the integers (default: find the max)')

parser.add_argument("--output_dir", type=str, default="data/xx")

def main(args):
    os.makedirs(args.output_dir)
    print args.accumulate(args.integers)

if __name__=='__main__':
    args = parser.parse_args()
    # Disable when run through terminal: For debugging process
    # args = argparse.Namespace(integers = 1, output_dir= 'mydata_223ss32')
    main(args)

These statement can be executed from terminal by

python test_file.py --output_dir data/xxxx 2 2 5 --sum

However, for debugging process, I want to skip the usage of terminal. The idea by hpaulj as can be found from here was simply modify the

if __name__=='__main__':

into

if __name__=='__main__':
    # Disable when run through terminal: For debugging process
    args = argparse.Namespace(output_dir= 'mydata')
    main(args)

However, I also want to include a list of integer during the debugging process. Including both the list of integer and dir address as below output an error

args = argparse.Namespace(integers = "2 2 5", output_dir= 'mydata') 
1
  • 1
    If you create a Namespace directly, make sure its contents match what the parser would produce. integers is type=int and nargs='+', which will produce a list of integers, not a string, e.g. integers=[1,2,3]. I suggested this as a way of testing the rest of the code, not as a way of testing the parser`. Commented Jun 16, 2018 at 16:04

2 Answers 2

27

By slightly changing your code to:

import argparse
import sys

parser = argparse.ArgumentParser()
parser.add_argument('integers', metavar='N', type=int, nargs='+',
                    help='an integer for the accumulator')

parser.add_argument('--sum', dest='accumulate', action='store_const',
                    const=sum, default=sum,
                    help='sum the integers (default: find the max)')

parser.add_argument("--output_dir", type=str, default="data/xx")

def main(args):
    #os.makedirs(args.output_dir)              # XXX: Commented out for debugging
    print(args.accumulate(args.integers))

if __name__=='__main__':
    print(sys.argv)
    args = parser.parse_args()  # Disable during debugging @ Run through terminal
    # args = argparse.Namespace(integers = 1, output_dir= 'mydata_223ss32')  # Disable when run through terminal: For debugging process
    print(args)
    main(args)

We can see that if we call the script with: ./test3.py --output_dir foo 1 2 3

Our output is:

['test3.py', '--output_dir', 'foo', '1', '2', '3']
Namespace(accumulate=<built-in function sum>, integers=[1, 2, 3], output_dir='foo')
6

So, if you want to emulate command line arguments, you have two options:

1) Edit sys.argv (personally preferred):

if __name__=='__main__':
    # Override sys.argv
    sys.argv = ['test3.py', '--output_dir', 'foo', '1', '2', '3']
    args = parser.parse_args()
    main(args)

2) Create a Namespace object:

if __name__=='__main__':    
    #args = parser.parse_args()  # Disable during debugging @ Run through terminal
    args = argparse.Namespace(accumulate=sum, integers=[1,2,3], output_dir='foo')
    main(args)

The problem with the second approach is two-fold. First, you're skipping a very important part of the debugging process by doing this, debugging the argparse configuration and ensuring the arguments are parsed as you expect. Second, implicit defaults added by your argparse configuration must be defined here (e.g. your --sum argument/accumulate). If you took the first approach, argparse would process sys.argv and add accumulate without you having to make any changes. In the second approach, we have to add accumulate=sum for it to run as expected.

Sign up to request clarification or add additional context in comments.

Comments

0

To add some more information to [jedwards Answer][1]:

I was using pycharm to achieve the same and I needed to alter jedwards proposition slightly, such that sys.argv is not overwritten but modified. The first entry of the list sys.argv[0] should be left, since it contains the path of the file that is being run. Without doing that the variable parser.prog would be messed up and therefore non functional.

Instead of adding the filename manually as the first list entry as jedwards did you can just append to sys.argv and extend the list:

if __name__=='__main__':
    # Append additional arguments to sys.argv
    sys.argv = sys.argv + ['--output_dir', 'foo', '1', '2', '3']
    args = parser.parse_args()
    main(args)

This makes it resilient to changes of the file name. [1]: https://stackoverflow.com/a/50886791/7705525

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.