In Python, can I call the main() of an imported module?

146,935

Solution 1

It's just a function. Import it and call it:

import myModule

myModule.main()

If you need to parse arguments, you have two options:

  • Parse them in main(), but pass in sys.argv as a parameter (all code below in the same module myModule):

    def main(args):
        # parse arguments using optparse or argparse or what have you
    
    if __name__ == '__main__':
        import sys
        main(sys.argv[1:])
    

    Now you can import and call myModule.main(['arg1', 'arg2', 'arg3']) from other another module.

  • Have main() accept parameters that are already parsed (again all code in the myModule module):

    def main(foo, bar, baz='spam'):
        # run with already parsed arguments
    
    if __name__ == '__main__':
        import sys
        # parse sys.argv[1:] using optparse or argparse or what have you
        main(foovalue, barvalue, **dictofoptions)
    

    and import and call myModule.main(foovalue, barvalue, baz='ham') elsewhere and passing in python arguments as needed.

The trick here is to detect when your module is being used as a script; when you run a python file as the main script (python filename.py) no import statement is being used, so python calls that module "__main__". But if that same filename.py code is treated as a module (import filename), then python uses that as the module name instead. In both cases the variable __name__ is set, and testing against that tells you how your code was run.

Solution 2

Martijen's answer makes sense, but it was missing something crucial that may seem obvious to others but was hard for me to figure out.

In the version where you use argparse, you need to have this line in the main body.

args = parser.parse_args(args)

Normally when you are using argparse just in a script you just write

args = parser.parse_args()

and parse_args find the arguments from the command line. But in this case the main function does not have access to the command line arguments, so you have to tell argparse what the arguments are.

Here is an example

import argparse
import sys

def x(x_center, y_center):
    print "X center:", x_center
    print "Y center:", y_center

def main(args):
    parser = argparse.ArgumentParser(description="Do something.")
    parser.add_argument("-x", "--xcenter", type=float, default= 2, required=False)
    parser.add_argument("-y", "--ycenter", type=float, default= 4, required=False)
    args = parser.parse_args(args)
    x(args.xcenter, args.ycenter)

if __name__ == '__main__':
    main(sys.argv[1:])

Assuming you named this mytest.py To run it you can either do any of these from the command line

python ./mytest.py -x 8
python ./mytest.py -x 8 -y 2
python ./mytest.py 

which returns respectively

X center: 8.0
Y center: 4

or

X center: 8.0
Y center: 2.0

or

X center: 2
Y center: 4

Or if you want to run from another python script you can do

import mytest
mytest.main(["-x","7","-y","6"]) 

which returns

X center: 7.0
Y center: 6.0

Solution 3

It depends. If the main code is protected by an if as in:

if __name__ == '__main__':
    ...main code...

then no, you can't make Python execute that because you can't influence the automatic variable __name__.

But when all the code is in a function, then might be able to. Try

import myModule

myModule.main()

This works even when the module protects itself with a __all__.

from myModule import * might not make main visible to you, so you really need to import the module itself.

Solution 4

The answer I was searching for was answered here: How to use python argparse with args other than sys.argv?

If main.py and parse_args() is written in this way, then the parsing can be done nicely

# main.py
import argparse
def parse_args():
    parser = argparse.ArgumentParser(description="")
    parser.add_argument('--input', default='my_input.txt')
    return parser

def main(args):
    print(args.input)

if __name__ == "__main__":
    parser = parse_args()
    args = parser.parse_args()
    main(args)

Then you can call main() and parse arguments with parser.parse_args(['--input', 'foobar.txt']) to it in another python script:

# temp.py
from main import main, parse_args
parser = parse_args()
args = parser.parse_args([]) # note the square bracket
# to overwrite default, use parser.parse_args(['--input', 'foobar.txt'])
print(args) # Namespace(input='my_input.txt')
main(args)

Solution 5

I had the same need using argparse too. The thing is parse_args function of an argparse.ArgumentParser object instance implicitly takes its arguments by default from sys.args. The work around, following Martijn line, consists of making that explicit, so you can change the arguments you pass to parse_args as desire.

