Skip to lesson
Python for JS Developers

Modules & ErrorsLesson 2 of 3

Scripts & __main__

4 min

Every Python file can be both a module you import and a script you run. Python tells the two cases apart with a special variable: __name__. When you run python app.py, the code in app.py runs with __name__ set to "__main__". When another file does import app, the same code runs with __name__ set to "app".

That is why almost every Python script ends with the main guard: if __name__ == "__main__": main(). Code inside the guard runs only when the file is executed directly, never when it is imported. Without it, importing your module to reuse one function would also start the whole program. It is the equivalent of Node's require.main === module check (or import.meta.main in Deno and Bun), except that in Python everyone uses it.

You can also run a module by its import path with python -m: python -m myapp.cli runs myapp/cli.py as __main__, with imports resolved from the project root. Many tools work this way: python -m venv, python -m pip, python -m http.server, python -m pytest.

Command-line arguments live in sys.argv. Unlike process.argv, where [0] is the node binary and [1] is the script, sys.argv[0] is the script name and the real arguments start at sys.argv[1]. For anything beyond one or two arguments, use the built-in argparse module (think yargs or commander, but in the standard library): it parses flags, converts types, applies defaults, and generates --help for free.

Finally, exit codes: sys.exit(0) means success and any non-zero number means failure, exactly like process.exit(). A common pattern is to write def main() -> int: ... and finish with sys.exit(main()) inside the main guard.

Quick reference

JavaScriptPythonNote
require.main === moduleif __name__ == "__main__":Run only when executed directly
node scripts/build.jspython -m scripts.buildRun a module by import path
process.argv.slice(2)sys.argv[1:]argv[0] is the script name
yargs / commanderargparseBuilt into the standard library
process.exit(1)sys.exit(1)Non-zero = failure

Side by side

JavaScript
// Node.js: greet.js
function main(argv) {
  const [name = "World"] = argv;
  const times = Number(argv[1] ?? 1);
  for (let i = 0; i < times; i++) {
    console.log(`Hello, ${name}!`);
  }
  return 0;
}

if (require.main === module) {
  process.exit(main(process.argv.slice(2)));
}

// node greet.js Ada 2
Python
# Python: greet.py
import argparse
import sys

def main(argv=None):
    parser = argparse.ArgumentParser(description="Greet someone")
    parser.add_argument("name")
    parser.add_argument("--times", type=int, default=1)
    args = parser.parse_args(argv)   # None -> uses sys.argv[1:]
    for _ in range(args.times):
        print(f"Hello, {args.name}!")
    return 0

if __name__ == "__main__":
    sys.exit(main())

# python greet.py Ada --times 2
# python greet.py --help   (generated for free)

Exercise

Real Python, running in your browser

Write a small command-line script with argparse and a main guard:

1. def main(argv=None) that uses argparse with: - a positional argument: name - an optional flag: --times (int, default 1) and prints "Hello, <name>!" that many times, then returns 0 2. A main guard that calls main(["World", "--times", "2"]) Note: the browser has no command line, so pass the arguments as a list. The runner executes your file with __name__ == "__main__", exactly like python greet.py would.
Expected output
Hello, World!
Hello, World!

Ctrl + Enter runCtrl + Shift + Enter checkTab indentEsc then Tab to leave the editorCtrl + / comment