33

This is probably a silly question, but I have a python script that current takes in a bunch of arguments using argparser and I would like to load this script as a module in another python script, which is fine. But I am not sure how to call the module as no function is defined; can I still call it the same way I do if I was just invoking it from cmd?

Here is the child script:

import argparse as ap
from subprocess import Popen, PIPE

parser = ap.ArgumentParser(
    description='Gathers parameters.')
parser.add_argument('-f', metavar='--file', type=ap.FileType('r'), action='store', dest='file',
                    required=True, help='Path to json parameter file')
parser.add_argument('-t', metavar='--type', type=str, action='store', dest='type',
                    required=True, help='Type of parameter file.')
parser.add_argument('-g', metavar='--group', type=str, action='store', dest='group',
                    required=False, help='Group to apply parameters to')
# Gather the provided arguments as an array.
args = parser.parse_args()

... Do stuff in the script

and here is the parent script that I want to invoke the child script from; it also uses arg parser and does some other logic

from configuration import parameterscript as paramscript

# Can I do something like this?
paramscript('parameters/test.params.json', test)

Inside the configuration directory, I also created an init.py file that is empty.

marc_s
  • 732,580
  • 175
  • 1,330
  • 1,459
Pectus Excavatum
  • 3,593
  • 16
  • 47
  • 68

3 Answers3

37

The first argument to parse_args is a list of arguments. By default it's None which means use sys.argv. So you can arrange your script like this:

import argparse as ap

def main(raw_args=None):
    parser = ap.ArgumentParser(
        description='Gathers parameters.')
    parser.add_argument('-f', metavar='--file', type=ap.FileType('r'), action='store', dest='file',
                        required=True, help='Path to json parameter file')
    parser.add_argument('-t', metavar='--type', type=str, action='store', dest='type',
                        required=True, help='Type of parameter file.')
    parser.add_argument('-g', metavar='--group', type=str, action='store', dest='group',
                        required=False, help='Group to apply parameters to')
    # Gather the provided arguments as an array.
    args = parser.parse_args(raw_args)
    print(vars(args))


# Run with command line arguments precisely when called directly
# (rather than when imported)
if __name__ == '__main__':
    main()

And then elsewhere:

from first_module import main

main(['-f', '/etc/hosts', '-t', 'json'])

Output:

{'group': None, 'file': <_io.TextIOWrapper name='/etc/hosts' mode='r' encoding='UTF-8'>, 'type': 'json'}
marc_s
  • 732,580
  • 175
  • 1,330
  • 1,459
Alex Hall
  • 34,833
  • 5
  • 57
  • 89
  • 1
    and to avoid needing to manually split argument lists: `main('-f /etc/hosts -t json'.split())` or if splitting needs to be more sophisticated: `import shlex; main(shlex.split('-f /etc/hosts -t json -g "special group name with spaces"'))` ([shlex](https://docs.python.org/3/library/shlex.html) is in the standard library.) – teichert Jun 25 '20 at 18:33
4

There may be a simpler and more pythonic way to do this, but here is one possibility using the subprocess module:

Example:

child_script.py

import argparse

parser = argparse.ArgumentParser()
parser.add_argument("-n", "--name", help="your name")
args = parser.parse_args()

print("hello there {}").format(args.name)

Then another Python script can call that script like so:

calling_script.py:

import subprocess

# using Popen may suit better here depending on how you want to deal
# with the output of the child_script.
subprocess.call(["python", "child_script.py", "-n", "Donny"])

Executing the above script would give the following output:

"hello there Donny"
Totem
  • 7,189
  • 5
  • 39
  • 66
  • 2
    Speaking from experience here, using subprocess to run python instead of importing modules leads to issues down the line. A FileNotFoundException in your subprocess turns into a return code that you have to check and handle with more than you would otherwise have to without subprocess. "except FileNotFoundError: raise" turns into spaghetti with subprocess. This is the tip of the iceberg. For the sake of your future self and coworkers, don't do this. – Ryan McGrath Oct 07 '21 at 19:07
  • Subprocesses cost significant overhead. – Tripp Kinetics Dec 13 '22 at 15:24
2

One of the option is to call it as subprocess call like below:

import subprocess
childproc = subprocess.Popen('python childscript.py -file yourjsonfile')
op, oe = childproc.communicate()
print op
be_good_do_good
  • 4,311
  • 3
  • 28
  • 42