__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.
- Basics
- Data Types
- Operators
- Strings
- Control Flow
- Lists
- Tuples
- Sets
- Dictionaries
- Comprehensions
- Functions
- Advanced Functions
- Recursion
- Exception Handling
- File Handling
- Modules
- Standard Library
- OOP
- Advanced OOP
- Iterators and Generators
- Decorators
- Context Managers
- Descriptors and Dataclasses
- Python Internals
- Concurrency
- Regular Expressions
- Serialization
- Command Line Python
- Testing and Debugging
- Type Hints
- Performance
- Python Security
- DSA with Python
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, MeeraPython 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 callingsys.exititself, 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 $?
2Zero 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.pathpython -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 PythonMany 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
mainrather than exiting inside it. - Write errors to
sys.stderrand results tosys.stdout. - Build data paths from
__file__.
Practice
- Write a module that behaves differently when run than when imported, and demonstrate both.
- Add a main guard to a script and prove that importing it now produces no output.
- Write a script returning exit code 0, 1 or 2 depending on its input, and check the code from the shell.
- Serve a directory with
python -m http.serverand explain what makes that possible. - 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.