Summary
For Python programmers, I've got some suggestions on how to write a main() function that's easy to invoke in other contexts, e.g. from the interactive Python prompt when you feel like experimenting.
I've written a few main() functions in my time. They usually have astructure roughly like this:
"""Module docstring. This serves as a long usage message. """ import sys import getopt def main(): # parse command line options try: opts, args = getopt.getopt(sys.argv[1:], "h", ["help"]) except getopt.error, msg: print msg print "for help use --help" sys.exit(2) # process options for o, a in opts: if o in ("-h", "--help"): print __doc__ sys.exit(0) # process arguments for arg in args: process(arg) # process() is defined elsewhere if __name__ == "__main__": main()I'm sure many people write similar main() functions. I've got a fewsuggestions that make main() a little more flexible, especially asoption parsing becomes more complex.
First, we change main() to take an optional 'argv' argument, whichallows us to call it from the interactive Python prompt:
def main(argv=None): if argv is None: argv = sys.argv # etc., replacing sys.argv with argv in the getopt() call.Note that we fill in the default for argv dynamically. This is moreflexible than writing
def main(argv=sys.argv): # etc.because sys.argv might have been changed by the time the call is made;the default argument is calculated at the time the main() function is defined, for all times.
Now the sys.exit()
calls are annoying: when main()calls sys.exit()
, your interactive Python interpreterwill exit! The remedy is to let main()'s return value specify the exitstatus. Thus, the code at the very end becomes
if __name__ == "__main__": sys.exit(main())and the calls to sys.exit(n) inside main() all become
return n
.
Another refinement is to define a Usage() exception, which we catchin an except clause at the end of main():
import sys import getopt class Usage(Exception): def __init__(self, msg): self.msg = msg def main(argv=None): if argv is None: argv = sys.argv try: try: opts, args = getopt.getopt(argv[1:], "h", ["help"]) except getopt.error, msg: raise Usage(msg) # more code, unchanged except Usage, err: print >>sys.stderr, err.msg print >>sys.stderr, "for help use --help" return 2 if __name__ == "__main__": sys.exit(main())This gives the main() function a single exit point, which ispreferable over multiple
return 2
statements. This alsomakes it easier to refactor the argument parsing:
raiseUsage
works just fine from inside a helper function, but
return 2
would require careful passing on of the returnvalues.
You might think that taking this to the extreme would move thetry/except clause out of the main() function, into the code at the endof the module (if __name__ == "__main__": ...
. But thatwould mean that when you call main() interactively, you'd get atraceback for command line syntax errors, which isn't very helpful.
However, another generalization can be helpful: defineanother exception, perhaps called Error
, which is treatedjust like Usage
but returns 1. This can then be used forexpected errors like failure to open necessary files, which are notcommand line syntax errors, but yet expected, and where again atraceback doesn't feel very friendly.
What's your favorite convention for writing main()?
Have an opinion?Readers have already posted29commentsabout this weblog entry. Why notadd yours?
If you'd like to be notified whenever Guido van van Rossum adds a new entry to his weblog, subscribe to his RSS feed.