Open this lesson in your favourite AI. It'll walk you through the why, explain the demo, and quiz you on the try-it list.
__main__.py is what makes a package directly runnable: python -m mypkg looks for mypkg/__main__.py and runs it. This is how python -m http.server, python -m venv, python -m pip all work. Pairing it with the if __name__ == "__main__": guard in submodules makes your code dual-use: importable as a library AND runnable as a command. This is the canonical Python pattern.
__main__.py is the file Python executes when you run python -m mypkg: the interpreter looks for mypkg/__main__.py and runs it as a script, with __name__ set to '__main__'. Every standard-library tool that doubles as a command uses this pattern — python -m http.server, python -m venv, python -m pip all work because their packages contain a __main__.py. Pairing it with the if __name__ == '__main__': guard in submodules gives you dual-use code: importable as a library when used from other code, and directly runnable from the shell without any installed entry-point shim.
# src/mypkg/__init__.py — empty (or expose top-level API)
# src/mypkg/cli.py
def run() -> int:
print("hello from mypkg.cli")
return 0
if __name__ == "__main__":
import sys
sys.exit(run())
# src/mypkg/__main__.py — what gets executed by python -m mypkg
from .cli import run
import sys
sys.exit(run())
# now both work:
$ python -m mypkg
hello from mypkg.cli
$ python -m mypkg.cli # runs cli.py directly via the __name__ guard
hello from mypkg.clipython3 main.py__main__.py to a package and run it with python -m mypkg. Note the difference vs running mypkg/__main__.py directly.if __name__ == "__main__": guard to a module that's also importable. Test both modes work.python -m http.server 8000 — that's the same pattern in stdlib. Find the source via python -c "import http.server; print(http.server.__file__)".python -m mypkg (uses main.py) vs your installed CLI script (uses [project.scripts]). When would you ship one over the other?Use these three in order. Each builds on the one before.
In one paragraph, explain what `python -m mypkg` actually does — what file does it execute and how is that different from `python mypkg/__init__.py`?
Walk me through the `if __name__ == '__main__':` idiom. What value does `__name__` have when imported vs run as a script, and why?
When I'm shipping a Python tool, when do I prefer `[project.scripts]` (a binary on PATH) vs `python -m mypkg` (no install required)? Cover security, discoverability, and shebang issues.