def main(args):
    # some stuff
    parser = argparse.ArgumentParser()
    # some other stuff
    parsed_args = parser.parse_args(args)
    # more stuff with the args

if __name__ == '__main__':
    import sys
    main(sys.argv[1:])

The key point is passing args to parse_args function. Later, to use the main, you just do as Martijn tell.

Share:
146,935

Related videos on Youtube

Ricky Robinson
Author by

Ricky Robinson

Updated on July 08, 2022

Comments

  • Ricky Robinson
    Ricky Robinson almost 2 years

    In Python I have a module myModule.py where I define a few functions and a main(), which takes a few command line arguments.

    I usually call this main() from a bash script. Now, I would like to put everything into a small package, so I thought that maybe I could turn my simple bash script into a Python script and put it in the package.

    So, how do I actually call the main() function of myModule.py from the main() function of MyFormerBashScript.py? Can I even do that? How do I pass any arguments to it?

    • Marius
      Marius over 11 years
      If you have imported myModule, then you should be able to call myModule.main(). What have you tried so far?
    • Ricky Robinson
      Ricky Robinson over 11 years
      I'm worried by the input arguments, which I usually pass from a shell script.
    • BenDundee
      BenDundee over 11 years
      Does it make sense to call it with the subprocess module?
    • Ricky Robinson
      Ricky Robinson over 11 years
      I guess it would be easier, yeah.
  • Ricky Robinson
    Ricky Robinson over 11 years
    Sure. But what about the input arguments? I use argparse, so when I call the script from a terminal, I do $ python myModule -a input_a -b input_b --parameterC input_c. How would it work from within python code? That's what I couldn't find from a simple search.
  • Ricky Robinson
    Ricky Robinson over 11 years
    Thanks. I am actually using argparse instead of sys.argv. How would it change in this case? Also, from the outer script I just want to pass a few input arguments the user types in, while other input arguments for the inner script (myModule.py) are hardcoded by me.
  • Ricky Robinson
    Ricky Robinson over 11 years
    Oh ok, thanks for the clarification. I put everything in a main() function, so it should be ok. I'm more concerned about how to pass input arguments to this "second" main. Any easy way to do so?
  • Martijn Pieters
    Martijn Pieters over 11 years
    @RickyRobinson: expanded to show that you can have it both ways; just pass in the arguments parsed or to-be-parsed.
  • Ricky Robinson
    Ricky Robinson over 11 years
    Thanks. Could you please also specify which excerpt of code belong to which module or script? It looks way more cumbersome than what I thought at the beginning.
  • agoebel
    agoebel over 11 years
    Without seeing the code itself it's hard to answer specifics. In general, you would just pass whatever arguments. The * unpacks an array f(a) => f([1,2,3]) vs f(*a) => f(1,2,3) You could just as easily do myModule.main(sys.argv[1], "other value", 39) or whatever.
  • Martijn Pieters
    Martijn Pieters over 11 years
    @RickyRobinson: Both excerpts belong together in the same module; I've made that explicit.
  • Aaron Digulla
    Aaron Digulla over 11 years
    Sure: import sys; module.main(sys.argv);
  • Charlie G
    Charlie G about 7 years
    This is a great alternate explanation about accessing __main__ which helped me, thanks @AaronDigulla
  • PatriceG
    PatriceG almost 7 years
    Here is a trick to call a main from another function: stackoverflow.com/a/20158605/3244382
  • HFBrowning
    HFBrowning over 6 years
    This is exactly what I needed - thank you so much for the helpful addendum
  • NaturalBornCamper
    NaturalBornCamper about 4 years
    This might have worked with Python 2, but in Python 3 it doesn't work anymore, can't call main() function of a module: AttributeError: module 'sqlacodegen' has no attribute 'main'
  • ejohnso49
    ejohnso49 almost 3 years
    @NaturalBornCamper that's due to how sqlacodegen is written. It's main is in sqlacodegen.cli and is written to only parse args from sys.argv[1:]. You cannot provide args to main.