How to supply args from the argparse to an external program call via subprocess

Viewed 51

In a task, I need to call an external program that run on terminal via python. This external program has lots of positional and optional arguments.

Here is the sample code developed sofar:

def multi_character_parameter():

    parser = argparse.ArgumentParser()

    parser.add_argument('b',
                        help="Execute tests with browser 'firefox' or 'chrome', or 'ALL")
    parser.add_argument('C', help="Execute tests in cloud 'aws' or 'azure'")
    parser.add_argument(
        'c', help="Clear output and temp directory before executing tests")
    parser.add_argument('d', help="Check syntax for test data")
    parser.add_argument("--upper-case", default=False, action="store_true")
...
args = parser.parse_args()

def my_external_program():
 subprocess.call(["external_program"])

Could someone please suggest:

  1. How to supply these args to the external_program called via subprocess.

i.e. upon running say python script.py some_args, these some_args will be supplied to the external_program function for execution as external_program some_args.

  1. What would be the better way to structure the code if the args specified in the code needs to be used at other parts of the code.
3 Answers

Once you set the variable args, you can access any of the arguments using dot notation.

I have edited your code to demonstrate this:

#!/usr/bin/env python3
 
import argparse
import subprocess

def multi_character_parameter():

    parser = argparse.ArgumentParser()

    parser.add_argument('--b',
                        help="Execute tests with browser 'firefox' or 'chrome', or 'ALL",
                        required=False,
                        default=False)
    parser.add_argument('--C', help="Execute tests in cloud 'aws' or 'azure'",
        required=False, default=False)   
    parser.add_argument(
        '--c', help="Clear output and temp directory before executing tests",
        required=False, default=False)
    parser.add_argument('--d', help="Check syntax for test data", required=False, default=False)
    parser.add_argument("--upper-case", action="store_true", required=False, default=False)
    args = parser.parse_args()
    return args

def my_external_program(args):
    print(args)
    b = str(args.b)
    C = str(args.C)
    c = str(args.c)
    d = str(args.d)
    upper_case = str(args.upper_case)
    subprocess.call(["echo", b,C,c,d,upper_case ])


if __name__ == "__main__":
    args = multi_character_parameter()
    my_external_program(multi_character_parameter())

Now to run it chmod +x test.py

./test.py

The stdout is presented to show access to the args (the external program was subbed out for echo as the program:

Namespace(b=False, C=False, c=False, d=False, upper_case=False)
False False False False False

If you just want to literally pass all the arguments:

import sys

subprocess.call(["external_program"] + sys.argv[1:])

This takes all the arguments passed to the script (except the first, which is the script's name) and just appends them to the argument you already provided, the name of the external program - and probably works as expected.

So, if you were to run python your_script.py 1 2 3 4 (which matches your specified argument requirements), it would call external_program 1 2 3 4.

The more general answer is that you should just provide a list of arguments to subprocess.call, as described in the documentation for subprocess.call - "args should be a sequence of program arguments or else a single string or path-like object."

Since you provide no information on exactly what mapping you're after, or how the arguments to your script translate to arguments for the external program, it's not possible to be a lot more specific - other than saying you need to construct a sequence of arguments to pass to the call.

If you want to limit or restrict the arguments passed to external_program you can do something like this

allowed_args = ['--upper-case', 'b', 'c', 'C', 'd']
subprocess.run(['external_program'] + list(set(sys.argv).intersection(allowed_args)))
Related