flowchart TD
A["sys.argv"] --> B["parser.parse_args()"]
B --> C{"a required positional missing?"}
C -->|yes| D["print usage, SystemExit(2)"]
C -->|no| E{"does each value fit its type=?"}
E -->|no| D
E -->|yes| F["build a Namespace"]
F --> G["--dry-run becomes ns.dry_run"]
F --> H["-n '3' becomes the int 3"]
G --> I["your script runs, with values already validated"]
H --> I
J["-h / --help"] --> K["print help, SystemExit(0)"]
Why argparse
It gives your scripts:
--help- typed arguments
- clear usage patterns
Example
import argparse
def build_parser():
p = argparse.ArgumentParser(description="Example automation CLI")
p.add_argument("--dry-run", action="store_true", help="Donβt change files")
p.add_argument("--source", required=True, help="Source folder")
return p
def main():
args = build_parser().parse_args()
print("source:", args.source)
print("dry_run:", args.dry_run)
if __name__ == "__main__":
main()Why scripts need arguments
A script with the folder name written inside it has to be edited every time you use it on another folder. A script with arguments can be reused, scheduled with different settings, and run safely with --dry-run. argparse is the standard-library way to define them, and it gives you --help for free.
The three kinds of argument
- Positional arguments are required and identified by position:
add_argument("path"). - Options start with dashes and usually have a default:
add_argument("--limit", type=int, default=10). - Flags are options with no value.
action="store_true"makes--dry-runa boolean that isFalseunless the flag is given.
Useful keywords: type= converts the value (int, float, Path), choices=[...] restricts it, default= sets the fallback, nargs="+" accepts one or more values, and help= feeds the --help output.
A realistic tool
This script takes a folder and an extension, and lists or deletes the matching files. Deletion is off unless you ask for it, which follows the dry-run pattern from the safety lesson.
import argparse
from pathlib import Path
def build_parser() -> argparse.ArgumentParser:
p = argparse.ArgumentParser(description="Find, and optionally delete, files by extension")
p.add_argument("folder", type=Path, help="folder to search")
p.add_argument("--ext", default=".tmp", help="extension to match (default: .tmp)")
p.add_argument("--apply", action="store_true",
help="actually delete; without this flag it only prints")
return p
def main(argv=None) -> int:
args = build_parser().parse_args(argv)
if not args.folder.is_dir():
print(f"error: {args.folder} is not a folder")
return 1
matches = sorted(args.folder.rglob(f"*{args.ext}"))
for path in matches:
action = "deleting" if args.apply else "would delete"
print(action, path)
if args.apply:
path.unlink()
print(f"{len(matches)} file(s)")
return 0
if __name__ == "__main__":
raise SystemExit(main())Run it as python argparse_cleanup.py ./demo --ext .log, and then with --apply once the output looks right. Two design points help you later. main accepts argv, so a test can call main(["./demo", "--ext", ".log"]) without touching the real command line. And it returns an exit code, which SystemExit passes to the operating system. Cron and Task Scheduler use that code to tell success (0) from failure.
Exit codes and errors
By convention, 0 means success and anything else means failure. Return non-zero when the job did not do its work, so a scheduler or a shell script (python job.py && next_step) can react. For a wrong argument, argparse prints the usage line and exits with 2 for you. Use parser.error("message") to report your own validation problems in the same style.
Combining with configuration
Give every option a sensible default and put secrets in environment variables, not in arguments. Arguments are visible to other users in the process list, so --password on a command line is a bad idea.
Common mistakes
- Reading
sys.argvby hand, then handling quoting, types, and help yourself. ns.dry-run. The dash becomes an underscore:args.dry_run.- Making destructive behaviour the default. Require
--applyfor the dangerous path. - Forgetting
type=.--limit 5arrives as the string"5", and"5" + 1raisesTypeError. - A bad default of a mutable type, such as
default=[]withappend.
π§ͺ Try It Yourself
Exercise 1 β List Files with os.listdir
Tab indents. Press Escape, then Tab, to leave the editor. Control plus Enter runs the code; Control plus Shift plus Enter submits it.
Press Run to see what your code prints.Exercise 2 β Join Paths with os.path.join
Tab indents. Press Escape, then Tab, to leave the editor. Control plus Enter runs the code; Control plus Shift plus Enter submits it.
Press Run to see what your code prints.Exercise 3 β Write and Read a File
Tab indents. Press Escape, then Tab, to leave the editor. Control plus Enter runs the code; Control plus Shift plus Enter submits it.
Press Run to see what your code prints.