How do I write a Python script that runs from the command line?
Short answer
Use argparse to define arguments, put the work in a main function, and call it under the name-main guard. This gives you help text, type conversion and validation for free.
The short version
Use argparse to declare the arguments your script accepts, put the real work in a main function, and call it under if __name__ == "__main__":. You get --help output, type conversion and error messages for free, instead of picking apart sys.argv by hand.
A complete small script
import argparse
import pandas as pd
def main():
parser = argparse.ArgumentParser(description="Summarise orders by city.")
parser.add_argument("input", help="path to the input CSV")
parser.add_argument("--output", default="summary.csv", help="where to write")
parser.add_argument("--minimum", type=float, default=0.0,
help="ignore orders below this amount")
args = parser.parse_args()
df = pd.read_csv(args.input)
df = df[df["amount"] >= args.minimum]
summary = df.groupby("city", as_index=False)["amount"].sum()
summary.to_csv(args.output, index=False)
print(f"wrote {len(summary)} rows to {args.output}")
if __name__ == "__main__":
main()
Run it with python summarise.py orders.csv --minimum 10.
What argparse gives you
A positional argument (input) is required; anything starting with -- is optional and can have a default. type=float converts and validates the value, so a non-numeric input produces a clear error rather than a crash deep in your code. And --help is generated automatically from your help strings, so the script documents itself.
Why not just use sys.argv
import sys
path = sys.argv[1] # crashes with an unhelpful IndexError if omitted
minimum = float(sys.argv[2]) # crashes if not a number, no useful message
This works until someone runs it wrongly, at which point they get an obscure traceback instead of "the following arguments are required: input". Every hand-rolled argument parser eventually reinvents a worse argparse.
Why the main function and the guard
Putting the work in main() and calling it under the guard means the script can also be imported (by a test, or another script) without running. That makes the logic testable, which a top-level script body is not.
Flags and choices
Two more options cover most needs: a boolean flag and a restricted set of values.
parser.add_argument("--verbose", action="store_true")
parser.add_argument("--mode", choices=["fast", "careful"], default="fast")
store_true makes --verbose a flag that needs no value, and choices rejects anything not in the list, with a helpful message.
Exit codes matter
If the script fails, exit with a non-zero status so that whatever runs it (a scheduler, a pipeline) knows it failed.
import sys
if df.empty:
print("no rows to summarise", file=sys.stderr)
sys.exit(1)
Printing errors to stderr and exiting non-zero is the convention that lets your script cooperate with other tools, rather than silently reporting success while doing nothing.