__name__, __main__ and Running Modules

Every module has a __name__. It is "__main__" when the file is run directly and the module name when it is imported, which is what the main guard tests.

The __name__ variable

# File: greet.py
print("__name__ is", __name__)


def hello(name):
    return f"Hello, {name}"


print(hello("Meera"))
$ python greet.py
__name__ is __main__
Hello, Meera
$ python -c "import greet"
__name__ is greet
Hello, Meera

Python sets __name__ before running the file:

  • Run directly: __name__ is the string "__main__".
  • Imported: __name__ is the module's own name.

Note the problem in the output above. Importing greet printed a greeting, because the call was at top level. That is exactly what the main guard prevents.

The main guard

# File: greet.py
def hello(name):
    return f"Hello, {name}"


def main():
    print(hello("Meera"))


if __name__ == "__main__":
    main()
$ python greet.py
Hello, Meera

$ python -c "import greet"
(nothing)

The module can now be both a program and a library. Importing it gives you hello and nothing else happens; running it produces output.

Read if __name__ == "__main__": as "only when this file is the one being run". It is the most recognisable line in Python and it exists entirely to keep import free of side effects.

Why it matters

# Without a guard: importing this deletes files
import shutil

shutil.rmtree("build")          # runs the moment anyone imports the module
print("build directory cleared")

Anything at module level runs on import: printing, reading input, opening network connections, deleting directories, starting a long computation. Test runners, documentation tools and editors all import your modules to inspect them, so side effects at import time cause real damage.

The standard shape of a script

"""Summarise a text file: line, word and character counts."""

import sys
from pathlib import Path


def summarise(path):
    """Return (lines, words, characters) for the file at path."""
    lines = words = characters = 0
    with open(path, encoding="utf-8") as handle:
        for line in handle:
            lines += 1
            words += len(line.split())
            characters += len(line)
    return lines, words, characters


def main(argv=None):
    argv = sys.argv[1:] if argv is None else argv
    if not argv:
        print("usage: summarise.py FILE", file=sys.stderr)
        return 2

    path = Path(argv[0])
    if not path.is_file():
        print(f"not a file: {path}", file=sys.stderr)
        return 1

    lines, words, characters = summarise(path)
    print(f"{lines:>8} lines")
    print(f"{words:>8} words")
    print(f"{characters:>8} characters")
    return 0


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

Three things are worth copying from this:

  • The work is in functions, so it can be imported and tested.
  • main() returns an exit code rather than calling sys.exit itself, which makes it testable.
  • sys.exit(main()) turns that return value into the process exit status.

Exit codes

import sys

sys.exit(0)      # success
sys.exit(1)      # a general failure
sys.exit(2)      # by convention, incorrect usage
sys.exit("error message")     # prints to stderr and exits with status 1
$ python summarise.py notes.txt
$ echo $?
0

$ python summarise.py
usage: summarise.py FILE
$ echo $?
2

Zero means success and anything else means failure. Shell scripts and build systems rely on this, so a program that always exits zero cannot be used in a pipeline.

Running a module with -m

python greet.py              # run a file by path
python -m greet              # run a module by name, found on sys.path
python -m json.tool data.json      # pretty print JSON
python -m http.server 8000         # serve the current directory
python -m unittest discover        # run tests
python -m timeit "sum(range(100))" # time a snippet
python -m calendar 2026            # print a calendar
python -m this                     # the Zen of Python

Many standard library modules are runnable programs. They achieve that with the same main guard, or with a __main__.py file inside the package.

Making a package runnable

reporter/
    __init__.py
    __main__.py        <-- run when you type: python -m reporter
    core.py
# File: reporter/__main__.py
import sys

from .core import build_report


def main():
    print(build_report())
    return 0


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

Self testing modules

def celsius_to_fahrenheit(c):
    return c * 9 / 5 + 32


def _self_test():
    assert celsius_to_fahrenheit(0) == 32
    assert celsius_to_fahrenheit(100) == 212
    assert celsius_to_fahrenheit(-40) == -40
    print("all checks passed")


if __name__ == "__main__":
    _self_test()

A quick way to keep a module honest during development. Real tests belong in a separate file with unittest, covered later in this path.

Other dunder module variables

# File: info.py
"""A module that reports on itself."""

if __name__ == "__main__":
    print("__name__    ", __name__)
    print("__file__    ", __file__)
    print("__doc__     ", __doc__)
    print("__package__ ", __package__)
from pathlib import Path

HERE = Path(__file__).resolve().parent
DATA = HERE / "data" / "settings.txt"

Building paths from __file__ makes a script work regardless of the directory it is launched from. Relying on the current working directory does not.

Common mistakes

  • Writing if __name__ == "main": without the underscores. It is never true, so nothing runs.
  • Putting the whole program at module level with no guard, so importing it runs everything.
  • Calling sys.exit() deep inside a function, making it impossible to test.
  • Assuming __name__ is the file name; for a script run directly it is always "__main__".
  • Using relative paths that depend on the working directory instead of __file__.
  • Always exiting zero, so failures are invisible to the shell.

Best practices

  • Put every script's work in functions and call them from main().
  • End every runnable module with if __name__ == "__main__": sys.exit(main()).
  • Return exit codes from main rather than exiting inside it.
  • Write errors to sys.stderr and results to sys.stdout.
  • Build data paths from __file__.

Practice

  1. Write a module that behaves differently when run than when imported, and demonstrate both.
  2. Add a main guard to a script and prove that importing it now produces no output.
  3. Write a script returning exit code 0, 1 or 2 depending on its input, and check the code from the shell.
  4. Serve a directory with python -m http.server and explain what makes that possible.
  5. Write a module that locates a data file next to itself regardless of the working directory.

Conclusion

__name__ tells a module whether it is being run or imported. Guard your entry point with it, keep the work in functions, and return an exit code - and the same file becomes both a usable library and a well behaved command line program.

Written by Lorens Mishra

Default administrator account created by the installer.

Continue reading

All Python notes →
Python

Trees and Graphs

A tree is a graph with no cycles and one root. Both are walked with the same two strategies - depth first with a stack, breadth first with a queue.

Read more

Discussion

0 comments
Sign in to join the discussion.

No comments yet. Be the first to say something.