Command-Line Programs with argparse
About 22 minutes
Why this matters
The most useful scripts are the ones you can point at different inputs without editing the code: tidy.py ~/Downloads, backup.py --dry-run. Programs like that take command-line arguments. Python’s argparse module reads them, checks them, converts types and writes the --help text for you.
The idea
import argparse
parser = argparse.ArgumentParser(description="Show the first lines of a file.")
parser.add_argument("path") # required, by position
parser.add_argument("-n", "--limit", type=int, default=5) # optional value
parser.add_argument("-v", "--verbose", action="store_true") # flag: False unless given
args = parser.parse_args()
print(args.path, args.limit, args.verbose)
Run as python head.py notes.txt -n 3 --verbose and you get notes.txt 3 True. Things argparse does for you:
--helpprints a usage message built from your arguments.- A missing argument, or
-n abcwhere a number is required, prints a clear error and exits with status 2. type=intconverts the text to a number;default=fills in what was not given.
parse_args() reads sys.argv, the list of words typed after python. For testing, pass a list instead: parser.parse_args(["notes.txt", "-n", "3"]). That is how this lesson’s checks run your code, without a real terminal.
Exit codes. A program tells the shell whether it worked through its exit status: 0 means success, anything else means failure. Put your logic in a main(argv=None) function that returns the code, and finish the script with:
if __name__ == "__main__":
raise SystemExit(main())
Print error messages to standard error (print("oops", file=sys.stderr)), so they stay out of the normal output when someone redirects it.
Try it
Make a parser with one positional argument and one --times option with type=int. Call parse_args(["hi", "--times", "3"]) and print the result.
Your turn
Build a tiny version of the Unix head command.
build_parser(): returns anArgumentParserwith a positionalpath, an option-n/--limit(a whole number, default 5) and a flag-v/--verbose.main(argv=None): parseargv, print the firstlimitlines of the file, and return0. If the file does not exist, print a message to standard error and return1. With--verbose, first print a line starting withReadingand the path.
Recap
add_argument("name")is required and positional;add_argument("-n", "--name", ...)is an option;action="store_true"makes a flag.type=converts,default=fills in, and--helpis free.- Pass a list to
parse_argsto test without a terminal. - Return an exit code from
main: 0 for success, non-zero for failure; errors go to stderr.
Common mistakes
- Reading
sys.argvby hand and rebuilding what argparse already does. - Forgetting
type=int, so the value is the text"5"andlines[:"5"]fails. - Printing errors to normal output, which mixes them into the results other programs read.