A Python file can be imported (used as a module by other code) or run (executed as a script). This lesson covers how to write files that do both, and how to pass them arguments from the command line.

The if __name__ == "__main__": pattern (recap)

You saw this in the modules lesson. Every Python file has a hidden variable __name__:

  • When the file is run directly: __name__ == "__main__"
  • When it’s imported: __name__ is the module’s name (e.g. "greetings")

The standard pattern for a runnable module:

def main() -> None:
    print("Doing the thing...")


if __name__ == "__main__":
    main()

When run directly, main() runs. When imported, the file’s functions become available but main() doesn’t run automatically.

Running with python -m

There are two ways to run a Python file:

uv run python path/to/file.py
uv run python -m path.to.module

The first treats the file as a standalone script. The second treats it as a module inside a package. The difference matters when the file uses relative imports — only -m makes those work.

For example, given:

my_project/
└── tools/
    ├── __init__.py
    └── analyse.py

Run it as:

uv run python -m tools.analyse

Not as python tools/analyse.py — that would break any relative imports inside.

Command-line arguments — sys.argv

When you run a script, Python passes any extra command-line arguments into a list called sys.argv:

import sys

print(sys.argv)
uv run python hello.py Manikandan 30
['hello.py', 'Manikandan', '30']

sys.argv[0] is the script name; the rest are the arguments. They’re always strings — convert if you need numbers.

A simple example:

import sys


def main() -> None:
    if len(sys.argv) < 2:
        print("Usage: python greet.py <name>")
        sys.exit(1)
    name: str = sys.argv[1]
    print(f"Hello, {name}!")


if __name__ == "__main__":
    main()

sys.exit(code) ends the program. 0 means success; anything non-zero means an error.

A nicer CLI — argparse

For anything beyond one or two arguments, sys.argv gets clunky. The standard library has argparse:

import argparse


def main() -> None:
    parser = argparse.ArgumentParser(description="Greet someone")
    parser.add_argument("name", help="The person's name")
    parser.add_argument("--shout", action="store_true", help="Shout the greeting")
    parser.add_argument("--times", type=int, default=1, help="How many times")

    args = parser.parse_args()

    message: str = f"Hello, {args.name}!"
    if args.shout:
        message = message.upper()

    for _ in range(args.times):
        print(message)


if __name__ == "__main__":
    main()

Run it:

uv run python greet.py Manikandan --shout --times 3
HELLO, MANIKANDAN!
HELLO, MANIKANDAN!
HELLO, MANIKANDAN!

You also get a free --help:

uv run python greet.py --help
usage: greet.py [-h] [--shout] [--times TIMES] name

Greet someone

positional arguments:
  name             The person's name

options:
  -h, --help       show this help message and exit
  --shout          Shout the greeting
  --times TIMES    How many times

argparse is part of the standard library — no install needed. For fancier CLI tools, look at typer or click (third-party).

Standard input — sys.stdin

If you want your script to accept data piped in from another program:

import sys

for line in sys.stdin:
    print(line.rstrip().upper())

Run it:

echo "hello world" | uv run python upper.py
HELLO WORLD

This is how the classic Unix tools (grep, awk, sort) are composed — by piping output of one into input of the next.

Exit codes

By default, a script exits with code 0 (success). To signal failure, call sys.exit(non_zero_int):

if not data:
    print("No data found", file=sys.stderr)
    sys.exit(1)

CI systems, shell scripts, and other tools use these codes to decide what happened. Use them properly in scripts that other systems run.

Summary of Section 11

You can now:

  • Import modules (and rename them with as)
  • Write your own modules that other files can import
  • Organise modules into packages with __init__.py
  • Recognise common standard library modules
  • Build runnable scripts with if __name__ == "__main__":
  • Pass command-line arguments with sys.argv or argparse

These are the skills for growing a project from one file to many — without losing your way.

What’s next

Section 12: Object-Oriented Programming — defining your own types with classes and objects.

Toggle theme (T)