Building CLI Tools with argparse
If you’ve ever used a command-line tool in Python, you’ve probably passed arguments like --verbose or -f filename. These are handled by the argparse module, part of Python’s standard library. It’s the standard way to build CLIs that feel professional and behave like Unix tools.
Why argparse?
Before argparse, developers used sys.argv directly. It works, but you end up parsing strings manually, handling errors yourself, and producing unhelpful error messages. Argparse gives you:
- Automatic help text generation
- Type conversion and validation
- Positional and optional arguments
- Subcommands (like
git commitvsgit push)
It’s been in the standard library since Python 3.2, so there’s no excuse not to use it.
Your First argparse Script
The simplest possible argparse script:
import argparse
parser = argparse.ArgumentParser()
parser.parse_args()
Run it with --help and you’ll see:
$ python script.py --help
usage: script.py [-h]
options:
-h, --help show this help message and exit
Not very useful yet. Let’s add some arguments.
Positional Arguments
Positional arguments are required and identified by position. Think of python script.py filename where filename is positional:
import argparse
parser = argparse.ArgumentParser(description="Read a file")
parser.add_argument("filename", help="Name of the file to read")
args = parser.parse_args()
print(f"Reading from: {args.filename}")
$ python script.py myfile.txt
Reading from: myfile.txt
Multiple positional arguments work too:
import argparse
parser = argparse.ArgumentParser()
parser.add_argument("x", type=int, help="First number")
parser.add_argument("y", type=int, help="Second number")
args = parser.parse_args()
print(f"Sum: {args.x + args.y}")
$ python script.py 5 3
Sum: 8
Optional Arguments
Optional arguments start with - or --. They’re, well, optional:
import argparse
parser = argparse.ArgumentParser(description="Print a message")
parser.add_argument("--name", help="Name to greet")
args = parser.parse_args()
if args.name:
print(f"Hello, {args.name}!")
else:
print("Hello, World!")
$ python script.py
Hello, World!
$ python script.py --name Alice
Hello, Alice!
You can combine short and long forms:
parser.add_argument("-v", "--verbose", help="Enable verbose output")
$ python script.py -v
$ python script.py --verbose
Flags and Boolean Options
Need a simple on/off flag? Use action="store_true":
import argparse
parser = argparse.ArgumentParser()
parser.add_argument("-q", "--quiet", action="store_true", help="Suppress output")
args = parser.parse_args()
if args.quiet:
print("Quiet mode on")
else:
print("Normal mode")
$ python script.py
Normal mode
$ python script.py -q
Quiet mode on
For the opposite, use store_false.
Type Conversion
Argparse automatically converts types. Just specify type:
import argparse
parser = argparse.ArgumentParser()
parser.add_argument("count", type=int, help="Number of times to repeat")
parser.add_argument("rate", type=float, help="Repeat rate per second")
args = parser.parse_args()
print(f"Will repeat {args.count} times at {args.rate} per second")
$ python script.py 10 2.5
Will repeat 10 times at 2.5 per second
This also validates input - try entering “abc” instead of a number and argparse will error out with a helpful message.
Choices
Limit acceptable values with choices:
import argparse
parser = argparse.ArgumentParser()
parser.add_argument("level", choices=["easy", "medium", "hard"], help="Difficulty level")
args = parser.parse_args()
print(f"Selected level: {args.level}")
$ python script.py easy
Selected level: easy
$ python script.py impossible
usage: script.py [-h] {easy,medium,hard}
script.py: error: argument level: invalid choice: 'impossible' (choose from 'easy', 'medium', 'hard')
Default Values
Set defaults for optional arguments:
import argparse
parser = argparse.ArgumentParser()
parser.add_argument("--count", type=int, default=1, help="Number of times")
parser.add_argument("--name", default="World", help="Name to greet")
args = parser.parse_args()
for i in range(args.count):
print(f"Hello, {args.name}!")
$ python script.py
Hello, World!
$ python script.py --count 3 --name Alice
Hello, Alice!
Hello, Alice!
Hello, Alice!
Required Options
Sometimes you really do want an option to be required. Use required=True:
import argparse
parser = argparse.ArgumentParser()
parser.add_argument("--config", required=True, help="Configuration file path")
args = parser.parse_args()
print(f"Using config: {args.config}")
Now the script won’t run without --config.
Argument Groups
Group related arguments together for better help text:
import argparse
parser = argparse.ArgumentParser(description="A mailer program")
group = parser.add_argument_group("email options")
group.add_argument("--to", help="Recipient email address")
group.add_argument("--from", dest="sender", help="Sender email address")
group.add_argument("--subject", help="Email subject")
args = parser.parse_args()
This organizes the help output logically.
Subcommands
Big CLI tools often have subcommands (like git commit, git push, git pull). Argparse supports this:
import argparse
parser = argparse.ArgumentParser()
subparsers = parser.add_subparsers(dest="command", help="Available commands")
# 'add' subcommand
add_parser = subparsers.add_parser("add", help="Add numbers")
add_parser.add_argument("x", type=int)
add_parser.add_argument("y", type=int)
# 'multiply' subcommand
mult_parser = subparsers.add_parser("multiply", help="Multiply numbers")
mult_parser.add_argument("x", type=int)
mult_parser.add_argument("y", type=int)
args = parser.parse_args()
if args.command == "add":
print(f"Result: {args.x + args.y}")
elif args.command == "multiply":
print(f"Result: {args.x * args.y}")
$ python calc.py add 5 3
Result: 8
$ python calc.py multiply 5 3
Result: 15
$ python calc.py --help
usage: calc.py [-h] {add,multiply} ...
positional arguments:
{add,multiply}
options:
-h, --help show this help message and exit
ArgumentParser Basics
A few useful ArgumentParser options:
parser = argparse.ArgumentParser(
description="My CLI tool",
epilog="Example: mytool.py --verbose input.txt",
formatter_class=argparse.RawDescriptionHelpFormatter,
)
description: Shown at the top of helpepilog: Shown at the bottomformatter_class: Controls help formatting
Putting It Together
Here’s a more complete example combining several features:
#!/usr/bin/env python3
import argparse
def main():
parser = argparse.ArgumentParser(
description="Process a data file"
)
parser.add_argument(
"input",
help="Input file path"
)
parser.add_argument(
"-o", "--output",
default="output.txt",
help="Output file path (default: output.txt)"
)
parser.add_argument(
"-v", "--verbose",
action="store_true",
help="Enable verbose output"
)
parser.add_argument(
"-n", "--lines",
type=int,
default=10,
help="Number of lines to process (default: 10)"
)
parser.add_argument(
"--format",
choices=["json", "csv", "xml"],
default="json",
help="Output format (default: json)"
)
args = parser.parse_args()
if args.verbose:
print(f"Processing: {args.input}")
print(f"Output to: {args.output}")
print(f"Format: {args.format}")
print(f"Lines: {args.lines}")
# Your processing logic here
print(f"Done! Processed {args.lines} lines.")
if __name__ == "__main__":
main()
Run with --help to see the complete, auto-generated interface.
See Also
- sys-module — System-specific parameters and functions
- subprocess-module — Running external commands
- env-variables — Working with environment variables